
前文我們了解了ansible有兩種執行方式ad-hoc和ansible-playbook,ad-hoc主要用于臨時指令的執行,而playbook我們可以了解為ad-hoc的集合,有點類似shell腳本,ad-hoc就相當于shell腳本裡的某條任務語句,playbook就相當于整個shell腳本。playbook是由一個或多個“play”組成的清單,play的主要功能在于将預定義的一組主機,裝扮成事先通過ansible中的task定義好的角色。task實際是調用ansible的一個子產品,将多個play組織在一個playbook中,即可以讓他們聯合起來,按事先編排的機制執行預定義的動作。
一、playbook簡介
ansiblie的任務配置檔案被稱為playbook,俗稱“劇本”,每一個劇本(playbook)中都包含了一系列的任務,這每個任務在ansible中又被稱為“戲劇”(play),一個劇本中包含多出戲劇。。
前文我們了解了ansible有兩種執行方式ad-hoc和ansible-playbook,ad-hoc主要用于臨時指令的執行,而playbook我們可以了解為ad-hoc的集合,有點類似shell腳本,ad-hoc就相當于shell腳本裡的某條任務語句,playbook就相當于整個shell腳本。playbook是由一個或多個“play”組成的清單,play的主要功能在于将預定義的一組主機,裝扮成事先通過ansible中的task定義好的角色。task實際是調用ansible的一個子產品,将多個play組織在一個playbook中,即可以讓他們聯合起來,按事先編排的機制執行預定義的動作。
如以上圖示,使用者可以把多條任務(ad-hoc任務)寫到playbook中,使用者用ansible-playbook指令調用執行編排好的playbook,ansible會讀取playbook中的每一條play和task,并按照playbook中的順序從上至下依次執行,ansible會調用每個task中定義的子產品去依次執行相應的任務,并按照playbook中指定的主機去主機清單裡比對對應的主機,然後通過ssh認證,把編譯好的相應的任務檔案發送到對應的主機或網絡裝置上執行,最後傳回執行的狀态。
二、YAML簡介
playbook采用yaml語言編寫,yaml是一個可讀性高的用來表達資料序列格式的語言,它參考了其他很多種語言,包括:XML、C語言、python、perl以及電子郵箱格式RFC2822等。Clark Evans在2001年首次發表了這種語言,另外Ingy döt Net與Oren Ben-Kiki也是這語言的共同設計者。YAML( YAML Ain't Markup Language),即yaml不是标記語言。不過在開發這種語言時,yaml的意思其實是:"Yet Another Markup Language"(仍是一種标記語言)
ymal特性
1)YAML的可讀性好
2)YAML和腳本語言的互動性好
3)YAML使用實作語言的資料類型
4)YAML有一個一緻的資訊模闆
5)YAML易于實作,可以基于流程處理,表達能力強,擴充性好
更多的内容及規範請參考官方文檔http://www.yaml.org
三、playbook文法簡介
1)需要以“---”(3個減号)開始,且需頂行首寫。另外還有選擇性的連續三個點号(...)用來表示檔案的結尾。
2)次行開始正常寫playbook的内容,建議次行寫該playbook的功能,當然不寫也是可以的。
3)使用“#”号注釋代碼。
4)縮進必須統一,不能空格tab混用。
5)縮進的級别必須是一緻的,同樣的縮進代表同樣級别,程式判别配置的級别是通過縮進結合換行來實作的。
6)YAML檔案内容和Linux系統大小寫判斷方式一直,區分大小寫(大小寫敏感),k/v的值均大小寫敏感。
7)k/v的值可同行寫也可換行寫。同行使用“:”分隔,換行寫需要以“-”分隔。
8)v可以是字元串,也可以另外一個清單,當然也可以是字典。
9)一個完整的代碼塊功能最少需要有name:xxx(對任務的描述)。
10)一個name隻能包括一個task
11)yaml檔案擴充名通常為yml或yaml
list:清單,其所有元素均使用“-”開頭
示例:
---
# A list of tasty fruits
- apple
- orange
- strawberry
- mango
~
dictionary:字典,通常由多個key與value構成
---
#An employee record
name: example developer
job: developer
skill: elite
當然也可以将key:value放置于{}中進行表示,用“,”分隔多個key:value
---
#An employee record
{name: example developer,job: developer, skill: elite}
~
YAML的文法和其他高階語言類似,并且可以簡單表達清單、散清單、标量等資料結構。其結構(Structure)通過空格來展示,序列(Sequence)裡的項用"-"來代表,Map裡的鍵值對用":"分隔。
---
name: John Smith
age: 41
gender: Male
spouse:
name: Jane Smith
age: 37
gender: Female
children:
- name: Jimmy Smith
age: 17
gender: Male
- name: Jenny Smith
age: 13
gender: Female
~
四、playbook核心元素
1)hosts :指定執行任務的遠端主機清單(主機清單定義的主機組或單個主機,支援前面的說的主機模式比對)
2)tasks :任務集
3)varniables :内置變量或自定義變量在playbook中調用
4)templates :模闆,可替換模闆檔案中的變量并實作一些簡單邏輯的檔案
5)handlers 和 notity結合使用,由特定條件出發的操作,滿足條件方才執行,否則不執行
6)tags标簽 :給指定的任務貼上标簽,我們在執行playbook的時候可以根據标簽選擇性的挑選部分代碼執行,如 ansible-playbook -t tagsname useradd.yml ,這條指令的意思就是在useradd.yml中挑選标簽名為tagsname的任務執行
五、playbook基礎元件
1)hosts:
playbook中的每一個play的目的都是為了讓特定主機以某個指定的使用者身份執行任務。hosts用于指定要執行任務的主機,須事先定義在主機清單中。hosts指定主機的形式同樣支援像主機清單中定義的那樣,支援通配,支援主機模式比對與或非,支援IP位址,當然也支援混合比對與或非。
示例:在websers組,但不再dbsers組,可以這樣定義hosts
---
- hosts: websers:!dbsers
2)remote_user:可用于host和task中,也可以通過指定其通過sudo的方式在遠端執行任務,其可用于play全局或某個任務;此外,甚至可以在sudo時使用用sudo_user指定sudo時切換的使用者,如下所示
---
- hosts: websers:!dbsers
remote_user: root
tasks:
- name: test connection
ping:
remote_user: qiuhom
sudo: yes
sudo_user: qiuping
說明:預設sudo 為root,上例指定了sudo_user 為qiuping,上述任務同sudo -u qiuping ping xxxx(代表某主機)指令一樣的意思,當然在使用sudo 時 我們還需要在目标主機上對qiuhom授權,要讓qiuhom這個使用者具有代表qiuping的權限去執行ping指令。
3)task清單和action:play的主體部分是task list,task list 中的各任務按次序逐個在hosts中指定的所有主機上執行,即在所有主機上完成第一個任務後,再開始第二個任務;task的目的是使用指定的參數執行子產品,而在子產品參數中可以使用變量,子產品執行時是幂等的,這意味着多次執行時是安全的,其結果均一緻;每個task都應該有其name,用于playbook的執行結果輸出,建議其内容能清晰地描述任務步驟,如未提供name,則action的結果将用于輸出。
tasks:任務清單,它有兩種格式如下
(1)action: module arguments
(2)module: arguments ##建議使用
注意:shell子產品和command子產品後面跟的是指令,而非key=value
如果某項任務的狀态在運作後為changed時,可通過“notify”通知給相應的handlers;當然任務可以通過“tags”打标簽,可以在ansible-playbook指令上使用-t指定進行指定其标簽名調用。
[qiuhom@test ~]$cat test.yml
---
- hosts: websers:!dbsers
remote_user: root
tasks:
- name: test connection
ping:
remote_user: qiuhom
sudo: yes
sudo_user: qiuping
tags: test
- name: test command
shell: /bin/ls /home/qiuhom/
[qiuhom@test ~]$ansible-playbook -t test test.yml
說明:用-t 指定标簽名,表示隻運作所指定标簽所在的任務,當然同名的标簽可以在多條任務中,一個任務也可以有多個标簽。
如果指令或腳本的退出碼不為零,可用使用如下方式忽略或跳過繼續執行以下代碼
---
- hosts: websers:!dbsers
remote_user: root
tasks:
- name: run this command and ignore the result
shell: /usr/sbin/ip addr show eth0 || /bin/true
- name: run this command and ignore the result
shell: /usr/sbin/ip addr show eth0
ignore_errors: True
說明:兩種方式都可以跳過出錯的指令而不打斷playbook,繼續執行以下的代碼,前者使用的短路或的特性,後者使用ignore_errors參數來控制
六、playbook運作的方式
ansible-playbook <filename.yml> ... [options]
常用選項:
-C , --check : 隻檢查可能會發生的改變,但不真正執行操作,相當于空跑一遍playbook,測試下是否和自己預想的結果一樣,但它不會真正的去遠端主機上執行。常用于測試寫的playbook文法是否有誤。
--list-hosts :列出playbook指定運作任務所比對的主機
--list-tags :列出playbook中所有标簽名稱清單
--list-tasks :列出playbook中所有任務名稱及标簽名稱
--limit 主機清單 :隻針對指定主機清單中的主機執行目前playbook(指定主機清單必須是在playbook裡定義的主機清單範圍内)
-v,-vv,-vvv : 顯示執行playbook的過程,-v,顯示較簡單,-vv顯示較詳細,-vvv顯示整個過程(非常詳細)
[root@test ~]#cat test.yml
---
- hosts: websers
remote_user: root
tasks:
- name: run this command
shell: hostname
tags: hostname
ignore_errors: True
- name: show ip addr
shell: /sbin/ip addr show
tags: showip
[root@test ~]#ansible-playbook test.yml --list-hosts
playbook: test.yml
play #1 (websers): websers TAGS: []
pattern: [u'websers']
hosts (2):
192.168.0.128
192.168.0.218
[root@test ~]#ansible-playbook test.yml --list-tags
playbook: test.yml
play #1 (websers): websers TAGS: []
TASK TAGS: [hostname, showip]
[root@test ~]#ansible-playbook test.yml --list-tasks
playbook: test.yml
play #1 (websers): websers TAGS: []
tasks:
run this command TAGS: [hostname]
show ip addr TAGS: [showip]
[root@test ~]#ansible-playbook test.yml --limit 192.168.0.218
PLAY [websers] ********************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************
ok: [192.168.0.218]
TASK [run this command] ***********************************************************************************************
changed: [192.168.0.218]
TASK [show ip addr] ***************************************************************************************************
changed: [192.168.0.218]
PLAY RECAP ************************************************************************************************************
192.168.0.218 : ok=3 changed=2 unreachable=0 failed=0
[root@test ~]#ansible-playbook test.yml --limit 192.168.0.218 -v
Using /etc/ansible/ansible.cfg as config file
PLAY [websers] ********************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************
ok: [192.168.0.218]
TASK [run this command] ***********************************************************************************************
changed: [192.168.0.218] => {"changed": true, "cmd": "hostname", "delta": "0:00:00.002139", "end": "2019-11-16 23:11:02.996962", "rc": 0, "start": "2019-11-16 23:11:02.994823", "stderr": "", "stderr_lines": [], "stdout": "localhost.localdomain", "stdout_lines": ["localhost.localdomain"]}
TASK [show ip addr] ***************************************************************************************************
changed: [192.168.0.218] => {"changed": true, "cmd": "/sbin/ip addr show", "delta": "0:00:00.002604", "end": "2019-11-16 23:11:03.733004", "rc": 0, "start": "2019-11-16 23:11:03.730400", "stderr": "", "stderr_lines": [], "stdout": "1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN \n link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n inet 127.0.0.1/8 scope host lo\n inet6 ::1/128 scope host \n valid_lft forever preferred_lft forever\n2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000\n link/ether 00:0c:29:e8:f6:7b brd ff:ff:ff:ff:ff:ff\n inet 192.168.0.218/24 brd 192.168.0.255 scope global eth0\n inet6 fe80::20c:29ff:fee8:f67b/64 scope link \n valid_lft forever preferred_lft forever\n3: pan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN \n link/ether d2:7a:38:cf:27:60 brd ff:ff:ff:ff:ff:ff", "stdout_lines": ["1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN ", " link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00", " inet 127.0.0.1/8 scope host lo", " inet6 ::1/128 scope host ", " valid_lft forever preferred_lft forever", "2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000", " link/ether 00:0c:29:e8:f6:7b brd ff:ff:ff:ff:ff:ff", " inet 192.168.0.218/24 brd 192.168.0.255 scope global eth0", " inet6 fe80::20c:29ff:fee8:f67b/64 scope link ", " valid_lft forever preferred_lft forever", "3: pan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN ", " link/ether d2:7a:38:cf:27:60 brd ff:ff:ff:ff:ff:ff"]}
PLAY RECAP ************************************************************************************************************
192.168.0.218 : ok=3 changed=2 unreachable=0 failed=0
[root@test ~]#
說明:--limit 所指定的主機必須是在playbook中所指定的主機範圍内。
七、playbook vs shell scripts
1)shell腳本如下:
#!/bin/bash
# 安裝Apache
yum install --quiet -y httpd
# 複制配置檔案
cp /tmp/httpd.conf /etc/httpd/conf/httpd.conf
cp/tmp/vhosts.conf /etc/httpd/conf.d/
# 啟動Apache,并設定開機啟動
service httpd start
chkconfig httpd on
2)playbook
---
- hosts: websers
remote_user: root
tasks:
- name: create apache group
group: name=apache gid=80 system=yes
- name: create apache user
user: name=apache uid=80 group=apache system=yes shell=/sbin/nologin home=/var/www/html
- name: install httpd
yum: name=httpd
- name: copy config file
copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/
- name: copy config 2 file
copy: src=/tmp/vhosts.conf dest=/etc/httpd/conf.d/
- name: start httpd service
service: name=httpd state=started enabled=yes
說明:兩者都是實作同樣的目的,很明顯playbook的優勢要比腳本的優勢多,playbook 可以針對很多台主機進行任務執行,而腳本隻可以在某一台主機上執行;腳本重複執行沒有幂等性,很有可能帶來很多錯誤,而playbook卻不會有這樣的苦惱。
作者:Linux-1874
出處:https://www.cnblogs.com/qiuhom-1874/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利.