1. 程式人生 > >3、Ansible playbooks

3、Ansible playbooks

Ansible playbooks

playbook是由一個或多個“play”組成的列表。play的主要功能在於將事先歸併為一組的主機裝扮成事先通過ansible中的task定義好的角色。從根本上來講,所謂task無非是呼叫ansible的一個module。將多個play組織在一個playbook中,即可以讓它們聯同起來按事先編排的機制同唱一臺大戲。下面是一個簡單示例。

- hosts: webnodes    //webnodes定義一個主機組,表示應用的目標主機。下面定義的任務只對此組內的主機生效

  vars:   //這裡是一個鍵值對,vars是鍵,它的值是一個字典

  http_port: 80

  max_clients: 256

  remote_user:root   //表示連結遠端主機以哪個主機的身份進行

  tasks:  //定義接下來要執行的任務

  - name: ensure apache is at the latest version  //第一個任務,

    yum: name=httpd state=latest                         //基於yum模組安裝httpd程式包

  - name: ensure apache is running                    //第二個任務

    service: name=httpd state=started                 //基於service模組啟動httpd服務

    handlers:

  - name: restart apache

    service: name=httpd state=restarted

 

1、playbook基礎元件

1.1 Hosts和Users

playbook中的每一個play的目的都是為了讓某個或某些主機以某個指定的使用者身份執行任務。hosts用於指定要執行指定任務的主機(這些主機一定是在Inventory定義的主機),其可以是一個或多個由冒號分隔主機組;remote_user則用於指定遠端主機上的執行任務的使用者。如上面示例中的

- hosts: webnodes

  remote_user: root    

不過,remote_user也可用於各task中。也可以通過指定其通過sudo的方式在遠端主機上執行任務,其可用於play全域性或某任務;此外,甚至可以在sudo時使用sudo_user指定sudo時切換的使用者。

編寫playbooks定義了hosts和users之後,接下來就是定義任務列表。任務可以有多個,所以每一個都要使用“-”引導。

在playbooks中一個劇本應用於不同主機上操作有很多,所以每一個主機組都應該使用"-"來引導

- hosts: webnodes

  remote_user: mageedu    

  tasks:

- name: test connection

  ping:

  remote_user: dongshi   //此tasks中定義了在遠端主機主機上執行任務的使用者,這裡的優先順序最高,不再使用全域性中定義的remote_user。

  sudo: yes   //表示以dongshi使用者的身份切入到遠端主機上,以dongshi這個使用者的身份sudo到root使用者在遠端主機來執行命令,這樣做的好處是在管控端儲存的普通使用者身份、                      //許可權資訊在遭到洩露時不知道導致太大的風險。

 

基本結構:

- host:websrvs

  remote_user:

  tasks:      //play的主題部分

  - task1    //在所有主機上完成第一個任務,才開始在所有主機上執行第二個任務

    module_name:module_args

  - task2

 

1.2 任務列表和action

play的主體部分是task list。task list中的各任務按次序逐個在hosts中指定的所有主機上執行,即在所有主機上完成第一個任務後再開始第二個。

在執行自下而下某playbook時,如果中途發生錯誤,所有已執行任務都可能將回滾(有些任務無法回滾),因此,在更正playbook後重新執行一次即可。

task的目的是使用指定的引數執行模組,而在模組引數中可以使用變數。模組執行是冪等的,這意味著多次執行是安全的,因為其結果均一致。

每個task都應該有其name,用於playbook的執行結果輸出,建議其內容儘可能清晰地描述任務執行步驟。如果未提供name,則action的結果將用於輸出。

定義task的可以使用“action: module options”或“module: options”的格式,推薦使用後者以實現向後相容。如果action一行的內容過多,也中使用在行首使用幾個空白字元進行換行。

tasks:

- name: make sure apache is running

  service: name=httpd state=running

 

在眾多模組中,只有command和shell模組僅需要給定一個列表而無需使用“key=value”格式,例如:

tasks:

- name: disable selinux      //變數後的":"與值用空格隔開

  command: /sbin/setenforce 0

 

如果某個命令或指令碼執行失敗,退出碼不為零的情況下,可能會阻止playnook繼續向後執行,為了避免此情況的出現,可以在某個命令執行結束以後,可以強行讓它輸出正確的資訊。

可以使用如下方式替代:

tasks:

- name: run this command and ignore the result

  shell: /usr/bin/somecommand || /bin/true          //此處命令執行成功了,就返回0,如果執行不成功就返回true,所以不會阻止後面的命令繼續執行

 

或者使用ignore_errors來忽略錯誤資訊:

tasks:

- name: run this command and ignore the result

  shell: /usr/bin/somecommand

  ignore_errors: True     //即使出錯無不管不問,繼續向後執行。

 

示例:在3臺被管控主機上都建立系統組叫nginx,建立使用者也叫nginx

# vim nginx.yml     //由哪個使用者的身份在哪些主機上執行什麼樣的任務

- hosts: websrvs     //定義主機組
  remote_user: root  //定義使用者的身份
  tasks:             //定義執行什麼樣的任務
   - name: create nginx group   //第一個任務。建立nginx組
     group: name=nginx system=yes gid=208
   - name: create nginx user    //第二個任務,建立nginx使用者
     user: name=nginx uid=208 group=nginx system=yes

- host: dbsrvs
  remote_user: root
  tasks:
  - name: copy file to dbsrvs
    copy: src=/etc/inittab dest=/tmp/inittab.ans

# ansible-playbook nginx.yml   //執行playbook

PLAY [websrvs] ********************//首先在websrvs主機組上執行任務************************************************************************

TASK [Gathering Facts] ************//在websrvs組主機上執行任務前,每一個被管控主機首先要向管控端報告跟自身主機相關的各種變數,即facts***************
ok: [192.168.184.142]
ok: [192.168.184.143]
ok: [192.168.184.145]

TASK [create nginx group] *********//建立nginx組****************************************************************************************
changed: [192.168.184.142]         //顯示changed表示任務執行成功。表示目標主機不處於已定義(即tasks中的定義)的目標狀態,所以要對目標做修改。
changed: [192.168.184.143]         //比如nginx組不存在,所以就在被管控主機上建立,就顯示changed。
changed: [192.168.184.145]

TASK [create nginx user] **********//建立nginx使用者**************************************************************************************
changed: [192.168.184.142]
changed: [192.168.184.143]
changed: [192.168.184.145]

PLAY [dbsrvs] **********************//然後在dbsrvs主機組上執行任務************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************
ok: [192.168.184.142]
ok: [192.168.184.145]

TASK [copy file to dbsrvs] ***********************************************************************************************************
changed: [192.168.184.142]
changed: [192.168.184.145]

PLAY RECAP **************************//在所有任務都執行完成後,會有一個報告******************************************************************
192.168.184.142            : ok=5    changed=3    unreachable=0    failed=0   //比如1422主機上執行任務ok是的數量是5,其中包括changed和Gathering Facts,changed是3
192.168.184.143            : ok=3    changed=2    unreachable=0    failed=0   //unreached和failed都為0,表示沒有執行失敗的
192.168.184.145            : ok=5    changed=3    unreachable=0    failed=0   

# ansible-playbook nginx.yml    //在執行一邊playbook與上面對比

PLAY [websrvs] ***********************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************
ok: [192.168.184.142]
ok: [192.168.184.143]
ok: [192.168.184.145]

TASK [create nginx group] ************************************************************************************************************
ok: [192.168.184.145]    //這裡就顯示ok,並不是建立nginx組ok,而是被管控主機所有的資料都存在,而且滿足tasks定義的需要,就報告為ok
ok: [192.168.184.143]
ok: [192.168.184.142]

TASK [create nginx user] *************************************************************************************************************
ok: [192.168.184.145]
ok: [192.168.184.143]
ok: [192.168.184.142]

PLAY [dbsrvs] ************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************
ok: [192.168.184.142]
ok: [192.168.184.145]

TASK [copy file to dbsrvs] ***********************************************************************************************************
ok: [192.168.184.142]
ok: [192.168.184.145]

PLAY RECAP ***************************************************************************************************************************
192.168.184.142            : ok=5    changed=0    unreachable=0    failed=0   
192.168.184.143            : ok=3    changed=0    unreachable=0    failed=0   
192.168.184.145            : ok=5    changed=0    unreachable=0    failed=0   

 

1.3 handlers 處理器

用於當關注的資源發生變化時採取一定的操作。

 

在一個服務的配置檔案發生改變時,應該讓程式重讀配置檔案或者重啟程式。

 

預設情況下,多次執行一個任務的時候,如果那個任務已經執行過了,為了保持冪等性,它是不會再次執行的,所以就不可能重啟。

 

handler就是為了解決這種問題的,它可以監控另外一個task,如果所監控的task做了某些修改,才執行指定的任務。

 

所以handler也是一個任務,這個任務不是上來就執行的,只有某個條件滿足時才執行。

 

“notify”這個action可用於在每個play的最後被觸發,這樣可以避免多次有改變發生時每次都執行指定的操作,取而代之,僅在所有的變化發生完成後一次性地執行指定操作。

在notify中列出的操作稱為handler,也即notify中呼叫handler中定義的操作。

- name: template configuration file      //定義的任務,配置檔案

  template: src=template.j2 dest=/etc/foo.conf  

  notify:   //如果定義的檔案發生改變後就通知給另外一個任務

- restart memcached      //任務的名字,哪個任務呢?就是下面在handlers中定義的name的名字 

- restart apache

 

handler是task列表,這些task與前述的task並沒有本質上的不同。

handlers:

- name: restart memcached      //即上面notify下面任務的名字

  service:  name=memcached state=restarted

- name: restart apache

  service: name=apache state=restarted

上述的意思是某一個資源或配置檔案發生改變後,必須要觸發另外的操作時,就是用notify指明觸發哪一個handler,事先把觸發的操作定義好,每一個操作就叫一個handler。

 

示例演示

# yum install httpd -y   //確保管控主機安裝了httpd

# mkdir conf

# cp /etc/httpd/conf/httpd.conf conf/    //把httpd的配置檔案httpd.conf複製到建立的目錄中

# vim conf/httpd.conf

Listen 8080   //讓httpd監聽在8080埠

# vim apache.yml

- hosts: websrvs
  remote_user: root
  tasks:
  - name: install httpd package
    yum: name=httpd state=latest
  - name: install configuration file for httpd
    copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf  //把目標檔案覆蓋掉
  - name: start httpd service
    service: enabled=true name=httpd state=started

# ansible-playbook apahce.yml    //都可以執行成功

# netstat -tunlp   //在被管控主機上都可以檢視到8080埠

那麼問題來了,假如源配置檔案src=/root/conf/httpd.conf 發生改變了,比如配置檔案的監聽埠改變為80,其他都不改變,那麼再執行# ansible-playbook apahce.yml 後,httpd服務要不要重新啟動呢?會不會根據配置檔案httpd.conf的修改監聽80埠呢?

# vim conf/httpd.conf    //修改配置檔案,把埠修改為80

Listen 80          //讓httpd監聽在80埠

# ansible-playbook apache.yml     //再次執行此劇本

PLAY [websrvs] ***********************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************
ok: [192.168.184.143]
ok: [192.168.184.142]

TASK [install httpd package] *********************************************************************************************************
ok: [192.168.184.143]
ok: [192.168.184.142]

TASK [install configuration file for httpd] ******************************************************************************************
changed: [192.168.184.143]       //這裡的changed是因為發現httpd.conf檔案中監聽的埠從8080改為80,所以重新把配置檔案從管控端複製到被管控端,所以發生了改變
changed: [192.168.184.142]

TASK [start httpd service] ***********************************************************************************************************
ok: [192.168.184.142]
ok: [192.168.184.143]

PLAY RECAP ***************************************************************************************************************************
192.168.184.142            : ok=4    changed=1    unreachable=0    failed=0   
192.168.184.143            : ok=4    changed=1    unreachable=0    failed=0   

 再次檢視被管控端的埠,可以看出監聽的埠依然是8080埠,沒有發生改變,即httpd並沒有在配置檔案更改後自動重啟。

在一個服務的配置檔案發生改變時,應該讓程式重讀配置檔案或者重啟程式。

預設情況下,多次執行一個任務的時候,如果那個任務已經執行過了,為了保持冪等性,它是不會再次執行的,所以就不可能重啟。

handler就是為了解決這種問題的,它可以監控另外一個task,如果所監控的task做了某些修改,才執行指定的任務。

所以handler也是一個任務,這個任務不是上來就執行的,只有某個條件滿足時才執行。

# vim apache.yml  //再次修改劇本,新增紅色部分

- hosts: websrvs
  remote_user: root
  tasks:
  - name: install httpd package
    yum: name=httpd state=latest
  - name: install configuration file for httpd
    copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
    notify:   //如果通知有多個,每一個要使用列表,這裡雖然只有一個,但仍然使用列表進行描述。表示會觸發一個事件,觸發restart httpd這個處理器
    - restart httpd
  - name: start httpd service
    service: name=httpd enabled=true state=started
  handlers:   //和tasks是同級別的,handlers可能有多個,每一個handler都用一個"-"來引導。handler定義方式和task是一樣的,只不過handler不會直接上來就執行,只有在某個條件被觸發時,才會執行
  - name: restart httpd    //這裡要和notify定義的保持一致
    service: name=httpd state=resarted

# ansible-playbook apache.yml    //再次執行此指令碼,注意如果apache.yml中定義的源配置檔案不發生改變,是不會觸發handler的

PLAY [websrvs] ***********************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************
ok: [192.168.184.143]
ok: [192.168.184.142]

TASK [install httpd package] *********************************************************************************************************
ok: [192.168.184.143]
ok: [192.168.184.142]

TASK [install configuration file for httpd] ******************************************************************************************
changed: [192.168.184.143]   
changed: [192.168.184.142]

TASK [start httpd service] ***********************************************************************************************************
ok: [192.168.184.143]
ok: [192.168.184.142]

RUNNING HANDLER [restart httpd] **********//這裡handler已經執行*************************************************************************
changed: [192.168.184.143]          //重新啟動了被管控主機的httpd服務
changed: [192.168.184.142]

PLAY RECAP ***************************************************************************************************************************
192.168.184.142            : ok=5    changed=2    unreachable=0    failed=0   
192.168.184.143            : ok=5    changed=2    unreachable=0    failed=0