當需要對多個遠程節點,做很多操作的時候,如果將所有的內容都書寫到一個playbooks中,這就會產生一個很大的文件,而且裏面的某些內容也很難複用。此時不得不考慮怎麼樣分隔及組織相關的文件。
最基本的,可以將任務列表單獨分隔到一個小文件裏,然後在tasks中包含該文件即可。同樣的handlers其實也是一個任務列表(裏面指定的任務都需要有一個全局唯一的名稱),所以也可以在handlers中包含單獨定義好的handlers任務文件。
playbooks也可以包含其他playbooks文件。
playbooks中的文件包含使得你在大多數情況下並不需要考慮具體功能的實現細節,只需要複用某些已有的文件即可,類似於編程中的封裝概念一樣,當某個功能封裝並測試OK以後,在需要的地方調用即可,如,一個人會開車,但他並不需要了解發動機的原理是什麼。
Ansible中的“角色”就是利用文件包含這個功能來使得文件組織的更加清晰明瞭,並具有很高的複用性。
任務文件包含和重用
如果想在多個playbooks之間服用任務列表,那麼include將會是個很好的方法。
官方示例的一個很小的任務列表文件:
--- # possibly saved as tasks/foo.yml - name: placeholder foo command: /bin/foo - name: placeholder bar command: /bin/bar
在playbooks中包含該文件:
tasks: - include: tasks/foo.yml
同時也可以向包含文件內傳遞參數,稱爲“參數化包含”。
例如,想要部署多個wordpress,可以將部署操作寫到一個文件中,保存爲wordpress.yml,然後以下邊這樣包含文件並傳遞參數:
tasks: - include: wordpress.yml wp_user=timmy - include: wordpress.yml wp_user=alice - include: wordpress.yml wp_user=bob
在wordpress.yml可以` wp_user `的方式使用變量。
還可以傳遞一個參數列表:
tasks: - { include: wordpress.yml, wp_user: timmy, ssh_keys: [ 'keys/one.txt', 'keys/two.txt' ] }
以上兩種傳遞參數都稱爲顯式傳參,在vars中定義的變量特可以在被包含文件中使用。
也可以在handlers中包含文件。如,想要定義一個重啓apache的handler,這時候只需要爲所有的playbooks定義一個handler即可,保存爲handlers.yml:
--- # this might be in a file like handlers/handlers.yml - name: restart apache service: name=httpd state=restarted
在playbooks中包含該文件:
handlers: - include: handlers/handlers.yml
還可以將一個playbooks文件導入到另外一個playbooks中,這樣的話,只需要定義一個頂級的且內容爲一些較爲通用的操作的playbooks文件,然後再文件中包含其他的playbooks即可。
- name: this is a play at the top level of a file hosts: all remote_user: root tasks: - name: say hi tags: foo shell: echo "hi..." - include: load_balancers.yml - include: webservers.yml - include: dbservers.yml
示例:
[root@web1 ansible]# tree /etc/ansible/ /etc/ansible/ ├── handlers │ └── restart.yml ├── hosts ├── main.yml ├── tasks │ └── apache.yml └── templates └── httpd.j2 [root@web1 ansible]# cat tasks/apache.yml --- - name: config httpd.file template: src=templates/httpd.j2 dest=/etc/httpd.conf [root@web1 ansible]# cat handlers/restart.yml --- - name: restart apache service: name=httpd state=restarted [root@web1 ansible]# cat main.yml --- - hosts: webservers remote_user: root vars: http_port: 8085 max_clients: 123 tasks: - include: tasks/apache.yml handlers: - include: handlers/restart.yml [root@web1 ansible]# ansible-playbook /etc/ansible/main.yml PLAY [webservers] ************************************************************* GATHERING FACTS *************************************************************** ok: [192.168.1.65] TASK: [config httpd.file] ***************************************************** changed: [192.168.1.65] PLAY RECAP ******************************************************************** 192.168.1.65 : ok=2 changed=1 unreachable=0 failed=0
注:上邊文件雖然定義幷包含了重啓apache的handler,但並沒有任務一個任務來觸發該handler。所以並沒有執行重啓操作,即使配置文件發生了改變。
角色
“文件包含”本身只是很簡單且很單純的包含各個子文件,當文件較多的時候,其組織方式也並是好,且需要人爲的來指定包含哪些文件。Ansible中的“角色”會基於已知的文件結構來自動加載特定的變量文件、任務文件一級handlers文件,使用角色來組織內容可以更好的實現共享。
官方的一個使用角色組織內容的文件結構:
site.yml webservers.yml fooservers.yml #playbooks文件,最後ansible執行的就是這些文件 roles/ common/ files/ templates/ #保存操作中所需要的模板文件 tasks/ #保存任務列表文件 handlers/ #保存handler文件 vars/ #保存定義變量的文件 defaults/ #默認變量文件 meta/ #角色的依賴關係 webservers/ files/ templates/ tasks/ handlers/ vars/ defaults/ meta/
在playbooks中這樣書寫內容,如上邊的webservers.yml文件:
--- - hosts: webservers roles: - common - webservers
每個角色都會遵循以下原則:
- 如果`roles/x/tasks/main.yml`存在,裏面的任務列表會被添加到`play`中。
- 如果`roles/x/handlers/main.yml`存在,裏面的`handlers`會被添加到`play`中。
- 如果`roles/x/vars/main.yml`存在,裏面的變量會被添加到`play`中。
- 如果`roles/x/meta/main.yml`存在,裏面的角色依賴會被添加到角色列表中。
- 在`roles/x/files`任務所需要被複制的文件,無需絕對路徑或者相對路徑都可以引用該文件。
- 在`roles/x/files`中的任務腳本都可以直接使用該文件,無需指定絕對路徑或者是相對路徑。
- 在`roles/x/templates`中的模板,無需指定絕對路徑或者相對路徑,都可以直接使用文件名引用該文件。
- 需要包含在`roles/x/tasks`中的任務文件時,無需指定絕對路徑或者相對路徑,可以直接使用文件名包含。
tasks,handlers,vars,meta目錄下main.yml中的內容都會被自動調用。
如果某些文件不存在的話,將會自動跳過,所以`vars/`之類的子目錄如果用不到的話也可以不創建。
也可以給角色傳遞參數,在playbooks文件定義:
--- - hosts: webservers roles: - common - { role: foo_app_instance, dir: '/opt/a', port: 5000 } - { role: foo_app_instance, dir: '/opt/b', port: 5001 }
有時候也可以給角色添加執行條件:
--- - hosts: webservers roles: - { role: some_role, when: "ansible_os_family == 'RedHat'" }
當遠程節點的操作系統爲RedHat的時候纔會執行"some_role"中的操作。
如果在某個play中定義了一個tasks任務塊,它將會在橘色執行完成後再執行。
定義在角色執行之前或者執行後需要執行的任務:
--- - hosts: webservers pre_tasks: - shell: echo 'hello' roles: - { role: some_role } tasks: - shell: echo 'still busy' post_tasks: - shell: echo 'goodbye'
示例:
[root@web1 ~]# tree /etc/ansible/ /etc/ansible/ ├── hosts ├── roles │ └── webservers │ ├── defaults │ ├── files │ ├── handlers │ │ ├── main.yml │ │ └── restart.yml │ ├── meta │ ├── tasks │ │ ├── apache.yml │ │ └── main.yml │ ├── templates │ │ └── httpd.j2 │ └── vars │ └── main.yml └── webservers.yml [root@web1 ~]# cat /etc/ansible/roles/webservers/tasks/main.yml --- - include: apache.yml [root@web1 ~]# cat /etc/ansible/roles/webservers/tasks/apache.yml --- - name: config httpd.file template: src=httpd.j2 dest=/etc/httpd.conf notify: - restart apache [root@web1 ~]# cat /etc/ansible/roles/webservers/handlers/main.yml --- - include: restart.yml [root@web1 ~]# cat /etc/ansible/roles/webservers/handlers/restart.yml --- - name: restart apache service: name=httpd state=restarted [root@web1 ~]# cat /etc/ansible/roles/webservers/vars/main.yml --- http_port: 8099 max_clients: 321 [root@web1 ansible]# ansible-playbook /etc/ansible/webservers.yml PLAY [webservers] ************************************************************* GATHERING FACTS *************************************************************** ok: [192.168.1.65] TASK: [webservers | config httpd.file] **************************************** changed: [192.168.1.65] NOTIFIED: [webservers | restart apache] *************************************** changed: [192.168.1.65] PLAY RECAP ******************************************************************** 192.168.1.65 : ok=3 changed=2 unreachable=0 failed=0
有些子目錄並沒有使用到,如meta目錄,就可以不用創建。
角色依賴
角色依賴使得當使用某個角色的時候自動將其他角色導入。角色依賴保存在角色目錄下的`meta/main.yml`文件中,該文件包含一個角色和參數的列表,這些內容會在某個特定的角色之前插入。即在某個特定的角色執行之前會先執行其依賴。
--- dependencies: - { role: common, some_parameter: 3 } - { role: apache, port: 80 } - { role: postgres, dbname: blarg, other_parameter: 12 }
也可以以絕對路徑的方式定義:
--- dependencies: - { role: '/path/to/common/roles/foo', x: 1 }
角色依賴總在包含它們的角色執行之前而執行,而且是遞歸執行。默認情況下,角色也只能作爲一個依賴關係添加一次,如果另一個角色也將它列爲一個依賴關係,它將不再運行。但是在`meta/main.yml`設置`allow_duplicates: yes`,可以突破這個限制。如一個稱爲`car`的角色需要添加一個名爲`wheel`的角色作爲它的依賴:
--- dependencies: - { role: wheel, n: 1 } - { role: wheel, n: 2 } - { role: wheel, n: 3 } - { role: wheel, n: 4 }
meta/main.yml內容:
--- allow_duplicates: yes dependencies: - { role: tire } - { role: brake }
執行的結果會是類似下邊這樣:
tire(n=1) brake(n=1) wheel(n=1) tire(n=2) brake(n=2) wheel(n=2) ... car
總結:
角色和文件包含,在平時的運維工作中會經常使用到,而且使得文件組織更加清晰,複用性較高。可以對遠程節點主機進行大量且複雜有序的操作。