playbook字面意思,即劇本,現實中由演員按照劇本表演,在Ansible中,此次由計算機進行表演,由計算機安裝、部署應用,提供對外服務,以及組織計算機處理各類各樣的事情。python
playbook文件由YMAL語言編寫。YMAL格式是相似於JSON的文件格式,便於人理解和閱讀,同時便於書寫。首先學習瞭解一下YMAL的格式,對後面書寫playbook頗有幫助。如下爲playbook經常使用到的YMAL格式規則。linux
文件的第一行應該以「---」 (三個連字符)開始,代表YMAL文件的開始。
在同一行中,#以後的內容表示註釋,相似於shell,python和ruby。
YMAL中的列表元素以「-」開頭而後緊跟着一個空格,後面爲元素內容。
同一個列表中的元素應該保持相同的縮進。不然會被看成錯誤處理。
play中hosts,variables,roles,tasks等對象的表示方法都是鍵值中間以「:」分隔表示,「:」後面還要增長一個空格。正則表達式
首先看下面這個例子:shell
- apple - banana - orange
等價於JSON的下面這個格式:json
[ 「apple」, 「banana」, 「orange」 ]
playbook文件是經過ansible-playbook命令進行解析的,ansbile-playbook命令會根據自上而下的順序依次執行playbook文件中的內容。安全
playbook是由一個或多個「play」組成的列表。play的主要功能在於,將事先合併爲一組的主機組合成事先經過Ansible定義好的角色。將多個play組織在一個playbook中就可讓它們聯同起來按事先編排好的機制完成一系列複雜的任務。ruby
playbooks主要有如下四部分構成,分別以下。app
Target部分: 定義將要執行 playbook 的遠程主機組。
Variable部分: 定義playbook運行時須要使用的變量。
Task部分: 定義將要在遠程主機上執行的任務列表。
Handler部分: 定義task 執行完成之後須要調用的任務。運維
下面介紹下構成playbook的四個組成部分。 dom
play的主體部分是task list。
task list中的各任務按次序逐個在hosts中指定的全部遠程主機上執行,即在全部遠程主機上完成第一個任務後再開始第二個。在運行自上而下某playbook時,若是中途發生錯誤,則全部已執行任務都將回滾,所以在更正playbook後須要從新執行一次。
task的目的是使用指定的參數執行模塊,而在模塊參數中可使用變量。模塊執行一個命令,即便執行一次或屢次, 其結果是同樣的,這意味着playbook屢次執行是安全的,由於其結果均一致。tasks包含name和要執行的模塊,name是可選的,只是爲了便於用戶閱讀,建議加上去,模塊是必需的,同時也要給予模塊相應的參數。
定義tasks推薦使用module: options」的格式,例如:
service: name=httpd state=running
用於當關注的資源發生變化時採起必定的操做。handlers是和「notify」配合使用的。
「notify」這個動做可用於在每一個play的最後被觸發,這樣能夠避免屢次有改變發生時,每次都執行指定的操做,經過「notify」,僅在全部的變化發生完成後一次性地執行指定操做。
在notify中列出的操做稱爲handler,也就是說notify用來調用handler中定義的操做。
注意:在notify中定義的內容必定要和handlers中定義的「 - name」內容同樣,這樣才能達到觸發的效果,不然會不生效。
(4)tags
tags用於讓用戶選擇運行或略過playbook中的部分代碼。Ansible具備冪等性,所以會自動跳過沒有變化的部分;可是當一個playbook任務比較多時,一個一個的判斷每一個部分是否發生了變化,也須要很長時間。所以,若是肯定某些部分沒有發生變化,就能夠經過tags跳過這些代碼片段。
使用ansible-playbook運行playbook文件,輸出的內容爲JSON格式。而且由不一樣顏色組成,便於識別。通常而言,輸出內容中,每一個顏色表示的含義以下。
綠色表明執行成功,但系統保持原樣。
黃色表明系統狀態發生改變,也就是執行的操做生效。
紅色表明執行失敗,會顯示錯誤信息。
下面是一個簡單的playbook文件:
- name: create user hosts: 172.16.213.231 user: root gather_facts: false vars: user1: testuser tasks: - name: start createuser user: name="{{user1}}"
上面的playbook 實現的功能是新增一個用戶,每一個參數含義以下。
name參數對該playbook實現的功能作一個概述,後面執行過程當中,會輸出name的值。
hosts參數指定了對哪些主機進行操做。
user參數指定了使用什麼用戶登陸到遠程主機進行操做。
gather_facts參數指定了在執行task任務前,是否先執行setup模塊獲取主機相關信息,此參數默認值爲true,表示開啓,若是咱們在task中要使用facts信息時,就須要開啓此功能。不然能夠設置爲false。設置爲false能夠加快playbook的執行速度。
vars參數,指定了變量,這裏指字一個user1變量,其值爲testuser,須要注意的是,變量值必定要用引號括起來。
tasks指定了一個任務,其下面的name參數一樣是對任務的描述,在執行過程當中會打印出來。user是一個模塊,user後面的name是user模塊裏的一個參數,而增長的用戶名調用了上面user1變量的值。
在playbook中,task部分是整個任務的核心,咱們前面介紹的ansible的經常使用模塊,例如commands模塊、shell模塊、file模塊、cron模塊、user模塊等,在playbook中仍然可用,每一個模塊所使用的參數以及含義跟命令行模式下也徹底同樣,只不過寫法不一樣而已,下面經過幾個例子來看看playbook中常見功能模塊的寫法。
下面是一個playbook示例,test.yml文件內容以下:
- hosts: hadoophosts remote_user: root tasks: - name: create hadoop user user: name=hadoop state=present - name: create hadoop directory and chmod/chown file: path=/opt/hadoop state=directory mode=0755 owner=hadoop group=hadoop - name: synchronize hadoop program synchronize: src=/data/hadoop/ dest=/opt/hadoop - name: Setting environment variables shell: echo "export JAVA_HOME=/usr/jdk" >> /etc/profile
這個playbook文件中,使用了user、file、synchronize和shell模塊,文件開始定義了一個主機組hadoophosts,而後設置root用戶在遠程主機上執行操做,接着,就是task任務的開始,「- name」是描述性信息,用來標識任務執行內容和進度,第一個task用來建立一個hadoop用戶,使用了user模塊,注意,上面的user表示ansible的user模塊,而user後面的name、state是user模塊的參數,這些參數含義上面已經作過介紹,這裏就不在重複了
。
下面還有file模塊、synchronize模塊以及shell模塊,它們的寫法跟user模塊相似,也再也不過多介紹。
今後文件能夠看出,經過playbook模式編寫的文件更加簡潔、易懂,只要設置好了任務的運行策略、順序,每次須要用到這個操做的話,直接執行就能夠了。執行的方式以下:
[root@server239 ansible]# ansible-playbook test.yml
除了前面已經介紹過的ansible模塊,還有一些模塊在playbook中也常常用到,下面再介紹一些經常使用的playbook模塊。
unarchive模塊用來實現解壓縮,也就是將壓縮文件解壓分發到遠程不一樣節點上。只需記住以下幾個參數便可:
src: 源文件路徑,這個源文件在管理機上。
dest: 指定遠程主機的文件路徑。
mode:設置遠程主機上文件權限。
看下面這個例子:
- hosts: 172.16.213.231 remote_user: root gather_facts: false tasks: - name: unarchive spark files unarchive: src=/src/spark.tar.gz dest=/opt
這個操做是將管理機上的/src/spark.tar.gz文件傳輸到遠程主機上後進行解壓縮,並將解壓縮後的文件放到遠程主機的/opt目錄下。注意,這個例子中咱們設置了gather_facts選項爲false,這是由於下面的操做中,沒有用到facts信息。
在自動化運維中,對文件進行內容替換是一個很是常見的場景,好比修改、刪除、添加操做系統的某些參數等,Ansible中雖然提供了shell模塊結合sed命令來達到替換的效果,但常常會遇到須要轉義的問題,而且考慮到可讀性性和可維護性等多方面因素,使用Ansible自帶的替換模塊是一個不錯的選擇。Ansible經常使用的替換模塊爲replace和lineinfile。
replace模塊能夠根據指定的正則表達式替換遠程主機下某個文件中的內容,經常使用的參數有以下幾個:
path:要操做的遠程主機上文件的路徑。
regexp:正則表達式,指定替換規則。
replace:指定最終要替換的字符串。
backup:是否在修改文件以前對文件進行備份,yes是進行備份。
看下面這個例子:
- hosts: 172.16.213.231 remote_user: root tasks: - name: modify selinux replace: path=/etc/selinux/config regexp="enforcing" replace=disabled backup=yes
這個操做是對遠程主機上/etc/selinux/config文件中的enforcing字符串進行替換,替換爲disabled,替換前進行備份。其實就是關閉遠程主機上selinux服務。
最後,再介紹一下lineinfile,此模塊也能夠實現replace的功能,但lineinfile功能更增強大,支持的參數也比較多,經常使用參數含義以下:
path:操做的遠程主機上的文件路徑
regexp:正則表達式,要替換的內容規則
line:指定替換後的文本內容
state:當設置爲absent表明刪除匹配的行
insertafter:insertafter參數能夠將文本插入到「指定的行」以後
insertbefore:insertbefore參數能夠將文本插入到「指定的行」以前
backup:進行替換操做前是否進行備份
下面來看一個基於lineinfile的playbook任務:
- hosts: 172.16.213.231 remote_user: root tasks: - lineinfile: dest=/etc/profile insertafter='ulimit(.*)' line="ulimit -c unlimited" - lineinfile: dest=/etc/profile line="export JAVA_HOME=/usr/jdk" - lineinfile: dest=/etc/selinux/config regexp='SELINUX=(.*)' line='SELINUX=disabled' - lineinfile: dest=/etc/resolv.conf regexp='search(.*)' state=absent
這個playbook任務中,調用了四次lineinfile替換操做,第一次是在/etc/profile文件中找到以ulimit開頭的行,並在後面添加一行內容"ulimit -c unlimited",第二次是在/etc/profile文件的最後添加一個JAVA_HOME路徑,第三次是修改/etc/selinux/config文件中以「SELINUX=」開頭的行,將其替換爲「SELINUX=disabled」,其實就是關閉selinux,最後一個操做是在/etc/resolv.conf文件找查找以search開頭的行,而後將其刪除掉。
ansible中定義變量的方式有不少種,能夠將模塊的執行結果註冊爲變量,也能夠在roles中的文件內定義變量,還可使用內置變量等,而register、set_fact均可用來註冊一個變量。
使用register選項,能夠將當前task的輸出結果賦值給一個變量,看下面這個例子:
- hosts: 172.16.213.231 remote_user: root tasks: - name: ps command shell: hostname register: host_result - debug: var=host_result
此例子是將在遠程主機上執行的shell命令「hostname」的輸出結果賦值給變量host_result,而後再將變量引用並使用debug模塊輸出。輸出結果是json格式的。注意,此例子最後還使用了debug模塊,此模塊用於在調試中輸出信息.
下面是上面playbook的debug輸出結果:
TASK [debug] ********************************************************** ok: [172.16.213.231] => { "host_result": { "changed": true, "cmd": "hostname", "delta": "0:00:00.007228", "end": "2020-04-01 04:42:34.254587", "failed": false, "rc": 0, "start": "2020-04-01 04:42:34.247359", "stderr": "", "stderr_lines": [], "stdout": "server231.localdomain", "stdout_lines": [ "server231.localdomain" ] } }
能夠看出,此輸出是一段json格式的數據,最頂端的key爲host_result,大括號內還有多個二級key,咱們想要的結果是輸出遠程主機的主機名便可,不須要這些額外的二級key信息,如何實現這個要求呢,若是想要輸出json數據的某二級key項,可使用"key.dict"或"key['dict']"的方式引用便可。從上面輸出能夠看到,咱們須要的二級key是stdout項,因此要僅僅輸出此項內容,能夠將變量引用改成host_result.stdout便可,也就是將上面的playbook任務改爲以下內容:
- hosts: 172.16.213.231 remote_user: root tasks: - name: hostname command shell: hostname register: host_result - debug: var=host_result.stdout - debug: 'msg="output: {{host_result.stdout}}"'
在這個playbook中,咱們又增長了一個debug參數,debug模塊經常使用的參數有兩個,分別是msg和var,它們均可以引用變量輸出信息,但有一點小區別,msg能夠輸出自定義信息,而且變量須要雙大括號包含起來,而var參數只能輸出變量,而且不須要雙大括號。
修改後的playbook執行debug輸出結果以下:
TASK [debug] ******************************************************** ok: [172.16.213.231] => { "host_result.stdout": "server231.localdomain" } TASK [debug] ******************************************************** ok: [172.16.213.231] => { "msg": "output: server231.localdomain" }
從輸出可知,這個纔是咱們想要的結果。
set_fact和register的功能很相似,它也能夠將task輸出賦值給變量。set_fact更像shell中變量的賦值方式,能夠將某個變量的值賦值給另外一個變量,也能夠將字符串賦值給變量。看下面這個例子:
- hosts: 172.16.213.231 remote_user: root tasks: - name: hostname command shell: hostname register: host_result - set_fact: var1="{{host_result.stdout}}" - set_fact: var2="This is a string" - debug: msg="{{var1}} {{var2}}"
這個例子是將hostname的輸出結果賦值給host_result變量,而後經過set_fact將host_result變量賦值給var1變量,接着又將一個字符串賦值給var2變量,最後,經過debug模塊輸出這些變量信息。注意這些模塊的使用方式和書寫格式。
這個playbook的輸出結果爲:
TASK [debug] ********************************************************** ok: [172.16.213.231] => { "msg": "server231.localdomain This is a string" }
ansible默認只會對遠程主機執行操做,但有時候若是須要在管理機本機上執行一些操做,該如何實現呢,這個實現的方法有不少,能夠經過delegate_to(任務委派)來實現,也能夠經過connection:local方法,還能夠經過local_action關鍵字來實現。
下面來看一個例子,說明它們的用法。
- hosts: 172.16.213.231 remote_user: root gather_facts: true tasks: - name: connection shell: echo "connection . {{inventory_hostname}} $(hostname) ." >> /tmp/local.log connection: local - name: delegate_to shell: echo "delegate_to . {{inventory_hostname}} $(hostname) ." >> /tmp/local.log delegate_to: localhost - name: local_action local_action: shell echo "local_action. {{inventory_hostname}} $(hostname)" >> /tmp/local.log
這個例子中,依次使用了connection、delegate_to和local_action三種方式,還使用了一個變量{{inventory_hostname}},這是ansible的一個內置變量,它用來獲取遠程主機的主機名,說到主機名,其實就是用到了facts信息,因此,須要設置gather_facts選項爲true,另外,$(hostname)是shell裏面的變量,也是用來獲取主機名,此例子實現的功能是將遠程主機的主機名依次輸出到管理機的/tmp/local.log文件中。