Playbooks與Ad-Hoc相比,是一種徹底不一樣的運用Ansible的方式,並且是很是之強大的;也是系統ansible命令的集合,其利用yaml語言編寫,運行過程,ansbile-playbook命令根據自上而下的順序依次執行。
簡單來講,Playbooks 是一種簡單的配置管理系統與多機器部署系統的基礎。與現有的其餘系統有不一樣之處,且很是適合於複雜應用的部署。html
同時,Playbooks開創了不少特性,它能夠容許你傳輸某個命令的狀態到後面的指令,如你能夠從一臺機器的文件中抓取內容並附爲變量,而後在另外一臺機器中使用,這使得你能夠實現一些複雜的部署機制,這是ansible命令沒法實現的。python
Playbooks可用於聲明配置,更強大的地方在於,在Playbooks中能夠編排有序的執行過程,甚至於作到在多組機器間,來回有序的執行特別指定的步驟。而且能夠同步或異步的發起任務。mysql
咱們使用Ad-Hoc時,主要是使用 /usr/bin/ansible 程序執行任務.而使用Playbooks時,更可能是將之放入源碼控制之中,用之推送你的配置或是用於確認你的遠程系統的配置是否符合配置規範。linux
在如右的連接中:ansible-examples repository,有一些整套的Playbooks,它們闡明瞭上述的這些技巧。git
playbooks 的格式是yaml,語法作到最小化,意在避免 playbooks 成爲一種編程語言或是腳本,但它也並非一個配置模型或過程的模型。github
playbook是由一個或多個「play」組成的列表。play的主要功能在於將事先歸併爲一組的主機裝扮成事先經過Ansible中的tasks定義好的角色(play的內容被稱爲tasks,即任務)。從根本上來說所謂tasks無非是調用Ansible的一個module。將多個「play」組織在一個playbook中便可以讓它們聯同起來按事先編排的機制一同工做。web
「plays」算是一個類比,能夠經過多個plays告訴系統作不一樣的事情,不只是定義一種特定的狀態或模型。也能夠在不一樣時間運行不一樣的plays。正則表達式
對於初學者,這裏有一個playbook,其中僅包含一個play:sql
--- - hosts: webservers vars: http_port: 80 max_clients: 200 remote_user: root tasks: - name: ensure apache is at the latest version yum: pkg=httpd state=latest - name: write the apache config file template: src=/srv/httpd.j2 dest=/etc/httpd.conf notify: - restart apache - name: ensure apache is running service: name=httpd state=started handlers: - name: restart apache service: name=httpd state=restarted
第一行中,文件開頭爲 ---;這是YAML將文件解釋爲正確的文檔的要求。YAML容許多個「文檔」存在於一個文件中,每一個「文檔」由 --- 符號分割,但Ansible只須要一個文件存在一個文檔便可,所以這裏須要存在於文件的開始行第一行。shell
YAML對空格很是敏感,並使用空格來將不一樣的信息分組在一塊兒,在整個文件中應該只使用空格而不使用製表符,而且必須使用一致的間距,才能正確讀取文件。相同縮進級別的項目被視爲同級元素。
以 - 開頭的項目被視爲列表項目。做爲散列或字典操做,它具備key:value格式的項。YAML文檔基本上定義了一個分層的樹結構,其中位於左側是包含的元素。YAML文件擴展名一般爲.yaml或者.yml。
接下來,咱們將分別講解 playbook 語言的多個特性
Playbook主要有如下四部分構成:
而Playbook對應的目錄層有五個,分別以下:
通常所需的目錄層有:(視狀況可變化)
接下來,咱們將分別介紹構成playbook的四層結構。
咱們能夠爲playbook中的每個play,個別的選擇操做的目標機器是哪些,以哪一個用戶身份去完成要執行的步驟(called tasks)
--- - hosts: webservers remote_user: root tasks: - name: test connection remote_user: yourname sudo: yes
playbook中的每個play的目的都是爲了讓某個或某些主機以某個指定的用戶身份執行任務。
hosts:用於指定要執行指定任務的主機其能夠是一個或多個,由逗號爲分隔符分隔主機組
remote_user:用於指定遠程主機上的執行任務的用戶。不過remote_user也可用於各tasks中。也能夠經過指定其經過sudo的方式在遠程主機上執行任務其可用於play全局或某任務。此外甚至能夠在sudo時使用sudo_user指定sudo時切換的用戶
user:與remote_user相同
sudo:若是設置爲yes,執行該任務組的用戶在執行任務的時候,獲取root權限
sudo_user:若是設置user爲bob,sudo爲yes,sudo_user爲tom時,則bob用戶在執行任務時會得到tom用戶的權限
connection:經過什麼方式鏈接到遠程主機,默認爲ssh
gather_facts:除非明確說明不須要在遠程主機上執行setup模塊,不然默認自動執行。若是確實不須要setup模塊傳遞過來的變量,則能夠將該選項設置爲False
說明:
每個play包含了一個tasks列表(任務列表)。
任務列表中的各任務按次序逐個在hosts中指定的全部主機上執行即在全部主機上完成第一個任務後再開始第二個。在自上而下運行某playbook時若是中途發生錯誤,全部已執行任務都將回滾,所以在更正playbook後從新執行便可。
每個tasks必須有一個名稱name,這樣在運行playbook時,從其輸出的任務執行信息中能夠很好的辨別出是屬於哪個tasks的。若是沒有定義name,「action」的值將會用做輸出信息中標記特定的tasks。
tasks的目的是使用指定的參數執行模塊,而在模塊參數中可使用變量。模塊執行是冪等的,這意味着屢次執行是安全的,由於其結果均一致。每一個tassk都應該有其name用於playbook的執行結果輸出,建議其內容儘量清晰地描述任務執行步驟。若是未提供name則action的結果將用於輸出。
若是要聲明一個tasks,之前有一種格式:「action: module options」(可能在一些老的playbooks中還能見到)。如今推薦使用更常見的格式:「module: options」。
下面是一種基本的taska的定義,service moudle使用key=value格式的參數,這也是大多數module使用的參數格式:
tasks: - name: make sure apache is running service: name=httpd state=running # 在衆多模塊中,只有command和shell模塊僅須要給定一個列表而無需使用「key=value」格式以下: tasks: - name: disable selinux command: /sbin/setenforce 0 # 使用command module和shell module時,咱們須要關心返回碼信息,若是有一條命令,它的成功執行的返回碼不是0,咱們能夠這樣作: tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand || /bin/true # 或者使用ignore_errors來忽略錯誤信息 tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand ignore_errors: True # 若是action行看起來太長,可使用space(空格)或者indent(縮進)隔開連續的一行: tasks: - name: Copy ansible inventory file to client copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts owner=root group=root mode=0644
上面咱們曾提到過,module具備「冪等」性,因此當遠程主機被人改動時,能夠重放playbooks達到恢復的目的。playbooks自己能夠識別這種改動,而且有一個基本的event system(事件系統),能夠響應這種改動。
(當發生改動時)「notify」這個actions會在playbook的每個tasks結束時被觸發,並且即便有多個不一樣的tasks通知改動的發生,「notify」 actions只會被觸發一次。這樣能夠避免屢次有改變發生時每次都執行指定的操做,取而代之僅在全部的變化發生完成後一次性地執行指定操做。
在「notify」中列出的操做稱爲handlers,即「notify」中調用handlers中定義的操做。
說明:
在「notify」中定義內容必定要和tasks中定義的 - name 內容同樣,這樣才能達到觸發的效果,不然會不生效。
舉例來講,好比多個resources指出由於一個配置文件被改動,因此apache須要從新啓動,可是從新啓動的操做只會被執行一次。
這裏有一個例子,當一個文件的內容被改動時,重啓兩個services:
- name: template configuration file template: src=template.j2 dest=/etc/foo.conf notify: - restart memcached - restart apache
「notify」下列出的便是 handlers。
handlers也是一些tasks的列表,經過名字來引用,它們和通常的tasks並無什麼區別。handlers是由通知者進行notify,若是沒有被notify,handlers不會執行。無論有多少個通知者進行了notify,等到play中的全部tasks執行完成以後,handlers也只會被執行一次。
這裏是一個 handlers 的示例:
handlers: - name: restart memcached service: name=memcached state=restarted - name: restart apache service: name=apache state=restarted
Handlers最佳的應用場景是用來重啓服務,或者觸發系統重啓操做。除此之外不多用到了。
說明:handlers會按照聲明的順序執行。
tags用於讓用戶選擇運行或略過playbook中的部分代碼。ansible具備冪等性,所以會自動跳過沒有變化的部分,即使如此,有些代碼爲測試其確實沒有發生變化的時間依然會很是的長。此時若是確信其沒有變化就能夠經過tags跳過這些代碼片段。
經過playbook添加用戶示例
(1)給遠程主機添加用戶test
[root@Ansible ~]# vim user.yml --- - name: create user hosts: all user: root gather_facts: False vars: - user: test tasks: - name: create user user: name={{ user }}
上面的playbook 實現的功能是新增一個用戶:
name:對該playbook實現的功能作一個概述,後面執行過程當中,會打印name變量的值
hosts:指定了對哪些主機進行參做
user:指定了使用什麼用戶登陸遠程主機操做
gather_facts:指定了在如下任務部分執行前,是否先執行setup模塊獲取主機相關信息,這在後面的task會使用到setup獲取的信息時用到
vars:指定了變量,這裏指定了一個user變量,其值爲test,須要注意的是,變量值必定要用引號引發來
tasks:指定了一個任務,其下面的name參數一樣是對任務的描述,在執行過程當中會打印出來。user提定了調用user模塊,name是user模塊裏的一個參數,而增長的用戶名字調用了上面user變量的值
查看執行結果以下:
[root@Ansible ~]# ansible-playbook user.yml
(2)刪除遠程主機test的帳號
只需user: name="{{ user }}" state=absent remove=yes 便可
[root@Ansible ~]# vim user.yml --- - name: create user hosts: all user: root gather_facts: False vars: - user: test tasks: - name: create user user: name={{ user }} state=absent remove=yes [root@Ansible ~]# ansible all -m command -a "id test"
(3)經過playbook 安裝apache
安裝 httpd 服務,將本地準備好的配置文件 copy 過去,而且啓動服務
[root@Ansible ~]# vim apache.yml --- - hosts: all remote_user: root gather_facts: False tasks: - name: install apache on CentOS 7 yum: name=httpd state=present - name: copy httpd conf copy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf - name: start apache service service: name=httpd state=started [root@Ansible ~]# ansible-playbook apache.yml [root@Ansible ~]# ansible all -m shell -a "lsof -i:80"
(4)經過playbook 安裝apache(修改端口,並帶有vars變量)
將httpd.conf監聽的端口改成8080,而後從新覆蓋配置文件,當這個配置文件發生改變時,就觸發handler進行服務重啓
notify 這個 action可用於在每一個play的最後被觸發,這樣能夠避免屢次有改變發生時每次都執行指定的操做,notify中列出的操做稱爲handler
[root@Ansible ~]# vim apache.yml --- - hosts: all remote_user: root gather_facts: False vars: src_http_dir: /etc/httpd/conf dest_http_dir: /etc/httpd/conf tasks: - name: install apache on CentOS 7 yum: name=httpd state=present - name: copy httpd conf copy: src={{ src_http_dir }}/httpd.conf dest={{ dest_http_dir }}/httpd.conf notify: - systemctl restart httpd - name: start apache service service: name=httpd state=restarted handlers: - name: restart apache service: name=httpd state=restarted [root@Ansible ~]# vim /etc/httpd/conf/httpd.conf 42 Listen 80 --》 修改成8080 [root@Ansible ~]# ansible-playbook apache.yml [root@Ansible ~]# ansible all -m shell -a "lsof -i:8080"
Playbook的模塊與在Ansible命令行下使用的模塊有一些不一樣。這主要是由於在playbook中會使用到一些facts變量和一些經過setup模塊從遠程主機上獲取到的變量。有些模塊無法在命令行下運行,就是由於它們須要這些變量。並且即便那些能夠在命令行下工做的模塊也能夠經過playbook的模塊獲取一些更高級的功能。
具體模塊可參考官網(http://docs.ansible.com/ansible/latest/list_of_all_modules.html)。
這裏從官方分類的模塊裏選擇最經常使用的一些模塊進行介紹。
(1)簡介
(2)參數
attributes(2.3後新增):文件或目錄的屬性
backup:若是原目標文件存在,則先備份目標文件
block_end_string(2.4後新增):標記塊結束的字符串
block_start_string(2.4後新增):標記塊的開始的字符串
dest:目標文件路徑
follow(2.4後新增):是否遵循目標中的文件連接
force:是否強制覆蓋,默認爲yes
group:目標文件或目錄的所屬組
owner:目標文件或目錄的所屬主
mode:目標文件的權限。對於那些習慣於/usr/bin/chmod的記住,模式其實是八進制數字(如0644or 01777)。離開前導零可能會有意想不到的結果。從版本1.8開始,能夠將模式指定爲符號模式(例如u+rwx或u=rw,g=r,o=r)
newline_sequence(2.4後新增):指定用於模板文件的換行符序列(\n、\r、\r\n)
src:源模板文件路徑
trim_blocks(2.4後新增):若是這設置爲True,則刪除塊後的第一個換行符
validate:在複製以前經過命令驗證目標文件,若是驗證經過則複製
variable_end_string(2.4後新增):標記打印語句結束的字符串
variable_start_string(2.4後新增):標記打印語句開頭的字符串
(3)示例
# 官方簡單示例 - template: src=/mytemplates/foo.j2 dest=/etc/file.conf owner=bin group=wheel mode=0644 - template: src=/mytemplates/foo.j2 dest=/etc/file.conf owner=bin group=wheel mode="u=rw,g=r,o=r" - template: src=/mine/sudoers dest=/etc/sudoers validate='visudo -cf %s' playbook的引用該模板配置文件的方法示例: - name: Setup BIND host: all tasks: - name: configure BIND template: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf owner=root group=root mode=0644 # 或者是這樣 - template: src: /etc/httpd/conf/httpd.conf dest: /etc/file.conf owner: root group: root mode: 0644 # 建立DOS樣式文本文件 - template: src: config.ini.j2 dest: /share/windows/config.ini newline_sequence: '\r\n'
(1)簡介
(2)參數
cacheable(2.4後新增):可緩存
key_value:該set_fact模塊將key=value做爲變量,設置在劇本範圍中
(3)示例
配置mysql innodb buffer size的示例
[root@Ansible ~]# echo "# Configure the buffer pool" >> /etc/my.cnf [root@Ansible ~]# echo "innodb_buffer_pool_size = {{ innodb_buffer_pool_size_mb|int }}M" >> /etc/my.cnf [root@Ansible ~]# vim set_fact.yml --- - name: Configure Mariadb hosts: all tasks: - name: install Mariadb yum: name=mariadb-server state=installed - name: Calculate InnoDB buffer pool size set_fact: innodb_buffer_pool_size_mb={{ ansible_memtotal_mb / 2 }} - name: Configure Mariadb template: src=/etc/my.cnf dest=/etc/my.cnf owner=root group=root mode=0644 - name: Start Mariadb service: name=mariadb state=started enabled=yes handlers: - name: restart Mariadb service: name=mariadb state=restarted [root@Ansible ~]# ansible-playbook set_fact.yml
在遠程主機上查看該配置
[root@Client ~]# vim /etc/my.cnf
(1)簡介
(2)參數
echo(2.5後增長):控制鍵入時是否顯示鍵盤輸入。若是設置了「秒」或「分鐘」,則不起做用
minutes:暫停多少分鐘
seconds:暫停多少秒
prompt:打印一串信息提示用戶操做
(3)示例
# 暫停5分鐘以創建應用程序緩存。 - pause: minutes: 5 # 有用的提醒,提醒您注意更新後的內容。 - pause: prompt: "Make sure org.foo.FooOverload exception is not present" # 暫停之後得到一些信息。 - pause: prompt: "Enter a secret" echo: no
(1)簡介
(2)參數
active_connection_states(2.3後新增) :被計爲活動鏈接的TCP鏈接狀態列表
connect_timeout:在下一個任務執行以前等待鏈接的超時時間
delay:等待一個端口或者文件或者鏈接到指定的狀態時,默認超時時間爲300秒,在這等待的300s的時間裏,wait_for模塊會一直輪詢指定的對象是否到達指定的狀態,delay即爲多長時間輪詢一次狀態
exclude_hosts(1.8後新增):在查找狀態的活動TCP鏈接時要忽略的主機或IP的列表drained
host:wait_for模塊等待的主機的地址,默認爲127.0.0.1
msg(2.4後新增):這會覆蓋正常的錯誤消息,使其不符合所需的條件
port:wait_for模塊等待的主機的端口
path:文件路徑,只有當這個文件存在時,下一任務纔開始執行,即等待該文件建立完成
search_regex(1.4後新增):能夠用來匹配文件或套接字鏈接中的字符串。默認爲多行正則表達式
sleep(2.3後新增):檢查之間睡眠的秒數,在2.3以前,這被硬編碼爲1秒
state:等待的狀態,即等待的文件或端口或者鏈接狀態達到指定的狀態時,下一個任務開始執行。當等的對象爲端口時,狀態有started,stoped,即端口已經監聽或者端口已經關閉;當等待的對象爲文件時,狀態有present或者started,absent,即文件已建立或者刪除;當等待的對象爲一個鏈接時,狀態有drained,即鏈接已創建。默認爲started
timeout:wait_for的等待的超時時間,默認爲300秒
(3)示例
- name: create task hosts: all tasks: # 等待8080端口已正常監聽,纔開始下一個任務,直到超時 - wait_for: port=8080 state=started # 等待8000端口正常監聽,每隔10s檢查一次,直至等待超時 - wait_for: port=8081 delay=10 # 等待8000端口直至有鏈接創建 - wait_for: host=0.0.0.0 port=8000 delay=10 state=drained # 等待8000端口有鏈接創建,若是鏈接來自192.168.8.66或者192.168.8.8,則忽略 - wait_for: host=0.0.0.0 port=8000 state=drained exclude_hosts=192.168.8.66,192.168.8.8 # 等待/tmp/foo文件已建立 - wait_for: path=/tmp/foo # 等待/tmp/foo文件已建立,並且該文件中須要包含completed字符串 - wait_for: path=/tmp/foo search_regex=completed # 等待/var/lock/file.lock被刪除 - wait_for: path=/var/lock/file.lock state=absent # 等待指定的進程被銷燬 - wait_for: path=/proc/3466/status state=absent # 等待openssh啓動,10s檢查一次 - local_action: wait_for port=22 host="{{ ansible_ssh_host | default(inventory_hostname) }}" search_regex=OpenSSH delay=10
(1)簡介
(2)參數
attributes(2.3後新增):文件或目錄應的屬性
backup:建立一個備份文件(若是yes),包括時間戳信息
decrypt(2.4後新增):控制使用保管庫對源文件進行自動解密
delimiter(1.4後新增):分隔文件內容的分隔符
dest:使用全部源文件的鏈接建立的文件,合併後的大文件路徑
group:合併後的大文件的所屬組
owner:合併後的大文件的所屬主
ignore_hidden(2.0後新增):組裝時,是否忽略隱藏文件,默認爲no
mode:合併後的大文件的權限。對於那些習慣於/usr/bin/chmod的記住,模式其實是八進制數字(如0644or 01777)。離開前導零可能會有意想不到的結果。從版本1.8開始,能夠將模式指定爲符號模式(例如u+rwx或u=rw,g=r,o=r)
regexp:在regex匹配文件名時彙編文件。若是未設置,則全部文件都被組合。全部「」(反斜槓)必須轉義爲「」才能符合yaml語法
remote_src(1.4後新增):若是爲False,它將在本地主機上搜索src,若是爲True,它將轉到src的遠程主機。默認值爲True
src:源文件(即零散文件)的路徑
validate(2.0後新增):與template的validate相同,指定命令驗證文件
(3)示例
# 指定源文件(即零散文件)的路徑,合併一個目標文件someapp.conf - assemble: src: /etc/someapp/fragments dest: /etc/someapp/someapp.conf # 指定一個分隔符,將在每一個片斷之間插入 - assemble: src: /etc/someapp/fragments dest: /etc/someapp/someapp.conf delimiter: '### START FRAGMENT ###' # 在SSHD傳遞驗證後,將新的「sshd_config」文件複製到目標地址 - assemble: src: /etc/ssh/conf.d/ dest: /etc/ssh/sshd_config validate: '/usr/sbin/sshd -t -f %s'
一、簡介
二、參數
groups:要添加主機至指定的組,以逗號分隔
name:要添加的主機名或IP地址,包含冒號和端口號
三、示例
# 添加主機到webservers組中,主機的變量foo的值爲42 - name: add host to group 'just_created' with variable foo=42 add_host: name: "{{ ip_from_ec2 }}" groups: just_created foo: 42 # 將主機添加到多個組 - name: add host to multiple groups add_host: hostname: "{{ new_ip }}" groups: - group1 - group2 # 向主機添加一個非本地端口的主機 - name: add a host with a non-standard port local to your machines add_host: name: "{{ new_ip }}:{{ new_port }}" # 添加一個經過隧道到達的主機別名(Ansible <= 1.9) - name: add a host alias that we reach through a tunnel (Ansible <= 1.9) add_host: hostname: "{{ new_ip }}" ansible_ssh_host: "{{ inventory_hostname }}" ansible_ssh_port: "{{ new_port }}" # 添加一個經過隧道到達的主機別名(Ansible >= 2.0) - name: add a host alias that we reach through a tunnel (Ansible >= 2.0) add_host: hostname: "{{ new_ip }}" ansible_host: "{{ inventory_hostname }}" ansible_port: "{{ new_port }}"
(1)簡介
(2)參數
key:其值將被用做組的變量
parents(2.4後新增):父組的列表
(3)示例
# 建立主機組 - group_by: key: machine_{{ ansible_machine }} # 建立相似「kvm-host」的主機組 - group_by: key: virt_{{ ansible_virtualization_type }}_{{ ansible_virtualization_role }} # 建立嵌套主機組 - group_by: key: el{{ ansible_distribution_major_version }}-{{ ansible_architecture }} parents: - el{{ ansible_distribution_major_version }}
(1)簡介
(2)參數
msg:調試輸出的消息,打印自定義消息
var:將某個任務執行的輸出做爲變量傳遞給debug模塊,debug會直接將其打印輸出
verbosity(2.1後新增):debug的運行調試級別
(3)示例
# 爲每一個主機打印IP地址和網關 - debug: msg: "System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}" - debug: msg: "System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}" when: ansible_default_ipv4.gateway is defined - shell: /usr/bin/uptime register: result - debug: var: result # 直接將上一條指令的結果做爲變量傳遞給var,由debug打印出result的值 verbosity: 2 - name: Display all variables/facts known for a host debug: var: hostvars[inventory_hostname] verbosity: 4
(1)簡介
(2)參數
msg:終止前打印出信息,用於執行失敗的自定義消息
(3)示例
# 執行失敗錢打印出自定義信息 - fail: msg: "The system may not be provisioned according to the CMDB status." when: cmdb_status != "to-be-staged"
在使用Ansible作自動化運維的時候,免不了的要重複執行某些操做,如:添加幾個用戶,建立幾個MySQL用戶併爲之賦予權限,操做某個目錄下全部文件等等。好在playbook支持循環語句,可使得某些需求很容易並且很規範的實現。
如下主要來自他人分享。
with_items是playbooks中最基本也是最經常使用的循環語句。
示例:
tasks: - name:Secure config files file: path=/etc/{{ item }} mode=0600 owner=root group=root with_items: - my.cnf - shadow - fstab # 或 with_items:"{{ somelist }}"
上面的例子說明在/etc下建立權限級別爲0600,屬主屬組都是root三個文件,分別爲my.cnf、shadow、fstab
使用with_items迭代循環的變量能夠是個單純的列表,也能夠是一個較爲複雜 的數據結果,如字典類型:
tasks: - name: add several users user: name={{ item.name }} state=present groups={{ item.groups }} with_items: - { name: 'testuser1', groups: 'wheel' } - { name: 'testuser2', groups: 'root' }
示例:
tasks: - name: give users access to multiple databases mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo with_nested: - [ 'alice', 'bob' ] - [ 'clientdb', 'employeedb', 'providerdb' ]
item[0]是循環的第一個列表的值['alice','bob']。
item[1]是第二個列表的值。表示循環建立alice和bob兩個用戶,而且爲其賦予在三個數據庫上的全部權限。
也能夠將用戶列表事先賦值給一個變量:
tasks: - name: here, 'users' contains the above list of employees mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo with_nested: - "{{users}}" - [ 'clientdb', 'employeedb', 'providerdb' ]
with_dict能夠遍歷更復雜的數據結構:
假若有以下變量內容:
users: alice: name: Alice Appleworth telephone: 123-456-7890 bob: name: Bob Bananarama telephone: 987-654-3210
如今須要輸出每一個用戶的用戶名和手機號:
tasks: - name: Print phone records debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})" with_dict: "{{ users }}"
能夠指定一個目錄,使用with_fileglob能夠循環這個目錄中的全部文件,示例以下:
tasks: - name:Make key directory file: path=/root/.sshkeys ensure=directory mode=0700 owner=root group=root - name:Upload public keys copy: src={{ item }} dest=/root/.sshkeys mode=0600 owner=root group=root with_fileglob: - keys/*.pub - name:Assemble keys into authorized_keys file assemble: src=/root/.sshkeys dest=/root/.ssh/authorized_keysmode=0600 owner=root group=root
假如如今須要遍歷一個用戶列表,並建立每一個用戶,並且還須要爲每一個用戶配置以特定的SSH key登陸。變量文件內容以下:
users: - name: alice authorized: - /tmp/alice/onekey.pub - /tmp/alice/twokey.pub mysql: password: mysql-password hosts: - "%" - "127.0.0.1" - "::1" - "localhost" privs: - "*.*:SELECT" - "DB1.*:ALL" - name: bob authorized: - /tmp/bob/id_rsa.pub mysql: password: other-mysql-password hosts: - "db1" privs: - "*.*:SELECT" - "DB2.*:ALL" # playbook中定義以下: - user: name={{ item.name }} state=present generate_ssh_key=yes with_items: "`users`" - authorized_key: "user={{ item.0.name }} key='{{ lookup('file', item.1) }}'" with_subelements: - users - authorized # 也能夠遍歷嵌套的子列表: - name: Setup MySQL users mysql_user: name={{ item.0.name }} password={{ item.0.mysql.password }} host={{ item.1 }} priv={{ item.0.mysql.privs | join('/') }} with_subelements: - users - mysql.hosts
with_sequence能夠生成一個自增的整數序列,能夠指定起始值和結束值,也能夠指定增加步長。 參數以key=value的形式指定,format指定輸出的格式。數字能夠是十進制、十六進制、八進制:
- hosts: all tasks: # create groups - group: name=evens state=present - group: name=odds state=present # create some test users - user: name={{ item }} state=present groups=evens with_sequence: start=0 end=32 format=testuser%02d # create a series of directories with even numbers for some reason - file: dest=/var/stuff/{{ item }} state=directory with_sequence: start=4 end=16 stride=2 # stride用於指定步長 # a simpler way to use the sequence plugin # create 4 groups - group: name=group{{ item }} state=present with_sequence: count=4
從列表中隨機取一個值:
- debug: msg={{ item }} with_random_choice: - "go through the door" - "drink from the goblet" - "press the red button" - "do nothing"
示例:
- action: shell /usr/bin/foo register: result until: result.stdout.find("all systems go") != -1 retries: 5 delay: 10
重複執行shell模塊,當shell模塊執行的命令輸出內容包含"all systems go"的時候中止。重試5次,延遲時間10秒。retries默認值爲3,delay默認值爲5。任務的返回值爲最後一次循環的返回結果。
在循環中使用register時,保存的結果中包含results關鍵字,該關鍵字保存模塊執行結果的列表。
- shell: echo "{{ item }}" with_items: - one - two register: echo
變量echo內容以下:
{ "changed": true, "msg": "All items completed", "results": [ { "changed": true, "cmd": "echo \"one\" ", "delta": "0:00:00.003110", "end": "2013-12-19 12:00:05.187153", "invocation": { "module_args": "echo \"one\"", "module_name": "shell" }, "item": "one", "rc": 0, "start": "2013-12-19 12:00:05.184043", "stderr": "", "stdout": "one" }, { "changed": true, "cmd": "echo \"two\" ", "delta": "0:00:00.002920", "end": "2013-12-19 12:00:05.245502", "invocation": { "module_args": "echo \"two\"", "module_name": "shell" }, "item": "two", "rc": 0, "start": "2013-12-19 12:00:05.242582", "stderr": "", "stdout": "two" } ] }
遍歷註冊變量的結果:
- name: Fail if return code is not 0 fail: msg: "The command ({{ item.cmd }}) did not have a 0 return code" when: item.rc != 0 with_items: "`echo`.`results`"
示例:
- hosts: webservers remote_user: root vars: alpha: [ 'a','b','c','d'] numbers: [ 1,2,3,4 ] tasks: - debug: msg="{{ item.0 }} and {{ item.1 }}" with_together: - "{{ alpha }}" - "{{ numbers }}"
輸出的結果爲:
ok: [192.168.1.65] => (item=['a', 1]) => { "item": [ "a", 1 ], "msg": "a and 1" } ok: [192.168.1.65] => (item=['b', 2]) => { "item": [ "b", 2 ], "msg": "b and 2" } ok: [192.168.1.65] => (item=['c', 3]) => { "item": [ "c", 3 ], "msg": "c and 3" } ok: [192.168.1.65] => (item=['d', 4]) => { "item": [ "d", 4 ], "msg": "d and 4" }
loop模塊通常在下面的場景中使用:
在有的時候play的結果依賴於變量、fact或者是前一個任務的執行結果,從而須要使用到條件語句。
如下主要來自他人分享。
有的時候在特定的主機須要跳過特定的步驟,例如在安裝包的時候,須要指定主機的操做系統類型,或者是當操做系統的硬盤滿了以後,須要清空文件等,可使用when語句來作判斷 。when關鍵字後面跟着的是python的表達式,在表達式中你可以使用任何的變量或者fact,當表達式的結果返回的是false,便會跳過本次的任務。
(1)基本用法
--- - name: Install VIM hosts: all tasks: - name:Install VIM via yum yum: name=vim-enhanced state=installed when: ansible_os_family =="RedHat" - name:Install VIM via apt apt: name=vim state=installed when: ansible_os_family =="Debian" - name: Unexpected OS family debug: msg="OS Family {{ ansible_os_family }} is not supported" fail=yes when: not ansible_os_family =="RedHat" or ansible_os_family =="Debian"
條件語句還有一種用法,它還可讓你當達到必定的條件的時候暫停下來,等待你的輸入確認。通常狀況下,當ansible遭遇到error時,它會直接結束運行。那其實你能夠當遭遇到不是預期的狀況的時候給使用pause模塊,這樣可讓用戶本身決定是否繼續運行任務:
- name: pause for unexpected conditions pause: prompt="Unexpected OS" when: ansible_os_family !="RedHat"
(2)在when中使用jinja2的語法
tasks: - command: /bin/false register: result # 將命令執行的結果傳遞給result變量 ignore_errors: True # 忽略錯誤 - command: /bin/something when: result|failed # 若是註冊變量的值 是任務failed則返回true - command: /bin/something_else when: result|success # 若是註冊變量的值是任務success則返回true - command: /bin/still/something_else when: result|skipped # 若是註冊變量的值是任務skipped則返回true - command: /bin/foo when: result|changed # 若是註冊變量的值是任務changed則返回true - hosts: all user: root vars: epic: true tasks: - shell: echo "This certainly is epic!" when: epic - shell: echo "This certainly is not epic!" when: not epic
(3)若是變量不存在,則能夠經過jinja2的'defined'命令跳過
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 not defined
(4)when在循環語句中的使用方法
tasks: - command: echo {{ item }} with_items: [ 0, 2, 4, 6, 8, 10 ] when: item > 5六、在include和roles中使用when: # 在include中使用的示例:- include: tasks/sometasks.yml when: "'reticulating splines' in output" # 在roles中使用的示例:- hosts: webservers roles: - { role: debian_stock_config, when: ansible_os_family == 'Debian' }
有些時候,你也許想在一個Playbook中以不一樣的方式作事,好比說在debian和centos上安裝apache,apache的包名不一樣,除了when語句,還可使用下面的示例來解決:
--- - hosts: all remote_user: root vars_files: - "vars/common.yml" - [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ] tasks: - name: make sure apache is running service: name={{ apache }} state=running
不少不一樣的yml文件只是包含鍵和值,以下:
[root@Ansible ~]# for vars/CentOS.yml apache: httpd somethingelse: 42
若是操做系統是「CentOS」,Ansible導入的第一個文件將是「vars/CentOS.yml」,緊接着 是「/var/os_defaults.yml」,若是這個文件不存在。並且在列表中沒有找到,就會報錯。 在Debian系統中,最早查看的將是「vars/Debian.yml」而不是「vars/CentOS.yml」,若是沒找到,則尋找默認文件「vars/os_defaults.yml」。
有些時候,咱們想基於不一樣的操做系統,選擇不一樣的配置文件,及配置文件的存放路徑,能夠藉助with_first_found來解決:
- name: template a file template: src={{ item }} dest=/etc/myapp/foo.conf with_first_found: - files: - {{ ansible_distribution }}.conf - default.conf paths: - search_location_one/somedir/ - /opt/other_location/somedir/
failed_when實際上是ansible的一種錯誤處理機制,是由fail模塊使用了when條件語句的組合效果。示例以下:
- 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.stderr"
咱們也能夠直接經過fail模塊和when條件語句,寫成以下:
- name: this command prints FAILED when it fails command: /usr/bin/example-command -x -y -z register: command_result ignore_errors: True - name: fail the play if the previous command did not succeed fail: msg="the command failed" when: "'FAILED' in command_result.stderr"
當咱們控制一些遠程主機執行某些任務時,當任務在遠程主機上成功執行,狀態發生更改時,會返回changed狀態響應,狀態未發生更改時,會返回OK狀態響應,當任務被跳過期,會返回skipped狀態響應。咱們能夠經過changed_when來手動更改changed響應狀態。示例以下:
- shell: /usr/bin/billybass --mode="take me to the river" register: bass_result changed_when: "bass_result.rc != 2" # 只有該條task執行之後,bass_result.rc的值不爲2時,纔會返回changed狀態 # 永遠不會報告「改變」的狀態 - shell: wall 'beep' changed_when: False # 當changed_when爲false時,該條task在執行之後,永遠不會返回changed狀態