ansible-playbook是ansible工具中的核心,對比ad-hoc(ansible)命令,能夠把playbook理解爲一系列動做的組成,結果傳遞、判斷等等,這些是ansible命令沒法完成的。
ansible中如何使用yaml編寫playbook,這對於不少人來講不容易搞懂,特別是層級縮進關係方面。
我打算從三個層面講playbook編寫,正如4.7所說,按照playbook、play、tasks三個層面理解
playbook關鍵字
附上官網關關鍵字連接,本文對其中一部分舉例,注意只是一部分介紹,我也不懂所有,介紹這些只爲了理解playbook書寫規則,規則掌握了,後續學習基本不會迷路html
# 先附上一部分綜合例子,便於後面理解 --- - hosts: lzcx # gather_facts: False connection: local become: yes remote_user: root vars: var1: value1 var2: value2 tasks: - debug: msg="{{var2}} {{var3}}" vars: var2: value3 var3: value4 when: ansible_distribution_major_version == "7" - name: print something shell: echo {{item}} with_items: - zhangsan - lisi - wangwu register: hi ignore_errors: True - debug: msg="{% for i in hi.results %} {{i.stdout}} {% endfor %}"
playbook層面,由於一個playbook通常一個play,便於維護,這裏不做詳細介紹。mysql
play層面,對應元素hosts, tasks, 變量vars, connection, remote_user, become、name, 等等,這裏介紹常見的元素。web
指定inventory中的主機或主機組,一個play對應一個hosts,通常yaml文件裏面就一個hosts,定義在開頭
取值方面,多個值按逗號","分割,支持2.5節裏面的匹配,具體查看2.5節sql
獲取遠程主機信息,關閉時能加快playbook執行速度,前提是不須要獲取主機信息,當須要調用主機信息相關變量,能夠用setup模塊查看,例如"ansible_all_ipv4_addresses",不能關閉該選項
本章開始的gether_facts註釋打開,則when語句行須要註釋shell
說明文檔原文"connection type to use (default=smart)",默認會根據環境自動設置,通常不用本身設置apache
是否切換爲其餘用戶,值爲yes|no編程
切換的用戶json
須要注意的是vars是能夠存在了play層面和tasks層面的元素,同時tasks層面的vars變量會覆蓋play層面的同名變量,這一點比較特殊,參考 5.3.1.四、 vars定義變量centos
ansible中定義變量的⽅式有不少種,⼤致有:(1)將模塊的執⾏結果註冊爲變量;(2)直接定義字典類型的變量;(3)role中⽂件內定義變量;(4)命令⾏傳遞變量;(5)藉助with_items迭代將多個task的結果賦值給⼀個變量;(6)inventory中的主機或主機組變量;(7)內置變量數組
使⽤register選項,能夠將當前task的輸出結果賦值給⼀個變量,注意,模塊的輸出結果是json格式的,因此,引⽤變量時要指定引⽤的對象
--- - hosts: lzcx tasks: - shell: echo 'hahaha' register: temp - debug: var=temp.stdout
set_fact和register的功能很類似,也是將值賦值給變量。它更像shell中變量的賦值⽅式,能夠將某個變量的值賦值給另⼀個變量,也能夠將字符串賦值給變量
--- - hosts: lzcx tasks: - shell: echo haha register: say_ha - set_fact: var1="{{say_ha.stdout}}" - set_fact: var2="your name is" - debug: msg="{{var2}} {{var1}}"
能夠在play或task層次使⽤vars定義字典型變量。若是同名,則task層次的變量覆蓋play層次的變量
--- - hosts: lzcx vars: var1: value1 var2: value2 tasks: - debug: msg="{{var1}} {{var2}}" vars: var2: value2.2 # tasks層次的變量會覆蓋play層次的同名變量
因爲role是整合playbook的,它有默認的⽂件組織結構。其中有⼀個⽬錄vars,其內的main.yml⽤於定義變量。還有defaults⽬錄內的main.yml則是定義role默認變量的,默認變量的優先級最低
ansible和ansible-playbook命令的"-e"選項均可以傳遞變量,傳遞的⽅式有兩種: -e key=value 和 -e @var_file 。注意,當key=value⽅式傳遞變量時,若是變量中包含特殊字符,必須防⽌其被shell解析
ansible localhost -m shell -a "echo {{say_hi}}" -e 'say_hi="hello world"' ansible localhost -m shell -a "echo {{say_hi}}" -e @/tmp/var_file1.yml
# /tmp/var_file1.yml 內容 --- say_hi: zhangsan lisi
ansible中能夠藉助with_items實現列表迭代的功能,做⽤於變量註冊的⾏爲上,就能夠實現將多個結果賦值給同⼀個變量
例以下⾯的playbook中,給出了3個item列表,並在shell模塊中經過固定變量"{{item}}"分別迭代,第⼀次迭代的是haha,第⼆次迭代的是heihei,第三次迭代的是hehe,也就實現了3次循環。最後,將結果註冊爲變量hi_var。還可使⽤f or循環遍歷列表
# --- - hosts: lzcx connection: local remote_user: root become: yes tasks: - name: test with items shell: echo "{{item}}" with_items: - haha - heihei - hehe register: hi_var - debug: var=hi_var.results[0].stdout - debug: var=hi_var.results[1].stdout - debug: var=hi_var.results[2].stdout - debug: msg="{% for i in hi_var.results %} {{i.stdout}} {% endfor %}"
每次迭代的過程當中,調⽤item的模塊都會將結果保存在⼀個key爲results的數組中。所以,引⽤迭代後註冊的變量時,須要在變量名中加上results,並指定數組名。例如上⾯的 hi_var.results[N].stdout
建議使用循環輸出格式
在inventory⽂件中能夠爲主機和主機組定義變量,不只包括內置變量賦值,還包括⾃定義變量賦值。
主機變量優先級⾼於主機組變量,給定的主機組變量優先級⾼於all特殊組
# 自定義臨時清單 cat /tmp/hosts 192.168.1.214 ansible_ssh_port=22 var1=1 # 主機內置變量,這類變量不能調用 [centos7] 192.168.1.214 var1=2 # 主機變量,優先級最高 [centos7:vars] var1=2.2 # 主機組變量,優先級次之 var2=3 [all:vars] var2=4 # 全部組變量,優先級最低 # 執行查看變量優先級 ansible 192.168.1.214 -i /tmp/hosts -m shell -a 'echo "{{var1}} {{var2}}"' # 結果 192.168.1.214 | CHANGED | rc=0 >> 2 3
ansible除了inventory中內置的⼀堆不可被引⽤的設置類變量,還有⼏個全局均可以引⽤的內置變量,主要有如下⼏個:
inventory_hostname、inventory_hostname_short、groups、groups_names、hostvars、play_hosts、inventory_dir、ansible_version
ansible中的循環都是藉助迭代來實現的。基本都是以"with_"開頭。如下是常見的⼏種循環
參考 5.3.1.7,
使⽤"with_dict"能夠迭代字典項。迭代時,使⽤"item.key"表⽰字典的key,"item.value"表⽰字典的值
另⼀種狀況,字典是已存儲好的。例如ansible f acts中的ansible_eth0.ipv4,這種狀況下,with_dict處能夠直接指定該字典的key
例如,拷貝⼀堆⽤通配符匹配出來的⽂件到各遠程主機上,注意,通配符⽆法匹配"/",所以⽆法遞歸到⼦⽬錄中,也就⽆法迭代⼦⽬錄中的⽂件
能夠將命令⾏的輸出結果按⾏迭代。例如,find⼀堆⽂件出來,copy⾛
--- - hosts: localhost tasks: - copy: src="{{item}}" dest=/tmp/yaml with_lines: - find /tmp -type -f -name "*.yml"
嵌套迭代是指屢次迭代列表項
--- - hosts: localhost tasks: - debug: msg="{{item[0]}} & {{item[1]}}" with_nested: - [a, b] - [1, 2, 3]
在ansible中,只有when語句能夠實現條件判斷。when判斷的對象是task,因此和task在同⼀列表層次。它的判斷結果決定它所在task是否執⾏,⽽不是它下⾯的task是否執⾏。when中引⽤變量的時候不須要加{{ }}符號。
tasks: - name: config the yum repo for centos 6 yum_repository: name: epel description: epel baseurl: http://mirrors.aliyun.com/epel/6/$basearch/ gpgcheck: no when: ansible_distribution_major_version == "6"
when語句支持邏輯操做,或(or)、與(and)也能夠分行寫、非(not)
支持直接引用定義了布爾值的變量
可使⽤jinja2的 defined 來測試變量是否已定義,使⽤ undefined 能夠取反表⽰未定義
tasks: - shell: echo "I've got '{{ foo }}' and am not afraid to use it!" when: foo is defined - fail: msg="Bailing out. this play requires 'bar'" when: bar is undefined
該選項相似編程語言中的try語句,可以抓取報錯信息,讓playbook執行完成
--- - hosts: lzcx tasks: - name: this command prints FAILED when it fails command: /usr/bin/example-command -x -y -z register: command_result failed_when: "FAILED in command_result.stdout"
很明顯,例子中的命令不存在,即執行失敗,failed_when經過調用註冊變量打印錯誤信息,此時會生成yaml文件同名的retry後綴格式文件
用於給模塊自定義命名,若是沒有設置,默認輸出模塊名
忽略報錯信息
--- - hosts: lzcx tasks: - name: this command prints FAILED when it fails command: /usr/bin/example-command -x -y -z register: command_result ignore_errors: True failed_when: "FAILED in command_result.stdout"
此時不會生成yaml文件同名的retry後綴格式文件
能夠爲playbook中的每一個任務都打上標籤,標籤的主要做⽤是能夠在ansible-playbook中設置只執⾏哪些被打上tag的任務或忽略被打上tag的任務,如下是ansible-playbook中關於tag的選項
--list-tags # list all available tags
t TAGS, --tags=TAGS # only run plays and tasks tagged with these values
--skip-tags=SKIP_TAGS # only run plays and tasks whose tags do not match these values
tasks: - name: make sure apache is running service: name=httpd state=started tags: apache - name: make sure mysql is runnig service: name=mysqld state=started tags: mysql
include的設計是爲了讓playbook模塊化,將任務中的playbook分拆,細化各個部分,便宜維護和複用
能夠將task列表和handlers獨⽴寫在其餘的⽂件中,而後在某個playbook⽂件中使⽤include來包含它們。除此以外,還能夠寫獨⽴的playbook⽂件,使⽤include來包含這個⽂件。也便是說,include能夠導⼊兩種⽂件:導⼊task、導⼊playbook。除此以外還能夠傳入變量給include
注意:nsible2.4以前,引入外部task或play只有include方法,在2.4以後,新增了includes和imports方法,具體查看官網說明includes和imports
用法一:
# 引入task列表文件,a.yml --- - name: execute ntpdate shell: /usr/sbin/ntpdate ntp1.aliyun.com - name: execute the variables debug: msg="{{hi}} {{haha}}"
同級目錄下調用a.yml
--- - hosts: localhost tasks: - include: a.yml hi="Hello world" # 傳入參數 vars: haha: "ni hao ya" # 用vars傳遞參數
用法二:
# 引入整個playbook,因爲是引入整個playbook,加載裏面的play,須要注意層級關係 - name: this is a play at the top level of a file hosts: localhost become: yes remote_user: root tasks: - name: say hi tags: foo shell: echo "hahaha" - include: webserver.yml # 注意層級關係 - include: dbserver.yml
從上面兩個例子能夠看出,include和vars元素同樣,是能夠跨層級出現的,二者跨的層級不一樣,vars是跨play和tasks;include是跨tasks和playbook
notify:ansible中⼏乎全部的模塊都具備冪等性,這意味着被控主機的狀態是否發⽣改變是能被捕捉的,即每一個任務的changed=true或changed=false
ansible在捕捉到changed=true時,能夠觸發notify組件,notify是⼀個組件,並⾮⼀個模塊,它能夠直接定義action,其主要⽬的是調⽤handler
notify這個action可用於在每一個play的最後被觸發,這樣能夠避免屢次有改變發生時每次都執行指定的操做,取而代之,僅在全部的變化發生完成後一次性地執行指定操做
handlers:handlers是一些 task 的列表,經過名字來引用,它們和通常的 task 並無什麼區別,handlers裏面的name必需要和notify一致,最佳的應用場景是用來重啓服務,或者觸發系統重啓操做
tasks: - name: template configuration file template: src=template.j2 dest=/etc/foo.conf notify: - restart memcached - restart apache handlers: - name: restart memcached service: name=memcached state=restarted - name: restart apache service: name=apache state=restarted
ansible中的playbook到此介紹,基礎部分相信可以搞懂了,至少版本新特性,進階高級用法等等,對於掌握規則來講不難,規則掌握以後,跟着官網例子學習就能夠了