【Ansible】
參考文檔:【http://www.ansible.com.cn/docs/intro.html】
和ansible類似的工具還有saltstack,puppet,sshpass等,都用於遠程(批量)地管理服務器資源。各種工具實現原理不同,像ansible的話就是基於SSH開發的,這就表示其無需安裝客戶端,在一台全新的服務器上線之后(只要其有sshd服務在運行)就可以直接加入被管理的集群了。
■ 安裝驗證
安裝ansible的話我還是用了yum install ansible,據說用pip也可以裝。對於其他一些的系統,可以選擇從源碼開始安裝。源碼安裝一年前自己那本筆記本上有,可參考。總的來說,ansible的安裝還要求許多系統的組件如pcre,zlib以及其開發包、還有就是很多python模塊如paramiko等。
安裝完成后先驗證一下。ansible --version可以看到版本信息。
ansible的主要配置文件在/etc/ansible中,修改其中的hosts文件,默認情況下應該全是注釋,在合適的地方比如第12行左右寫入一行127.0.0.1。這個hosts文件主要用來配置所有被控端,現在驗證性地把本地先配置進去。
● 關於驗證用戶方式
因為ansible基於SSH,所以不可避免的一個問題就是通過ansible遠程連接主機時如何驗證用戶?兩種方式,一個是密碼,一個是公鑰認證。
如果選擇通過密碼來認證,可以在/etc/ansible/hosts的相應主機配置項后面加上" ansible_ssh_user=xxx ansible_ssh_pass=yyy",此外通過密碼登錄時還要保證目標主機在當前使用ansible的用戶的known_hosts中,即要求其至少要登錄一次。添加known_hosts的方法可以是手動ssh登錄一下,然后添加公鑰。在/etc/ansible/ansible.cfg中配置host_key_checking = False也可以取消公鑰檢查步驟,但是這樣並不安全。
其實,使用ansible的時候大概都是期望不要在配置文件中寫入密碼來通過ssh驗證了,免密ssh驗證的方法是ssh-keygen和ssh-copy-id兩個命令一起用。默認配置時,ansible使用和當前用戶同名的用戶登錄目標主機,因此需要目標主機的此用戶的HOME/.ssh/authorized_key中有本機本用戶的公鑰。在此前提下,當我們用root用戶執行,且/root/.ssh/authorized_key中有了root的公鑰信息,此時執行
ansible all -m ping
來測試ansible的安裝情況,如果返回中沒有報錯,就表示OK了。
■ 關於hosts文件
/etc/ansible/hosts文件指出了ansible被控端的一些配置。除了上面的簡單配置,其實這個配置文件的形式可以非常豐富。比如上面說了在一個IP地址后面加上ansible_ssh_user和ansible_ssh_pass這兩個額外的配置項。另外還可以加上ansible_ssh_port(默認22),ansible_ssh_host(主機名,假如你想給目標IP的主機取一個別名,但是又不想下到系統層面的/etc/hosts中去改,就可以在這里取完別名后加上此參數指明IP即可),ansible_ssh_private_key_file(指定私鑰文件)還有ansible_connection,可選local,ssh或paramiko。一些其他額外配置選項如下:
ansible_sudo_pass sudo 密碼(這種方式並不安全,我們強烈建議使用 --ask-sudo-pass) ansible_sudo_exe (new in version 1.8) sudo 命令路徑(適用於1.8及以上版本) ansible_connection 與主機的連接類型.比如:local, ssh 或者 paramiko. Ansible 1.2 以前默認使用 paramiko.1.2 以后默認使用 'smart','smart' 方式會根據是否支持 ControlPersist, 來判斷'ssh' 方式是否可行. ansible_shell_type 目標系統的shell類型.默認情況下,命令的執行使用 'sh' 語法,可設置為 'csh' 或 'fish'. ansible_python_interpreter 目標主機的 python 路徑.適用於的情況: 系統中有多個 Python, 或者命令路徑不是"/usr/bin/python",比如 \*BSD, 或者 /usr/bin/python 不是 2.X 版本的 Python.我們不使用 "/usr/bin/env" 機制,因為這要求遠程用戶的路徑設置正確,且要求 "python" 可執行程序名不可為 python以外的名字(實際有可能名為python26). 與 ansible_python_interpreter 的工作方式相同,可設定如 ruby 或 perl 的路徑....
hosts文件的基本格式是ini格式的,ini中的每個section是一個服務器邏輯分組,每個option是一個服務器,然后option后面可以添加上面說的額外配置。如:
[localloop] 127.0.0.1 ansible_ssh_user=frank ansible_connection=paramiko
section無形中也對服務器做了分組,我們就可以基於分組 對一部分主機進行操作了。一台主機也可分屬於幾個不同的組。
hosts文件還支持多匹配的配置,比如下面這個配置:
[localloops] 127.0.0.[1:5] ansible_connection=paramiko ansible_ssh_user=root
指的是127.0.0.1到127.0.0.5所有IP都放在localloops組中。如果在這個配置下運行ansible all -m ping的話就會收到5條回應(雖然127.0.0.x都是指本地機)。
● 變量
對於批量管理系統,一個很重要的要素就是可以設置根據主機不同而不同的變量。在ansible中,變量被使用在playbook中以發起對主機不同而個性化的配置。變量的設置可以在hosts中進行。變量分成好幾個層級的變量,最小的是主機變量,跟在主機后面直接設置:
127.0.0.1 http_port=80 maxRequestPerChild=808
這兩個變量的作用域只有這個主機一台。由於對變量名沒有限制,所以前面說到的那些ansible的配置如果打錯字了也不會報錯,ansible會認為這是你指定的一個主機變量
另外還可以設置組變量。設置組變量需要新開一個section,名為[組名:vars],如:
[localloop:vars] http_port=8080
另外,組之間可以通過[組名:children]的方式來指出組之間的隸屬關系。比如
[localloop1] 127.0.0.[1:5] [localloop2] 127.0.0.[6:10] [localloops:children] localloop1 localloop2
如此之后,在父組中定義的變量將也會可以被子組使用
也可以把變量的定義在hosts文件外完成,這就是分文件定義主機和組變量。在/etc/ansible/下創建兩個目錄: host_vars和group_vars,這兩個組中再分別創建YAML格式的,對應主機or組名的文件。例如可以在/etc/ansible/group_vars中創建localloop文件,在/etc/ansible/host_vars中創建testhost,然后修改hosts配置如下:
-----/etc/ansible/hosts----- [localloop] testhost ansible_ssh_host=127.0.0.1 -----/etc/ansible/group_vars/localloop----- key1: value1 key2: vaule2 -----/etc/ansible/host_vars/testhost----- key3: value3
至於變量的用處,主要是在playbook中使用,這個后面細說。
■ 命令行調用API
配置完hosts之后,在命令行中我們可以調用一些命令來使用ansible如上面的ansible all -m ping。總的來說,命令行的ansible工具大體格式是這樣的:
ansible <pattern> -m <module> -a <arguments>
pattern是一個標識,規定出了一部分主機。比如可以寫all或者*,就是代表所有主機;寫組名,代表該組內的主機;寫主機名,就代表這一個主機。如果主機名和組名沖突則以先規定的那個為准,而且命令行中會給出警告信息,提示有重名發生。另外也可以直接寫域名或者IP(只要在hosts中有配置),甚至可以寫192.168.1.*這種格式的來匹配一批主機。
pattern中可以借助符號來規划簡單的組分邏輯,如
a:b a組和b組的並集
a:!b 屬於a但不屬於b的主機們
a:&b a組和b組的交集
甚至可以有a[0]這種下標的形式來取得a組中指定第幾個的主機,a[0-1]則是下標群,最萬能的正則表達式則需要在pattern最前面加上~,如~(www|db)\.example\.com。需要注意一旦用上正則,那么點都要反轉義了。
● 命令行使用時一些其他參數
使用命令行時如果想動態的指定遠程執行命令的用戶,可以加入-u參數;
-f命令控制命令執行的進程數。比如操控5個主機進行一個比較耗時的任務,那么-f 5說不定只要一個單位的時間就能搞定,而-f 1的話必然需要五個單位的時間才能搞定。完成任務后返回是及時的,有點像多線程模型,但這里是多進程的。另外ansible一定會按照-f指出的進程數fork出那么多個子進程,並不管是否真有那么多主機需要處理。這個參數的值默認是5,在ansible.cfg中有配置。
--sudo可以讓命令在遠端通過sudo執行,當然沒有在遠端主機上配置運行sudo的話會要求輸密碼,在ansible中默認不提供這種進一步的交互所以直接報錯。如果想要手動輸入密碼來繼續sudo任務,可以在運行命令時加上--ask-sudo-password參數。但是考慮到自動化的目的,相信這個參數是不太常用的。
-m參數是指出了使用的ansible模塊。ansible模塊這東西有的說說,這類先不細講。目前用過的模塊有ping, command, shell等。
-a參數就是argument,指出某個模塊要求的參數,通常-a后參數的值有一定格式要求。
需要注意的是,命令行執行命令適合於一些簡單的批量管理任務,面對更加復雜的配置,安裝等任務應該使用ansible-playbook,兩者之間的關系就像是linux命令行和shell腳本。至於playbook詳細后面說。
■ 關於配置
上面簡單提到過了/etc/ansible下的ansible.cfg配置文件是用來配置ansible主控端的一些參數,而/etc/ansible/hosts則是用來配置一些被控端主機信息的。下面詳細來看下這些配置文件。
首先是ansible.cfg,這是一個INI格式,#開頭行注釋的配置文件。在目前的ansible中,運行ansible時會依次加載 環境變量ANSIBLE_CONFIG,當前目錄的ansible.cfg,~/.ansible.cfg,/etc/ansible/ansible.cfg,針對同一個配置項以最先加載到的為准。
這些配置文件中,大多數配置都集中在名為defaults的section中,其中可以使用的option有
action_plugins action代碼被執行時運行的一些插件(ansible支持開發者自己寫插件使用,非常靈活),類似的還有callback_plugins,connection_plugins,filter_plugins,lookup_plugins,vars_plugins
ansible_managed
ask_pass 默認是False,當設置為True時,ansible執行會要求輸入遠端的密碼,即使配置了免密登錄。所以一般維持False
forks 進程數,默認為5
gathering 用來指出是否收集遠程主機的facts(一些被控端的變量),默認的值是implicit,每次都會收集,explicit表示不收集,smart表示掃描主機,對已經收集過的不收集
host_key_checking 之前說過了,是否檢驗被控端主機的公鑰,默認是True
inventory 被控端配置文件(它的官方名稱是inventory),默認是/etc/ansible/hosts
log_path 日志文件,默認/var/log/ansible.log
module_name 在不指出-m的參數時默認使用的模塊,默認值是command,建議可以改成shell
nocolor 默認為0,設置為1時ansible不會為不同狀態(成功or失敗or警告)的結果染不同顏色
nocow 運行playbook的時候默認輸出會有一頭字符畫的牛,設置為1可以關掉牛的顯示(哈哈哈哈哈哈哈)
pattern 當沒有給出pattern時的默認pattern,默認值是*即所有主機
poll_interval ansible中有異步任務,這個配置設置一個秒數,當執行開始后每隔這么多秒查詢一次任務狀態
remote_tmp 一些ansible任務涉及到文件的傳輸,而傳輸的第一目的地就是這個配置項
remote_user 遠端用什么用戶執行任務,默認是和主控端當前用戶名稱一致的用戶
timeout SSH連接超時時間
除了defaults這個section之外,還有一些section可以配置比如Paramiko Specific Settings,OpenSSH Specific Settings等等。由於這些都是比較高端的玩法,就不多介紹了。
【Ansible-playbook】
playbook的基本語法是YAML格式的。這個格式長成這樣:
--- - hosts: local
name: this is a test for playbook remote_user: root tasks: - name: test to ping ping:
-------------------運行后返回是-------------------
PLAY [this is a test for playbook] ************************************************************************************************************************
TASK [Gathering Facts] **************************************************************************************************************
ok: [testhost2]
ok: [testhost1]
ok: [testhost3]
TASK [test to ping] *****************************************************************************************************************
ok: [testhost3]
ok: [testhost2]
ok: [testhost1]
PLAY RECAP **************************************************************************************************************************
testhost1 : ok=2 changed=0 unreachable=0 failed=0
testhost2 : ok=2 changed=0 unreachable=0 failed=0
testhost3 : ok=2 changed=0 unreachable=0 failed=0
解釋一下,playbook有一個根節點,下面的第一級子節點被稱為一個個play。這里只有一個play。此play名為this is a test for playbook,其含義就是對主機組local,以root的身份進行名為test to ping的任務,具體內容是利用ping模塊,參數為空進行操作。返回內容中可以看到有gathering facts板塊,這個參看前面配置文件中說到的gathering項。關於facts的具體內容相信后面也會再講。另外需要注意的一點就是返回的內容並不是在各個遠端執行的stdout,而是一個狀態碼。比如我把模塊換成了shell: echo hello,返回內容不是hello而也是ok。所以在使用command或者shell模塊的時候通常會采用 xxxx || /bin/true的方式。
運行方式是ansible-playbook test.yaml,ansible-playbook這個命令也可以加很多和ansible一樣的參數,比如-f 10就是開10條進程來進行操作。上面說通過ansible獲取到的返回信息是有限的,如果想要獲取到更多的信息,可以加上參數--verbose。這樣返回的信息中就會包含一些其他的比如stdout,stderr等輸出的信息了。如果在正式運行playbook之前想查看本次playbook會對哪些主機進行操作的話可以加上參數--list-hosts來提前檢查一下。
其中比較中心的是tasks這個列表,tasks中包含了很多task。這些task會針對hosts字段定義的主機組中相應主機,從上到下依次進行task操作。每個task的本質是執行一個模塊(module),除了少數如ping一樣的模塊是不用參數,大多數模塊需要提供參數。參數中可以使用在配置文件、命令行參數等多個地方定義的變量。在playbook中調用變量的格式采用Jinja2的語法,比如:
--- - hosts: local tasks: - name: make dir {{ name }} command: mkdir {{ name }}
name變量可以定義是組變量,也可以是主機變量。可以察覺到當其是主機變量且各個主機配的name值不同的時候,返回內容會返回什么這個問題。經過實驗,返回的是第一個主機的變量值。
利用playbook執行模塊只做必要的改動,如果ansible檢測到模塊動作的目的在執行前就已經達到,那么就會放棄本次操作。可以說即使重復執行playbook也是安全的。而像對於shell,command這樣的命令執行模塊而言是個例外,重復執行playbook等於重復執行命令。為了避免這樣的情況發生,可以為參數中添加一個flag=creates來保證不重復操作。
這種特性用術語來說就是每一個task都具有“冪等性”。
■ 一個task的其他參數
上面的例子中說到了一個task中最主要的兩個字段,name(指出task名)以及<模塊名>(指出task的具體操作)。除了這兩個之外,task還可以寫一些其他的字段參數。
比如notify,notify下面跟着的每一條記錄被稱為handler。前面說過ansible在執行模塊動作時會檢測動作的目的是否已經達到,如果達到那么可以在task結束的時候做一些鈎子操作。設置這個鈎子操作的就是handlers了。需要注意的是,一個handler本身也是一個task,只不過它通過notify的形式在主task中被調用。由於handler本身就是task,所以說handler本身也具有冪等性,即不論主task通過notify調用了多少次同一個handler,在所有task結束之后這個handler都只被執行最多一次。handler的常用場景是在配置等變更后通過handler來重啟服務等。下面是一個帶有handler的task的例子:
tasks: - name: change configuration template: template/nginx.tpl.conf /etc/nginx/nginx.conf notify: - restart nginx handlers: - name: change configuration shell: /usr/sbin/nginx -s reload || /bin/true
上面的例子中task中有個template字段參數,template字段有點像saltstack里面的file.manage模塊中的template字段,其將指定的一個本地的模板文件中的變量具體化,然后將其傳送到目標主機上的指定位置。變量可以來自於之前所說的多種多樣的變量定義手段。
■ include(import)和roles
include(在較新的版本中已經提示include關鍵字不再鼓勵使用,應該使用更加確切的import_tasks和import_playbook)可以在一個playbook中引用進一些其他yaml文件的信息。這種引用可以在多個層面進行,比如在play這一層面進行引用那就是將其他文件中定義的play一並加入到當前文件中來執行。
如果引用是發生在task層面,那么就是在一個play中的tasks下面聲明引用,相對應的,被引用文件內容也是一個tasks集合。
此外引用還可以插入到handlers中去,但是由於handler就是一個task,考慮這一點的話其實handlers中進行include的本質和在tasks下面進行是類似的。下面給出例子
--- - name: this is a test for playbook hosts: local tasks: - name: test connection ping: - import_tasks: mytask.yml - import_playbook: playbook/myplaybook.yml
在tasks中引入了mytask.yml文件中定義的task,在整個playbook中引入了myplaybook.yml中定義的那些play。
另外還需要指出,當在task層面進行引入的時候,除了引入一個具體的yml文件,還可以引入一個帶有模板變量的文件,並且可以在引入之后定義變量的具體值(當然用其他手段定義的變量也都可以使用)。此時關鍵字不能寫import_tasks而應該寫include_tasks。這種引入模板的操作只有在task層面才能進行。
在了解了playbook的引入機制之后,接下來的重點就是要說明一下roles這個ansible里面的重要概念了。一個role把一部分的task,handler等ansible要素邏輯上抽象成一個集合,我們可以基於這個集合進行ansible操作。而在playbook中,我們只要指出哪些機子是什么role(role和主機間多對多關系),那個角色該執行的所有操作都會被執行上去。
那么如何定義一個role? 首先是在ansible.cfg中有一個配置項roles_path,這是一個用冒號隔開的多個路徑。每個路徑都被認為是roles路徑。若這些路徑下有名為testrole的目錄,則表示有名為testrole的一個role。假設我們在某一個roles目錄下的testrole目錄結構如下:
testrole ├── defaults ├── files ├── handlers ├── meta ├── tasks ├── templates └── vars
在playbook中指定role的方式如下:
--- - hosts: local roles: - testrole
指定之后,此play還可以補充一些其他內容,並且針對local這個主機組認可了以下行為:
若testrole/tasks/main.yml存在,則其中定義的tasks將被添加到play中執行
(main.yml中應該直接從task開始寫,連tasks都是不用寫的,也就是說類似- name:xxx\n shell: xxx。下面也都是類似,直接從被引用那個層級開始寫)
若testrole/handlers/main.yml存在,則其中定義的handlers將被添加到play中(被添加到的意思是在這個play的context中可以直接寫其name來使用,下同)
若testrole/vars/main.yml存在,則其中定義的變量將被添加到play中
若testrole/meta/main.yml存在,則其中定義的角色依賴將被添加到roles列表中
若testrole/files存在,其中的文件和腳本可以分別被指定模塊為copy和script類的task直接引用而不用加上路徑
若testrole/templates存在,其中的模板文件可以被template類task直接引用而不用加上路徑
若testrole/tasks存在,其中的文件可以被直接import_tasks等類型task直接引入,而不用加上路徑
執行roles相關操作的優先級是高於一般定義的tasks的,也就是說tasks會在roles全部完成之后再被執行。關於給roles具體化一些變量,以及role之間的依賴等內容先不說了。
■ 關於變量
ansible的變量使用jinja2的語法制成,支持了{{ var }}之類的變量語法以及變量相關的一些過濾器方法,同時也支持{% if %}{% for %}等jinja2語法規定的邏輯結構,一般而言這些邏輯結構更多的用在template模塊的模板中,而不是playbook本身中,playbook本身更多的是用了簡單的變量形式。小提醒一下,若變量在一個值的開頭,要用引號引起,要不然解析yaml的時候會以為我們在試圖聲明一個字典。
上面所說的所有變量,幾乎都是我們自定義的變量。其實ansible為我們准備了很多內置的變量,一部分跟主機相關的被稱為facts。
facts的值根據主機不同而不同,ansible會為我們自動收集這些數據。一台主機具體有哪些facts並且值是多少可以ansible <host> -m setup來查看。數據量挺大的需要注意。可以注意到,在默認配置gathering為implicit的時候是不收集facts的,所以就無法將facts的變量名應用在ansible中。如果設置成了其他兩個,那么在ansible執行之前一定會去gather一下facts。有時確認不需要用到facts,還想要加快運行速度的時候可以在相關play中加上gather_facts: no作為參數。
facts是一個JSON結構的數據,其根節點的KEY名是ansible_facts,實際使用時不用寫出這個根節點。比如{"ansible_facts": {"ansible_all_ipv4_addresses": ["xxx","yyy"]}}這個不完全的facts結構,如果在模板中想要獲取yyy的值,那么可以直接{{ ansible_all_ipv4_addresses[1] }}即可。
另一部分則是和ansible系統本身相關的變量,這些變量通常是一些預設的變量名比如groups, hostvars, group_names。這些變量名我們不能覆蓋,具體哪些內容可以試驗一下看看。只需要記得,這些變量都是和ansible本身相關,不隨某一次playbook的運行而變化。
除了在playbook運行之前定義變量,tasks被執行前gather一下facts之外,在tasks運行過程中也可以使用變量。這種操作被稱為注冊變量。注冊變量是指將某一個task的返回結果保存下來,並且供給給后續其他task使用。一般而言,這種機制常和條件判斷一起使用。
脫離開playbook本身,從ansible的命令行來看(不論是ansible還是ansible-playbook),都可以在后面加上參數--extra-vars來指定額外的變量。后面跟的值的格式可以是--extra-vars "var1=value1 var2=value2"或者JSON格式的 --extra-vars {"var1":"value1", "var2": "value2"}
現在來總結下可以定義變量的地方:
首先在inventory(也就是默認的/etc/ansible/hosts文件)中,可以定義變量,可以給單個主機定義(直接跟在一個主機行后面),也可以給一整個組定義(開一個新的section)
其次,在/etc/ansible/group_vars和/etc/ansible/host_vars下也可以定義變量。方法是創建和組/主機同名的文件,文件格式是yaml,里面定義變量。
再次,roles的vars目錄下的main.yml中也可以定義變量。
此外,ansible有內置的變量,分成facts(內置主機變量,隨着主機變化而變化)和其他一些和ansible相關,諸如groups,group_names的變量。
在playbook的運行過程中,還可以把某一個task的返回作為變量的值保存下來。
命令行加上--extra-vars也可以指定額外變量
當這些地方聲明的變量之間命名沖突了怎么辦,優先級是這樣的,命令行的--extra-vars最為優先,然后是inventory文件中定義的變量(包括/etc/ansible/host|group_vars),然后是play中定義的,roles中定義的變量等,然后是facts等系統內置變量。
■ 條件判斷和When語句
通過when語句進行條件判斷,並結合注冊變量,可以在play的執行過程中加入邏輯順序。比如下面這段:
tasks: - name: first step command: /bin/false register: result ignore_errors: True - name: do when failed command: /bin/dosomething when: result|failed - name: do when success command: /bin/dosomethingelse when: result|success - name: do when skipped command: /bin/other_something when: result|skipped
注意到,when語句的值中可以使用jinja2過濾器,將變量做一些處理后返回一個布爾值的判斷結果給when語句。當符合when語句規定的條件時,自然when語句所在的task就會被執行了。
ignore_errors參數指出,如果當前task失敗了是否忽略錯誤繼續往下執行其他task。
更加靈活的when語句的使用方法還有諸如在其他地方定義一個變量然后給when用。比如定義了var: True的話,在when: var的時候就可以執行task,還有when: var is not defined這種用法來判斷一個變量是否被定義。簡單的判斷相等,不等,大於小於也可以用when: xxx == "yyy",when: var|int <= 10之類的,就不再一一列舉了。
import的時候以及play在關聯role的時候,均可以使用when條件語句來進行邏輯判斷后再執行操作。
● 關於注冊變量
之前簡單提到了下注冊變量的內容,注冊變量實質是將某一個task的返回的對象(完整對象可以通過--verbose參數來查看,是一個JSON格式的東西)保存下來。所以有時候如果想要獲取到返回結果輸出到stdout的值,可以result.stdout來調用。下面就是一個分析stdout輸出的例子:
- name: test shell: cat /tmp/testfile register: result - name: test2 shell: echo "find word hello in testfile" when: result.stdout.find('hello') != -1 # find方法是來源於python的,python中對字符串的處理方法這里基本都可以用上
其他諸如stderr,stdout_lines都可以調用,詳細可以用上--verbose看返回的結果到底是怎樣一個JSON。
■ 循環結構
這里要說的循環不是基於JINJA2模板語法的循環,而是本身ansible提供的對於playbook邏輯的循環。試想這樣一些場景,比如你要在新服務器上一次性新增多個用戶,或者要通過yum一次性安裝好多包。當然你可以把每一個用戶/每一個包寫成一個task,但這樣會很麻煩。通過循環結構,只要控制每次的用戶名/包名不同,其他操作都是相同的,就可以更加輕松方便了:
- name: add many users user: name={{item.name}} state=present groups={{item.group}} with_items: - {"name": "Frank","group": "FrankG"} - {"name": "Takanashi", "group": "TakanashiG"}
with_items,其中item是前面循環每個單元的代號,是固定的,事實上下面所有循環的方法的循環體內單元的代號都是item。像這個例子給出的是對一個字典的列表的循環,而一般情況下也可以單純的對一個列表循環。
如果指出是with_nested,那么在下面可以指出多個列表,進行笛卡爾積式的作業。比如:
- name: test shell: echo "{{item[0]}}: {{item[1]}}" with_nested: - ['a','b'] - ['1','2','3']
這個task最終的輸出是在每台機子上分別都輸出了a:1,a:2,a:3,b:1,b:2,b:3。(實驗了下,with_nested出來的result不是一個和其他簡單輸出一樣的帶有stdout什么的結構,而是有一個字段名為results的列表)
除了with_items和with_nestsed,ansible中還有許多這種內置定義了的循環關鍵字:
with_fileglob 以非遍歷的方式循環一個指定目錄下的文件名,通常可以和template,copy等模塊相結合
with_subelements 對於一些層級比較復雜的數據結構來說,可以直接循環遍歷其指定名字的一個子字段
with_sequence 類似於range函數,可以指定start,end,stride,format等,通過它來生成一個有數字規律的字符串列表供playbook使用如with_sequences: start=0 end=10 stride=2 format=var%d
with_random_choice 下可跟若干個字符串,隨機選中一個
■ 其他一些playbook的特性
● 加速模式
對於較老版本的ansible用戶,以及還在使用比較老的系統版本主機的用戶,ansible提供了所謂的加速模式可以在操作上加快很多。使用方法就是在play中指出accelerate: true。
● 異步和輪詢
在默認情況下,ansible會在操作過程中一直保持SSH連接,當操作任務耗時較長,並且操作主機較多時維護這樣的連接顯然有些成本過高。所以ansible也提供了異步的模式。在異步模式下,操作請求發出之后每隔一個輪詢時間去查詢一次,當查詢到操作結果則返回,並且設置有一個超時時間。
使用方法是在task中指出ansyc: nn,nn是超時時間的指定。同時也可通過poll: n來指出輪詢間隔時間。若不設置輪詢時間默認是10秒。
● 檢查
對於某個playbook會影響哪些主機,之前說過只要加上--list-hosts參數即可。
那么對於其到底做哪些變化,可以用--check參數來檢查。同時還可以加上--diff參數,這樣還可以看到本次playbook對目標主機上的文本會做出什么變化。在--check模式下的所有變更都不是真實的變更,而只是做一個預覽,比較方便。
● 錯誤處理
前面說過了,當設置一個task的ignore_error為true或者yes的時候,這個task即使出錯也會跳過它繼續執行下面的幾個task。與之相對的,也可以手動拋出錯誤。方法如下:
fail: msg="the task is failed"
failed_when: "'ERROR' in res.stderr" 這個是有條件的發起失敗。當然,也可以通過fail+when兩個子句來實現。
更多的一些關於模塊的內容我打算另開一篇寫寫