Ansible 是一個非常簡單的 IT 自動化引擎,可以完成諸如云端資源調配、配置管理、應用部署、服務協調等眾多 IT 自動化任務。它不需要在受控端安裝配置額外的 agent 軟件,因此部署流程非常簡單。
Ansible 的運行機制也并不復雜,它通過 SSH 協議向遠程節點推送特定的“小程序”(Ansible modules)并運行,一旦運行完成則將其移除。
同時 Ansible 提供了一種非常簡單易懂的語言(YAML)用于編寫自動化代碼。
一、簡介
Ansible 通常會被描述為一個配置管理工具,類似于 Chef、Puppet 和 Salt 等。
通過配置管理工具,我們可以確保服務器主機處于某種期望的狀態,比如指定的軟件包已安裝,配置文件中包含正確的值,運行著特定的服務等。
與其他配置管理工具一樣,Ansible 也加入了自己的 DSL(領域專用語言)用來描述服務器的狀態,即后面會介紹到的用于編寫 Playbook 的 YAML 語言。
Ansible 也可以用于部署任務,比如通過源代碼生成可執行程序或靜態資源并發布到服務器,然后開啟遠程主機上的對應服務。類似的開源部署工具如 Fabric 等。
還有一種需求叫做流程編排,通常會涉及到多個遠程服務器,用于確保各類型的任務以特定順序執行。比如在開啟 Web 服務器之前確保數據庫服務處于已經運行的狀態。
Ansible 從設計之初即關注多個服務器狀態下任務的執行,它有一個非常簡單的模塊用于控制動作的順序。
此外,Ansible 還提供眾多的模塊用來與常見的云服務進行交互,包括 Amazon EC2、Azure、Digital Ocean、Linode 以及任何支持 OpenStack API 的云端服務。
一個簡單的 Ansible 運行實例如下圖:
二、Ansible 安裝
絕大部分 Linux 發行版的軟件鏡像中都默認包含了 Ansible 的安裝包, 可以直接通過對應的包管理器進行安裝。如 Ubuntu 系統:
$ sudo apt-get install ansible
Ansible 是基于 Python 語言開發的,因此也可以通過 Python 的包管理器進行安裝,命令如下:
$ pip install ansible
我用 VirtualBox 軟件搭建了兩臺 Ubuntu 19.04 虛擬機,配置了內部網絡(Internal)的聯網方式。
server1 IP 地址為 192.168.1.101,用于安裝 Ansible 環境。
server2 IP 地址為 192.168.1.102,作為遠程主機進行測試。兩者可以相互 ping 通。
SSH 的密鑰認證
為了使得每次運行 Ansible 任務時,不需要重復輸入遠程主機的認證信息,這里先配置好 SSH 連接的無密碼認證(即 RSA 密鑰認證)。命令如下:
$ # 生成 SSH 密鑰文件
$ ssh-keygen
$ # 復制公鑰文件到遠程主機(需要輸入密碼)
$ ssh-copy-id remoteuser@remoteserver
運行成功后,使用 $ ssh remoteuser@remoteserver
命令進行測試,如可以自動登錄,則配置完成。
PS:被控制的遠程主機不需要安裝任何客戶端軟件,但是必須確保已安裝 Python 且 sshd 服務處于運行狀態。如兩者未正確安裝,可運行以下命令:
$ sudo apt-get install python
$ sudo apt-get install openssh-server
主機清單(Inventory)
Ansible 通過一個主機清單文件(hosts
)存放被管理的服務器的列表。
創建 playbooks
目錄作為存放 ansible 腳本和配置文件的項目文件夾,進入該目錄并編輯如下 hosts
文件:
server2 ansible_ssh_host=192.168.1.102 ansible_ssh_user=skitar ansible_ssh_private_key_file=~/.ssh/id_rsa
通過 hosts 中定義的服務器別名和連接信息訪問遠程主機并執行 ping
模塊:
$ ansible server2 -i hosts -m ping
輸出內容如下:
server2 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
如看到類似上面的輸出則 Ansible 環境配置完成。
PS:Ansible 主機清單的配置文件默認為 /etc/ansible/hosts
。如需使用其他位置的主機清單文件,可以通過 -i
選項手動指定,或者修改 ansible.cfg
配置文件的 inventory
項。
ansible.cfg
ansible.cfg 文件中包含了 Ansible 工具的一些默認配置,該文件通常位于以下位置:
- 當前目錄(./ansible.cfg)
- Home 目錄(~/.ansible.cfg)
- /etc/ansible/ansible.cfg
在當前目錄下創建 ansible.cfg
文件并輸入以下內容:
[defaults]
inventory = hosts
remote_user = skitar
private_key_file = ~/.ssh/id_rsa
host_key_checking = False
由于部分默認選項已配置,此時的 hosts
文件則可以省略用戶名和密鑰文件等信息,簡化為如下形式:
server2 ansible_ssh_host=192.168.1.102
使用 ansible 命令時也無需再通過 -i hosts
選項手動指定主機清單文件。即之前的 ping
模塊可以這樣調用:
$ ansible server2 -m ping
Ad-Hoc 命令
即通過命令行的形式直接調用 Ansible 模塊。
Ansible 有著功能豐富的內置模塊,可以通過 ansible-doc -l
命令顯示所有的內置模塊,通過 ansible-doc <module>
命令查看指定模塊的介紹以及使用案例。
Ansible 內置的 command 模塊可以用來在遠程主機上執行 Linux 命令。如執行 uptime
命令:
$ ansible server2 -m command -a uptime
server2 | CHANGED | rc=0 >>
17:45:12 up 5:13, 1 user, load average: 0.12, 0.08, 0.02
其中 -m
用于指定調用的模塊,-a
用于指定傳遞給該模塊的選項,此處即某個具體的命令。
由于 command 模塊很常用,它其實是不指定任何模塊情況下的默認模塊。所以上面的命令也可以使用如下形式:
$ ansible server2 -a uptime
當遠程主機上執行的命令中包含空格時,需要用引號括起來,示例如下:
$ ansible server2 -a "tail /var/log/syslog"
server2 | CHANGED | rc=0 >>
Jul 8 18:18:18 server2 systemd[6250]: Reached target Paths.
Jul 8 18:18:18 server2 systemd[6250]: Listening on GnuPG cryptographic agent and passphrase cache (access for web browsers).
Jul 8 18:18:18 server2 systemd[6250]: Listening on D-Bus User Message Bus Socket.
Jul 8 18:18:18 server2 systemd[6250]: Reached target Sockets.
Jul 8 18:18:18 server2 systemd[6250]: Reached target Basic System.
Jul 8 18:18:18 server2 systemd[1]: Started User Manager for UID 1000.
Jul 8 18:18:18 server2 systemd[1]: Started Session 47 of user skitar.
Jul 8 18:18:18 server2 systemd[6250]: Reached target Default.
Jul 8 18:18:18 server2 systemd[6250]: Startup finished in 75ms.
Jul 8 18:18:19 server2 python3[6304]: ansible-command Invoked with _raw_params=tail /var/log/syslog warn=True _uses_shell=False stdin_add_newline=True strip_empty_ends=True argv=None chdir=None executable=None creates=None removes=None stdin=None
對于某些需要 root 權限才能執行的操作,則需要加上 --become --ask-become-pass
或者 -b -K
選項通過 sudo 執行。如使用 service 模塊重啟遠程主機上的 nginx 服務:
$ ansible server2 -b -K -m service -a "name=nginx state=restarted"
BECOME password:
server2 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": true,
"name": "nginx",
"state": "started",
"status": {
"ActiveEnterTimestamp": "Mon 2019-07-08 18:16:58 CST",
"ActiveEnterTimestampMonotonic": "20685653757",
"ActiveExitTimestamp": "Mon 2019-07-08 18:16:57 CST",
"ActiveExitTimestampMonotonic": "20685035019",
"ActiveState": "active",
...
其他常用的內置模塊還有 apt、copy、user 等。
如通過 apt
模塊管理遠程主機上的軟件包:
$ ansible server2 --b -K -m apt -a "name=nginx state=latest"
BECOME password:
[WARNING]: Could not find aptitude. Using apt-get instead
server2 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"cache_update_time": 1562598780,
"cache_updated": false,
"changed": false
}
通過 copy
模塊復制本地文件到遠程主機:
$ ansible server2 -m copy -a "src=ansible.cfg dest=/home/skitar/ansible.cfg owner=skitar mode=644"
server2 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": true,
"checksum": "2f4f9975ef1875adcc2a299d3962f70630f49965",
"dest": "/home/skitar/ansible.cfg",
"gid": 1001,
"group": "skitar",
"mode": "0644",
"owner": "skitar",
"path": "/home/skitar/ansible.cfg",
"size": 109,
"state": "file",
"uid": 1000
}
通過 user
模塊管理遠程主機上的用戶(需要先通過 openssl
命令生成密碼,因為 user 模塊的 password 參數只接受加密后的值):
$ echo ansible | openssl passwd -1 -stdin
$1$ZyNqkbXH$i.4R0EDQZV.zu8akyJAu10
$ ansible server2 -b -K -m user -a 'name=starky password="$1$ZyNqkbXH$i.4R0EDQZV.zu8akyJAu10" shell=/bin/bash'
BECOME password:
server2 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"append": false,
"changed": true,
"comment": "",
"group": 1002,
"home": "/home/starky",
"move_home": false,
"name": "starky",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/bash",
"state": "present",
"uid": 1001
}
三、Playbooks
Playbooks 即 Ansible 用于執行自動化配置的腳本文件。它使用非常簡單的 YAML 語言描述期望達到的狀態,YAML 之于 JSON 類似于 Markdown 之于 HTML 。
一個簡單的用于配置 nginx 站點的 playbook 示例(web-notls.yml
)如下:
- name: Configure webserver with nginx
hosts: webservers
become: True
tasks:
- name: install nginx
apt: name=nginx update_cache=yes
- name: copy nginx config file
copy: src=files/nginx.conf dest=/etc/nginx/sites-available/default
- name: enable configuration
file: >
dest=/etc/nginx/sites-enabled/default
src=/etc/nginx/sites-available/default
state=link
- name: copy index.html
template: src=templates/index.html.j2 dest=/var/www/html/index.html mode=0644
- name: restart nginx
service: name=nginx state=restarted
編輯 nginx 配置文件,即 playbook 中 copy
模塊的 src
選項指定的文件(files/nginx.conf
),內容如下:
server {
listen 80 default_server;
root /var/www/html;
index index.html;
server_name 192.168.1.102;
location / {
try_files $uri $uri/ =404;
}
}
編輯 nginx 站點的主頁文件,即 playbook 中 template
模塊的src
選項指定的文件(templates/index.html.j2
),內容如下:
<html>
<head>
<title>Welcome to ansible</title>
</head>
<body>
<h1>nginx, configured by Ansible</h1>
<p>If you see this, Ansible successfuly installed nginx.</p>
<p>Current time is {{ now() }}</p>
</body>
</html>
此處使用了 Jinjia2 模板引擎,所以可以通過 {{ now() }}
獲取當前系統時間并替換到 HTML 文檔中。
編輯 Inventory (主機清單)即hosts
文件,創建 webservers
主機組:
[webservers]
server2 ansible_ssh_host=192.168.1.102
主機組中可以包含一個或多個遠程主機,便于同時管理多個遠程節點。
上述配置完成后,通過 ansible-playbook web-nolts.yml -K
命令運行 playbook,輸出如下:
$ ansible-playbook web-notls.yml -K
BECOME password:
PLAY [Configure webserver with nginx] **********************************************************
TASK [Gathering Facts] *************************************************************************
ok: [server2]
TASK [install nginx] ***************************************************************************
[WARNING]: Could not find aptitude. Using apt-get instead
ok: [server2]
TASK [copy nginx config file] ******************************************************************
changed: [server2]
TASK [enable configuration] ********************************************************************
ok: [server2]
TASK [copy index.html] *************************************************************************
changed: [server2]
TASK [restart nginx] ***************************************************************************
changed: [server2]
PLAY RECAP *************************************************************************************
server2 : ok=6 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
注意 ansibe-playbook
命令的 -K
選項(即 --ask-become-pass
),由于 playbook 中的部分任務需要 root 權限執行,加上 -K
選項后,執行 playbook 時會出現 BECOME password:
提示用于輸入 sudo 密碼。否則會報錯。
playbook 執行成功后,使用 curl 192.168.1.102
命令訪問 server2 上剛剛配置的 nginx 站點,輸出如下:
$ curl 192.168.1.102
<html>
<head>
<title>Welcome to ansible</title>
</head>
<body>
<h1>nginx, configured by Ansible</h1>
<p>If you see this, Ansible successfuly installed nginx.</p>
<p>Current time is 2019-07-09 00:28:46.484053</p>
</body>
</html>