1. 程式人生 > >83.學習Ansible[6]:playbook

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格式的引數,對於commandshell這種直接接收引數列表的模組,可以按照如下格式定義:

# YAML snippet
tasks:
  - name: enable selinux
    command: /sbin/setenforce 1

commandshell模組關注命令的返回值,如果命令成功執行時的返回值不是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