83.學習Ansible[6]:playbook
關於Playbook
學習playbook前,推薦先閱讀:學習Ansible[5]:YAML檔案格式淺析。
Playbook是Ansible的配置、部署、編排語言,可以描述遠端機器的執行策略,或一般IT流程中的一組步驟。如果Ansible的模組是車間裡的工具,playbook就是操作手冊,資源清單中的主機就是操作的原材料。
playbook的基本功能是管理遠端機器的配置、部署,更高階點可以對涉及滾動更新的多層釋出任務進行排序,將操作委派給其他主機,同時與監視伺服器和負載平衡器進行互動。
Github上的ansible/ansible-examples示例是Ansible提供的官方示例,推薦閱讀。
第一個Playbook例子
一個playbook,是一個或多個play的列表。一個play,建立起一組機器和一些任務的對映。一個任務,是對Ansible模組的一次呼叫。組合多個play成為一個playbook,可以實現複雜的功能,比如在webservers
組上執行某些任務,再在dbservers
組上執行另一些任務,最後在webservers
組上執行剩下的任務,等等。一個簡單的包含一個play的playbook示例:
--- - hosts: webservers vars: http_port: 80 max_clients: 200 remote_user: root tasks: - name: ensure apache is at the latest version yum: name: httpd state: latest - name: write the apache config file template: src: /srv/httpd.j2 dest: /etc/httpd.conf notify: - restart apache - name: ensure apache is running service: name: httpd state: started handlers: - name: restart apache service: name: httpd state: restarted
包含多個play的playbook例子:
--- - hosts: webservers remote_user: root tasks: - name: ensure apache is at the latest version yum: name: httpd state: latest - name: write the apache config file template: src: /srv/httpd.j2 dest: /etc/httpd.conf - hosts: databases remote_user: root tasks: - name: ensure postgresql is at the latest version yum: name: postgresql state: latest - name: ensure that postgresql is started service: name: postgresql state: started
play和任務的執行順序,與在playbook中的定義順序一樣,都是從上到下,所以上面的例子可以實現在不同的機器組間切換不同的任務。下面深入介紹playbook的基礎。
主機和使用者
每個play,都要明確針對哪些機器(hosts
)、以何種身份(remote_user
)完成指定的任務。
hosts
的值,是一個或多個機器組或機器pattern的列表,用冒號分割。remote_user
是登入遠端機器的使用者名稱。一個play級的主機配置片段舉例:
# YAML snippet
---
- hosts: webservers
remote_user: root
remote_user
可以在每個任務中單獨定義:
# YAML snippet
---
- hosts: webservers
remote_user: root
tasks:
- name: test connection
ping:
remote_user: mars
play級的提升許可權:
# YAML snippet
---
- hosts: webservers
remote_user: mars
become: yes
任務級的提升許可權(可以同時設定提升許可權的方法,比如su
):
# YAML snippet
---
- hosts: webservers
remote_user: mars
tasks:
- service:
name: nginx
state: started
become: yes
become_method: sudo
play級的切換到其他使用者:
# YAMl snippet
---
- hosts: webservers
remote_users: mars
become: yes
become_user: loo
如果切換使用者或sudo
需要密碼,執行ansible-playbook
命令時要加--ask-become-pass
引數。執行有使用者切換場景的playbook,ansible-book
程式hang住很有可能是切換使用者或sudo
時鑑權出現問題,直接Ctrol+C
殺死ansible-playbook
,重新提供--ask-become-pass
引數即可。
play級的order
引數控制任務在所有機器的執行順序:
# YAMl snippet
---
- hosts: all
order: sorted
gather_facts: False
tasks:
- debug:
var: inventory_hostname
order
引數取值範圍:
- inventory:預設,按照
hosts
引數列表中的順序執行。 - reverse_inventery:按照
hosts
引數列表中的反序執行。 - sorted:按照
hosts
引數中機器名字典序執行。 - reverse_sorted:按照
hosts
引數中機器名字反字典序執行。 - shuffle:按照
hosts
引數中機器隨機序執行。
任務列表
playbook執行過程中,如果一個機器執行任務失敗,這個機器將不再參與後續的任務執行。
模組應該是冪等的,要實現冪等,可以檢查模組的最終狀態是否符合預期。如果playbook使用的所有模組都是冪等的,整個playbook就是冪等的,多次執行playbook就是安全的。
一個簡單的任務定義的示例片段:
# YAML snippet
tasks:
- name: make sure apache is running
service:
name: httpd
state: started
name
引數描述這個任務的功能,執行playbook時會輸出到日誌中。
大多數模組(比如上例中的service
模組)接受key=value
格式的引數,對於command
、shell
這種直接接收引數列表的模組,可以按照如下格式定義:
# YAML snippet
tasks:
- name: enable selinux
command: /sbin/setenforce 1
command
和shell
模組關注命令的返回值,如果命令成功執行時的返回值不是0,用如下方法規避:
# YAML snippet
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true
或者:
# YAML snippet
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True
如果引數過長,可以在任意空格處換行,在下一行縮排繼續定義引數:
# YAML snippet
tasks:
- name: Copy ansible inventory file to client
copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts
owner=mars group=mars mode=0644
任務的定義可以使用play級定義的變數,比如假設在play的vars
部分提前定義了一個變數vhost
:
# YAML snippet
tasks:
- name: use variable {{ vhost }}
template:
src: somefile.j2
dest: /etc/conf/{{ vhost }}
舊版本的Ansible定義任務使用action: module arguments
的形式,現在已經不推薦使用這種形式,例如:
# YAML snippet
action: template src=somefile.j2 dest=/etc/conf/somefile.j2
推薦使用上面的module: arguments
形式:
# YAML snippet
- name: copy files
template:
src: somefile.j2
dest: /etc/conf/somefile.j2
處理器
playbook的通知機制(notify),可以在一個play結束時執行指定的任務(即使被多個不同的任務多次通知)。例如,多個任務都修改了Apache的配置並通知Apache重啟,Apache僅在這個play結束時重啟一次,例如:
# YAML snippet
tasks:
- name: template configuration file
template:
src: template.j2
dest: /etc/apache.conf
notify:
- restart apache
handlers:
- name: restart apache
service:
name: apache
state: restarted
notify
中定義的任務列表是處理器(handler)的列表。處理器也是任務,有全域性唯一的名字,被通知觸發執行,如果沒有被通知則不會執行。
除了通過處理器名字觸發處理器,處理器可以監聽topic訊息,被topic訊息觸發:
# YAML snippet
handlers:
- name: restart memcached
service:
name: memcached
state: restarted
listen: "restart web services"
- name: restart apache
service:
name: apache
state: restarted
listen: "restart web services"
tasks:
- name: restart everything
command: echo "this task will restart the web services"
notify: "restart web services"
細心的讀者會發現notify
的值也可以是標量。使用topic訊息的方式將處理器與名字本身解耦,在劇本、角色間共享處理器場景下非常方便。
- 多個處理器一起觸發時,按照定義的順序執行,而不是觸發順序。
- 處理器名字和topic訊息都存在於全域性名稱空間。
- 如果兩個處理器名字相同,只有一個處理器會被觸發。
- 不能觸發一個定義在include中的觸發器。
如果執行某個任務前,需要先執行排隊中的處理器任務,可以用如下方式清空並執行排隊中的處理器任務:
# YAML snippet
tasks:
- shell: some tasks
- meta: flush_handlers
- shell: some other tasks
執行playbook與其他
編寫完playbook的YAML檔案後,執行playbook非常簡單,假設1.yml
內容為:
---
- hosts: example
remote_user: mars
gather_facts: false
tasks:
- name: list files
shell: pwd && ls -al
以10併發執行playbook:
$ ansible-playbook 1.yml -f 10
PLAY [example] *****************************************************************
TASK [list files] **************************************************************
changed: [a.example.com]
changed: [b.example.com]
PLAY RECAP *********************************************************************
a.example.com : ok=1 changed=1 unreachable=0 failed=0
b.example.com : ok=1 changed=1 unreachable=0 failed=0
檢查playbook的語法:ansible-playbook --syntax-check 1.yml
。
檢視模組執行過程中的輸出:ansible-playbook 1.yml --verbose
。
檢視一個playbook會影響哪些機器:
$ ansible-playbook 1.yml --list-hosts
playbook: 1.yml
play #1 (example): example TAGS: []
pattern: [u'example']
hosts (2):
a.example.com
b.example.com