TL;DR
Ansible
是適用於Python環境的自動化組態管理工具,組態(Configuration Management)意味著環境的設定及部署。
部署環境時,需要花費大量的時間來安裝套件、設定Config檔案、調整dependency,使用 Ansible
可以將這些「制式化的系統操作」自動化,讓開發者將注意力投注在值得關注的事物上。
撰寫好「部署的流程」(例如安裝套件、複製檔案…)及「遠端主機的連線資訊」後,運行Ansible的主機即可連線至其他主機,進行自動部署,提高部署的效率、降低安裝過程中人為失誤的機率。
1. Why Ansible ?
Ansible
的優勢在於:
使用Python生態系:
近期許多機器學習框架皆是建構於Python生態系
使用SSH連線,不需額外安裝工具:
使用
Ansible
對遠端主機進行操作時,是透過SSH
進行遠端操作,意味著不需要在遠端主機額外安裝套件,僅需開啟SSH連接阜即可。 在某些難以安裝套件的環境(例如醫院、銀行)相當方便。
2. 使用 Ansible 的目的
提升 Production 環境的穩定性及可靠性
使用檔案管理「部署內容」,達到 Infrastructure as Code,管理上也相對單純,可以使用版本控制進行進退版操作。
減少服務中斷時間
若發生服務中斷,需要將節點下線重新部署,使用
Ansible
進行部署能夠按照「預先設計好的流程」重新部署,不會受限於人員的精神狀況及操作穩定度。確保環境間(開發、測試、正式)設定對齊
如果同時有多台機器,使用 Ansible 統一部署,可確保在部署過程中統一參考組態檔案,不會發生人為失誤(例如手誤輸入錯誤的資訊),或是不同部署人員部署順序不一致。
3. 檔案結構 & 安裝
3.1 安裝
雖然在先前曾提過 Ansible
不需要額外安裝套件,但這是指被操控的機器,我們仍然需要在主要操作的主機上安裝 Ansible
,使用 pip
即可輕易安裝:
python -m pip install --user ansible
3.2 檔案結構
在學習一個工具時,我習慣先確認「專案的結構該長怎麼樣」,在後續的文章我們將會以此專案結構來作為範例。
以下是一個 Ansible
專案的大致樣貌:
ansible_project
| - README.md
| - INVENTORY_FILE_1 # 概念 - Inventory
| - INVENTORY_FILE_2
| main.yml # 概念 - Playbook
|
| - group_vars/
| - GROUP1.yml
| - GROUP2.yml
| - roles/
| - common/
| - tasks/
| - main.yml
| - handlers/
| - main.yml
備註: 這是用來說明的專案結構,更多Ansible的專案結構可以參考 Best Practices - Ancible Documentation
4. Playbook - 設定工作的流程及步驟
在上方架構中,首先看到 main.yml
,這是 Ansible
專案中的一個 Playbook(劇本),顧名思義在這個檔案中記錄了「做事的方式」及「做事的流程」。
Playbook
的範例如下:
# main.yml
- name: The example playbook # 階層1 - Play
hosts: localhost # 定義執行此Play的機器
vars: # 設定變數
dynamic_word: "Hello World"
tasks: # 設定 task
- name: generate the hello_world.txt file # 定義 task-1
lineinfile: # 概念 - Module
path: /tmp/hello_world.txt
state: present
line: "{{ dynamic_word }}"
create: yes
- name: show file context # 定義 task-2
command: cat /tmp/hello_word.txt
register: result
- name: print file result # 定義 task-3
debug:
msg: "{{ result.stdout_lines }}"
4.1 Playbook的階層
在 Playbook
中包含幾個不同的概念:
Play
: 定義了「角色」(hosts) 該執行什麼樣的「任務」(task)Task
: 一個具體的工作,可能是一個具體的 bash 指令 (例如:echo $PATH
)在一個
Playbook
中可能包含了數個不同的Play
:分別定義「扮演DB-Server角色」的機器該完成哪些任務、「扮演Web-Server」的機器又該完成哪些任務。
在一個
Play
中可能定義了數個Task
:「扮演DB-Server角色」的機器可能需要「建立本地資料夾」、「安裝PostgreDB」、「初始化DB」等三項具體的任務。
以上方的 main.yml
來說,我們建立了一個 Play
(The example playbook),執行這個Play
的角色是localhost
(hosts: localhost
),而這個 Play
裡面包含了三項具體的 Task
:
+ generate the hello_world.txt file
+ show file context
+ print file result
4.2 Task 的定義
接著我們來看如何定義 Task
,從上方 main.yml
中節錄 Task
的部分:
tasks: # 設定 task
- name: generate the hello_world.txt file # 定義 task-1
lineinfile: # 概念 - Module
path: /tmp/hello_world.txt
state: present
line: "{{ dynamic_word }}"
create: yes
- name: show file context # 定義 task-2
command: cat /tmp/hello_word.txt
register: result
- name: print file result # 定義 task-3
debug:
msg: "{{ result.stdout_lines }}"
這三個 Task
語法看起來很不一樣,感覺相當複雜,但涵蓋了大部分的概念,理解這三個Task
,就大致了解 Ansible Task
了!
首先可以看到三個 Task
都有一個共通的 name
欄位,定義此 Task
的名稱。
接下來我們將三個任務分開來講:
4.2.1 - Command (Task2)
command
欄位放的是單純的 bash script,舉凡 ls /tmp
, cat /xxx/yyy.txt
,是最為單純的使用方式。
task-2 (show file context)
使用 command 來顯示特定位置的檔案:
- name: show file context # 定義 task-2
command: cat /tmp/hello_word.txt
register: result
如果需要將 terminal 執行的資訊儲存下來,則需要透過 register
語法來註冊變數,以上方yml檔為例,將cat /tmp/hello_world.txt
的內容儲存至 result
變數中。
4.2.2 - Module (Task1 & Task3)
除了單純使用 command
下語法外, Ansible
還有提供許多內建的語法,讓使用者能用簡單的語法執行複雜的任務(例如檔案新增、權限變動、Git下載等等)。 接下來我們分別以 Task1
及 Task3
來介紹兩個常用的 Module
。
4.2.2.1 - debug
在 Task2
我們將輸出結果儲存至 result
變數中,接下來我們就透過 Ansible
的 Module - Debug
來輸出結果,每個不同的 Module
會自帶不同的參數,這些參數可以在 Ansible
的文件查詢,得到參數 msg
是放置要輸出的值。
所以在 Task3
中,我們將剛剛註冊的變數放入 msg
中:
- name: print file result # 定義 task-3
debug:
msg: "{{ result.stdout_lines }}"
在稍後執行 Playbook
的過程中,即可將 Task2
的結果輸出。
4.2.2.2 - fileinline
除了 debug
之外,Ansible
還提供許多模組,例如檔案的處理(Module-file)、Git操作(Module-git)等等…
第二個介紹的模組是Module-fileinline,將內容寫入檔案時使用,參考官網的文件可以得到各項參數的意義,以 task1
為例:
- name: generate the hello_world.txt file # 定義 task-1
lineinfile: # 概念 - Module
path: /tmp/hello_world.txt
state: present
line: "{{ dynamic_word }}"
create: yes
- path: 要寫入檔案的路徑
- state: present(寫入)、absent(移除)
- line: 要寫入的資訊 (對於
Jinja
語法不熟悉的朋友,別緊張,請繼續看下去) - create: 檔案若不存在,是否要創建
透過 Module - lineinfile
, task1
將會創建 /tmp/hello_world.txt
,並將 dynamic_word
變數寫入檔案中。 (請見下節)
4.3 Variable
在 Playbook
中,我們可以自定義變數,以 main.yml
為例:
# main.yml
- name: The example playbook # 階層1 - Play
hosts: localhost # 定義執行此Play的機器
vars: # 設定變數
dynamic_word: "Hello World"
我們在 Play
中定義了一個變數 dynamic_word
,在此Play
的各項任務中,如果需要使用此變數,可以使用 Jinja2
的渲染語法來取得 :
- name: generate the hello_world.txt file # 定義 task-1
lineinfile: # 概念 - Module
path: /tmp/hello_world.txt
state: present
line: "{{ dynamic_word }}" # 使用 {{ 變數名稱 }} 來取得變數
create: yes
這樣的好處在於: 多個task
都使用 Jinja 方式取得變數,未來該變數的值如果有變動,可以直接更換 vars
欄位,不需要每一個 task
都更動
4.4 執行 Playbook
恭喜! 我們成功設定好第一個 Playbook
了,快速總結這個Playbook
的目的:
扮演「localhost」角色的主機,需要進行三個任務,依序是建立檔案、顯示資訊、輸出資訊
設定好劇本後,該開始執行了!
ansible-playbook main.yml
應該可以看到 Ansible
開始依序執行三項任務,且最後輸出結果將會如下圖:
在執行的過程中,也可以透過 -e
來覆蓋原先設定的變數,舉例來說,原先在 main.yml
中設定的 dynamic_word
變數為 hello world ,我們可以透過:
ansible-playbook -e dynamic_word=minglunwu main.yml
透過上述指令將 dynamic_word
變數覆寫為 minglunwu,執行結束時,應可在debug的介面看到檔案內容由 hello world 變為 minglunwu了。
5. Inventory - 紀錄機器的群組及連線資訊
在 Playbook
中我們定義了不同角色的機器需要進行什麼樣的行為,接下來我們將介紹 Inventory File
,這個檔案的用意是
定義機器的連線資訊以及所屬的角色
# INVENTORY_FILE_1
[test-server] # 群組(角色)名稱
localhost ansible_connection=local
[web-server] # 群組(角色)名稱
# 連線資訊
# 機器名稱/連線方式/使用者帳號/使用者密碼(不建議)
webserver ansible_connection=ssh ansible_user=myuser ansible_ssh_pass=password
webserver-2 ansible_connection=ssh ansible_user=myuser-2 ansible_ssh_pass=password2
在INVENTORY_FILE_1
中我們可以看到 [test-server]
和 [web-server]
兩個群組(角色),這些群組中可包含不同的機器及連線資訊。
對於 Inventory File
和 Group
,我個人的理解是:
Inventory File
是用來管理不同的環境(例如 SIT、UAT、PROD)Group
: 同一個環境中,則用Group
來區別不同類型的服務(例如: DB、Web-Server)。Playbook
在設定task
時,可以透過hosts
來篩選不同Group
的機器該做什麼任務。
你可能會有兩個不同的 Inventory File
,分別代表 UAT
及 PROD
環境的機器連線資訊,而兩個 Inventory File
中可能都會有 db-server
和 web-server
的群組。
在編寫 Playbook
時,你可以透過設定 hosts
參數來設定各個角色要做的事情:
- name: task-db
hosts: db-server
tasks:
- name: example-1
command: pip install MySQL-python
- name: task-web
hosts: web-server
tasks:
- name: example-2
command: pip install flask
被歸類在 db-server
群組的機器需要安裝MySQL-python
、被歸類在 web-server
群組的機器則需要安裝 flask
這是以「群組」去區分不同機器該做什麼事情 (功能層級)
在執行 Playbook
時,也可以透過 -i
來指定 Inventory file
ansible-playbook -i INVENTORY_FILE_1 main.yml
不同的 Inventory File
可能會定義相同的Group
(Ex: web-server
, db-server
),但這些群組中包含著不同的節點 (Prod
環境的 web-server
是節點A、UAT
環境的 web-server
則是節點B)
單純切換 Inventory File
即可在多個環境間部署多台節點,且每個節點根據自身被賦予的角色,進行不同的任務。
ansible-playbook -i UAT main.yml # 僅更新UAT環境
ansible-playbook -i PROD main.yml # 僅更新PROD環境
ansible-playbook -i UAT -i PROD main.yml # 兩個環境同時更新
使用
Group
來區分不同性質的服務
使用
Inventory
來區分不同環境
透過
Inventory File
和Group
,Ansible
能根據使用者需求快速的部署對應的機器。
6. 小結
本篇筆記對於 Ansible
進行入門的介紹,包含幾個重要觀念:
Playbook
中的元件及階層- 如何使用
Ansible
內建的模組 - 使用
Group
區分不同功能的節點 - 使用
Inventory File
區分不同環境 Ansible
如何執行Playbook
- 執行時如何覆寫變數、如何選擇
Inventory File
在下篇筆記中,我們將會繼續介紹 Ansible
的應用,包含:
- 設定不同
Group
專屬的變數 - 如何使用
role
來提高Ansible
專案的程式碼重用率 - 使用
Ansible-Galaxy
來取得眾多開源Playbook
筆記連結: Ansible 入門筆記(二) - Variable
感謝您的閱讀!如果有任何問題歡迎透過電子郵件聯繫我!
我們下次見!