Ansible PlayBook语法
PlayBook語法實例
playbook是由一個或多個play組成的列表,play的主要功能在于將事先歸并為一組的主機(jī)裝扮成事先通過Ansible中的tasks定義好的角色(play的內(nèi)容被稱為tasks,即任務(wù)),從根本上來講所謂tasks無非是調(diào)用Ansible的一個module,將多個play組織在一個playbook中即可以讓它們聯(lián)同起來按事先編排的機(jī)制一同工作.
下面來看一個基礎(chǔ)的playbook,其中只包含一個tasks任務(wù):
--- - hosts: web_server #指定執(zhí)行的主機(jī)組vars: #聲明了2個變量http_port: 80max_clients: 200remote_user: root #使用root權(quán)限執(zhí)行tasks:- name: ensure apache is at the latest versionyum: pkg=httpd state=latest- name: write the apache config file #將apache配置文件拷貝到指定主機(jī)template: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd.confnotify: #當(dāng)上面拷貝工作完成以后,執(zhí)行消息發(fā)送- restart apache- name: ensure apache is running #設(shè)置開機(jī)自啟動service: name=httpd state=startedhandlers:- name: restart apache #接收消息,并執(zhí)行重啟apache服務(wù)service: name=httpd state=restarted第一行中,文件開頭為---,這是YAML將文件解釋為正確的文檔的要求,YAML允許多個文檔存在于一個文件中,每個文檔由 --- 符號分割,但Ansible只需要一個文件存在一個文檔即可,因此這里需要存在于文件的開始行第一行.
YAML對空格非常敏感,并使用空格來將不同的信息分組在一起,在整個文件中應(yīng)該只使用空格而不使用制表符,并且必須使用一致的間距,才能正確讀取文件,相同縮進(jìn)級別的項目被視為同級元素.
以 - 開頭的項目被視為列表項目.作為散列或字典操作,它具有key:value格式的項,YAML文檔基本上定義了一個分層的樹結(jié)構(gòu),其中位于左側(cè)是包含的元素.YAML文件擴(kuò)展名通常為.yaml或者.yml,接下來,我們將分別講解 playbook 語言的多個特性.
PlayBook構(gòu)成部分
◆Hosts(主機(jī))與Users(用戶)◆
我們可以為playbook中的每一個play,個別的選擇操作的目標(biāo)機(jī)器是哪些,以哪個用戶身份去完成要執(zhí)行的步驟(called tasks)
--- - hosts: web_serversremote_user: roottasks:- name: test connectionremote_user: yournamesudo: yesplaybook中的每一個play的目的都是為了讓某個或某些主機(jī)以某個指定的用戶身份執(zhí)行任務(wù).
| hosts | 指定要執(zhí)行指定任務(wù)的主機(jī) |
| remote_user | 指定遠(yuǎn)程主機(jī)上的執(zhí)行任務(wù)的用戶,此處的意思是以root身份執(zhí)行 |
| user | 與remote_user相同 |
| sudo | 如果設(shè)置為yes執(zhí)行該任務(wù)組的用戶在執(zhí)行任務(wù)的時候,獲取root權(quán)限 |
| sudo_user | 指定使用那個用戶授權(quán)執(zhí)行 |
| connection | 通過什么方式連接到遠(yuǎn)程主機(jī),默認(rèn)為ssh |
| gather_facts | setup模塊默認(rèn)自動執(zhí)行 |
◆Tasks 和 Action◆
每一個play包含了一個tasks列表(任務(wù)列表),任務(wù)列表中的各任務(wù)按次序逐個在hosts中指定的所有主機(jī)上執(zhí)行即在所有主機(jī)上完成第一個任務(wù)后再開始第二個,在自上而下運(yùn)行某playbook時如果中途發(fā)生錯誤,所有已執(zhí)行任務(wù)都將回滾,因此在更正playbook后重新執(zhí)行即可.
每一個tasks必須有一個名稱name,這樣在運(yùn)行playbook時,從其輸出的任務(wù)執(zhí)行信息中可以很好的辨別出是屬于哪一個tasks的,如果沒有定義name,action的值將會用作輸出信息中標(biāo)記特定的tasks.tasks的目的是使用指定的參數(shù)執(zhí)行模塊,而在模塊參數(shù)中可以使用變量.模塊執(zhí)行是冪等的,這意味著多次執(zhí)行是安全的,因為其結(jié)果均一致.
每個tassk都應(yīng)該有其name用于playbook的執(zhí)行結(jié)果輸出,建議其內(nèi)容盡可能清晰地描述任務(wù)執(zhí)行步驟,如果未提供name則action的結(jié)果將用于輸出.
下面是一種基本的taska的定義,service moudle使用key=value格式的參數(shù),這也是大多數(shù)module使用的參數(shù)格式:
tasks:- name: make sure apache is runningservice: name=httpd state=running[在眾多模塊中,只有command和shell模塊僅需要給定一個列表而無需使用“key=value”格式如下]tasks:- name: disable selinuxcommand: /sbin/setenforce 0[使用command module和shell module時,我們需要關(guān)心返回碼信息,如果有一條命令,它的成功執(zhí)行的返回碼不是0我們可以這樣做] tasks:- name: run this command and ignore the resultshell: /usr/bin/somecommand || /bin/true[或者使用ignore_errors來忽略錯誤信息] tasks:- name: run this command and ignore the resultshell: /usr/bin/somecommandignore_errors: True[如果action行看起來太長,可以使用space(空格)或者indent(縮進(jìn))隔開連續(xù)的一行] tasks:- name: Copy ansible inventory file to clientcopy: src=/etc/ansible/hosts dest=/etc/ansible/hosts owner=root group=root mode=0644◆Handlers 發(fā)生改變后執(zhí)行◆
上面我們曾提到過,module具有“冪等”性,所以當(dāng)遠(yuǎn)程主機(jī)被人改動時,可以重放playbooks達(dá)到恢復(fù)的目的.playbooks本身可以識別這種改動,并且有一個基本的event system(事件系統(tǒng)),可以響應(yīng)這種改動.
當(dāng)發(fā)生改動時notify這個actions會在playbook的每一個tasks結(jié)束時被觸發(fā),而且即使有多個不同的tasks通知改動的發(fā)生,notify actions只會被觸發(fā)一次.這樣可以避免多次有改變發(fā)生時每次都執(zhí)行指定的操作,取而代之僅在所有的變化發(fā)生完成后一次性地執(zhí)行指定操作.
下面一個小例子,當(dāng)一個文件的內(nèi)容被改動時,重啟兩個services進(jìn)程.
- name: template configuration filetemplate: src=template.conf dest=/etc/foo.confnotify:- restart memcached- restart apachehandlers也是一些tasks的列表,通過名字來引用,它們和一般的tasks并沒有什么區(qū)別handlers是由通知者進(jìn)行notify,如果沒有被notify,handlers不會執(zhí)行.不管有多少個通知者進(jìn)行了notify,等到play中的所有tasks執(zhí)行完成之后,handlers也只會被執(zhí)行一次.
下面是一個 handlers 的示例:
handlers:- name: restart memcachedservice: name=memcached state=restarted- name: restart apacheservice: name=apache state=restartedHandlers最佳的應(yīng)用場景是用來重啟服務(wù),或者觸發(fā)系統(tǒng)重啟操作,除此以外很少用到了.
◆幾個常用小實例◆
1.通過playbook添加用戶,給遠(yuǎn)程主機(jī)添加用戶lyshark.
--- - name: create userhosts: alluser: rootgather_facts: False #在執(zhí)行命令前是否去獲取setup信息vars:- user: lysharktasks:- name: create useruser: name={{ user }}2.通過playbook刪除用戶,刪除遠(yuǎn)程主機(jī)lyshark的賬號.
--- - name: create userhosts: alluser: rootgather_facts: Falsevars:- user: lysharktasks:- name: create useruser: name={{ user }} state=absent remove=yes3.安裝 httpd 服務(wù),將本地準(zhǔn)備好的配置文件 copy 過去,并且啟動服務(wù).
--- - hosts: allremote_user: rootgather_facts: Falsetasks:- name: install apache on CentOS 7yum: name=httpd state=present- name: copy httpd confcopy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf- name: start apache serviceservice: name=httpd state=started4.通過playbook 安裝apache修改端口,并帶有vars變量.
--- - hosts: allremote_user: rootgather_facts: Falsevars:src_http_dir: /etc/httpd/confdest_http_dir: /etc/httpd/conftasks:- name: install apache on CentOS 7yum: name=httpd state=present- name: copy httpd confcopy: src={{ src_http_dir }}/httpd.conf dest={{ dest_http_dir }}/httpd.confnotify:- systemctl restart httpd- name: start apache serviceservice: name=httpd state=restartedhandlers: #當(dāng)httpd.conf文件發(fā)生改變時,就觸發(fā)handler進(jìn)行服務(wù)重啟.- name: restart apacheservice: name=httpd state=restartedPlayBook常用模塊
Playbook的模塊與在Ansible命令行下使用的模塊有一些不同.這主要是因為在playbook中會使用到一些facts變量和一些通過setup模塊從遠(yuǎn)程主機(jī)上獲取到的變量,有些模塊沒法在命令行下運(yùn)行,就是因為它們需要這些變量.而且即使那些可以在命令行下工作的模塊也可以通過playbook的模塊獲取一些更高級的功能.
◆template模塊◆
在實際應(yīng)用中,我們的配置文件有些地方可能會根據(jù)遠(yuǎn)程主機(jī)的配置的不同而有稍許的不同,template可以使用變量來接收遠(yuǎn)程主機(jī)上setup收集到的facts信息,針對不同配置的主機(jī),定制配置文件,用法大致與copy模塊相同.
| attributes | 文件或目錄的屬性 |
| backup | 如果原目標(biāo)文件存在,則先備份目標(biāo)文件 |
| block_end_string | 標(biāo)記塊結(jié)束的字符串 |
| block_start_string | 標(biāo)記塊的開始的字符串 |
| dest | 目標(biāo)文件路徑 |
| follow | 是否遵循目標(biāo)中的文件鏈接 |
| force | 是否強(qiáng)制覆蓋,默認(rèn)為yes |
| group | 目標(biāo)文件或目錄的所屬組 |
| owner | 目標(biāo)文件或目錄的所屬主 |
| mode | 目標(biāo)文件的權(quán)限 |
| newline_sequence | 指定用于模板文件的換行符序列 |
| src | 源模板文件路徑 |
| trim_blocks | 如果這設(shè)置為True,則刪除塊后的第一個換行符 |
| validate | 在復(fù)制之前通過命令驗證目標(biāo)文件,如果驗證通過則復(fù)制 |
| variable_end_string | 標(biāo)記打印語句結(jié)束的字符串 |
| variable_start_string | 標(biāo)記打印語句開頭的字符串 |
◆set_fact模塊◆
set_fact模塊可以自定義facts,這些自定義的facts可以通過template或者變量的方式在playbook中使用,如果你想要獲取一個進(jìn)程使用的內(nèi)存的百分比,則必須通過set_fact來進(jìn)行計算之后得出其值,并將其值在playbook中引用.
[root@localhost ~]# echo "# Configure the buffer pool" >> /etc/my.cnf [root@localhost ~]# echo "innodb_buffer_pool_size = {{ innodb_buffer_pool_size_mb|int }}M" >> /etc/my.cnf [root@localhost ~]# vim set_fact.yml --- - name: Configure Mariadbhosts: alltasks:- name: install Mariadbyum: name=mariadb-server state=installed- name: Calculate InnoDB buffer pool sizeset_fact: innodb_buffer_pool_size_mb={{ ansible_memtotal_mb / 2 }}- name: Configure Mariadbtemplate: src=/etc/my.cnf dest=/etc/my.cnf owner=root group=root mode=0644- name: Start Mariadbservice: name=mariadb state=started enabled=yeshandlers:- name: restart Mariadbservice: name=mariadb state=restarted◆pause模塊◆
在playbook執(zhí)行的過程中暫停一定時間或者提示用戶進(jìn)行某些操作,要為每個主機(jī)暫停、等待、休眠,可以使用wait_for模塊,如果您想提前暫停而不是設(shè)置為過期,或者您需要完全中止劇本運(yùn)行.
| echo | 控制鍵入時是否顯示鍵盤輸入 |
| minutes | 暫停多少分鐘 |
| seconds | 暫停多少秒 |
| prompt | 打印一串信息提示用戶操作 |
◆wait_for模塊◆
wait_for模塊是在playbook的執(zhí)行過程中,等待某些操作完成以后再進(jìn)行后續(xù)操作.
| active_connection_states | 被計為活動連接的TCP連接狀態(tài)列表 |
| connect_timeout | 在下一個任務(wù)執(zhí)行之前等待連接的超時時間 |
| delay | 等待一個端口或者文件或者連接到指定的狀態(tài) |
| exclude_hosts | 在查找狀態(tài)的活動TCP連接時要忽略的主機(jī)或IP的列表drained |
| host | wait_for模塊等待的主機(jī)的地址,默認(rèn)為127.0.0.1 |
| msg | 這會覆蓋正常的錯誤消息,使其不符合所需的條件 |
| port | wait_for模塊等待的主機(jī)的端口 |
| path | 文件路徑,只有當(dāng)這個文件存在時,下一任務(wù)才開始執(zhí)行,即等待該文件創(chuàng)建完成 |
| search_regex | 可以用來匹配文件或套接字連接中的字符串,默認(rèn)為多行正則表達(dá)式 |
| sleep | 檢查之間睡眠的秒數(shù),在2.3之前,這被硬編碼為1秒 |
| state | 等待的狀態(tài),狀態(tài)有started,stoped,present或started,absent |
| timeout | wait_for的等待的超時時間,默認(rèn)為300秒 |
◆assemble模塊◆
assemble模塊用于組裝文件,即將多個零散的文件(稱為碎片),合并一個目標(biāo)文件.
| attributes | 文件或目錄應(yīng)的屬性 |
| backup | 創(chuàng)建一個備份文件(如果yes),包括時間戳信息 |
| decrypt | 控制使用保管庫對源文件進(jìn)行自動解密 |
| delimiter | 分隔文件內(nèi)容的分隔符 |
| dest | 使用所有源文件的連接創(chuàng)建的文件,合并后的大文件路徑 |
| group | 合并后的大文件的所屬組 |
| owner | 合并后的大文件的所屬主 |
| ignore_hidden | 組裝時,是否忽略隱藏文件,默認(rèn)為no |
| mode | 合并后的大文件的權(quán)限,指定文件權(quán)限 |
| regexp | 在regex匹配文件名時匯編文件 |
| src | 源文件(即零散文件)的路徑 |
| validate | 與template的validate相同,指定命令驗證文件 |
◆add_host模塊◆
add_host模塊使用變量在清單中創(chuàng)建新的主機(jī)組,以便在以后的相同劇本中使用.獲取變量以便我們可以更充分地定義新主機(jī),add_host模塊在playbook執(zhí)行的過程中,動態(tài)的添加主機(jī)到指定的主機(jī)組中.
[添加主機(jī)到webservers組中,主機(jī)的變量foo的值為42] - name: add host to group 'just_created' with variable foo=42add_host:name: "{{ ip_from_ec2 }}"groups: just_createdfoo: 42[將主機(jī)添加到多個組] - name: add host to multiple groupsadd_host:hostname: "{{ new_ip }}"groups:- group1- group2[向主機(jī)添加一個非本地端口的主機(jī)] - name: add a host with a non-standard port local to your machinesadd_host: name: "{{ new_ip }}:{{ new_port }}"[添加一個通過隧道到達(dá)的主機(jī)別名] - 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 }}"[添加一個通過隧道到達(dá)的主機(jī)別名] - 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 }}"◆group_by模塊◆
group_by模塊在playbook執(zhí)行的過程中,動態(tài)的創(chuàng)建主機(jī)組.
[創(chuàng)建主機(jī)組] - group_by:key: machine_{{ ansible_machine }}[創(chuàng)建類似kvm-host的主機(jī)組] - group_by:key: virt_{{ ansible_virtualization_type }}_{{ ansible_virtualization_role }}[創(chuàng)建嵌套主機(jī)組] - group_by:key: el{{ ansible_distribution_major_version }}-{{ ansible_architecture }}parents:- el{{ ansible_distribution_major_version }}◆debug模塊◆
debug模塊在執(zhí)行過程中打印語句,可用于調(diào)試變量或表達(dá)式中輸出信息.
[為每個主機(jī)打印IP地址和網(wǎng)關(guān)] - 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/uptimeregister: result- debug:var: result [直接將上一條指令的結(jié)果作為變量傳遞給var,由debug打印出result的值]verbosity: 2- name: Display all variables/facts known for a hostdebug:var: hostvars[inventory_hostname]verbosity: 4◆fail模塊◆
fail模塊用于終止當(dāng)前playbook的執(zhí)行,通常與條件語句組合使用,當(dāng)滿足條件時,終止當(dāng)前play的運(yùn)行,也可以直接由failed_when取代.
[執(zhí)行失敗錢打印出自定義信息] - fail:msg: "The system may not be provisioned according to the CMDB status."when: cmdb_status != "to-be-staged"PlayBook條件判斷
在有的時候play的結(jié)果依賴于變量,fact或者是前一個任務(wù)的執(zhí)行結(jié)果,從而需要使用到條件語句.
◆when◆
有的時候在特定的主機(jī)需要跳過特定的步驟,例如在安裝包的時候,需要指定主機(jī)的操作系統(tǒng)類型,或者是當(dāng)操作系統(tǒng)的硬盤滿了之后,需要清空文件等,可以使用when語句來做判斷.when關(guān)鍵字后面跟著的是python的表達(dá)式,在表達(dá)式中你能夠使用任何的變量或者fact,當(dāng)表達(dá)式的結(jié)果返回的是false,便會跳過本次的任務(wù).
when基本用法
--- - name: Install VIMhosts: all tasks:- name:Install VIM via yumyum: name=vim-enhanced state=installedwhen: ansible_os_family =="RedHat"- name:Install VIM via aptapt: name=vim state=installedwhen: ansible_os_family =="Debian"- name: Unexpected OS familydebug: msg="OS Family {{ ansible_os_family }} is not supported" fail=yeswhen: not ansible_os_family =="RedHat" or ansible_os_family =="Debian"條件語句還有一種用法,它還可以讓你當(dāng)達(dá)到一定的條件的時候暫停下來,等待你的輸入確認(rèn).一般情況下,當(dāng)ansible遭遇到error時,它會直接結(jié)束運(yùn)行.那其實你可以當(dāng)遭遇到不是預(yù)期的情況的時候給使用pause模塊,這樣可以讓用戶自己決定是否繼續(xù)運(yùn)行任務(wù):
- name: pause for unexpected conditionspause: prompt="Unexpected OS"when: ansible_os_family !="RedHat"在when中使用jinja2的語法
tasks:- command: /bin/falseregister: result # 將命令執(zhí)行的結(jié)果傳遞給result變量ignore_errors: True # 忽略錯誤- command: /bin/somethingwhen: result|failed # 如果注冊變量的值 是任務(wù)failed則返回true- command: /bin/something_elsewhen: result|success # 如果注冊變量的值是任務(wù)success則返回true- command: /bin/still/something_elsewhen: result|skipped # 如果注冊變量的值是任務(wù)skipped則返回true- command: /bin/foowhen: result|changed # 如果注冊變量的值是任務(wù)changed則返回true - hosts: alluser: rootvars:epic: truetasks: - shell: echo "This certainly is epic!" when: epic- shell: echo "This certainly is not epic!"when: not epic如果變量不存在,則可以通過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 definedwhen在循環(huán)語句中的使用方法
tasks:- command: echo {{ item }}with_items: [ 0, 2, 4, 6, 8, 10 ]when: item > 56 #在include和roles中使用when: # 在include中使用的示例:- include: tasks/sometasks.ymlwhen: "'reticulating splines' in output" # 在roles中使用的示例:- hosts: webserversroles:- { role: debian_stock_config, when: ansible_os_family == 'Debian' }◆條件導(dǎo)入◆
有些時候,你也許想在一個Playbook中以不同的方式做事,比如說在debian和centos上安裝apache,apache的包名不同,除了when語句,還可以使用下面的示例來解決:
--- - hosts: allremote_user: rootvars_files:- "vars/common.yml"- [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ]tasks:- name: make sure apache is runningservice: name={{ apache }} state=running很多不同的yml文件只是包含鍵和值,如下:
[root@localhost ~]# for vars/CentOS.yml apache: httpd somethingelse: 42如果操作系統(tǒng)是“CentOS”,Ansible導(dǎo)入的第一個文件將是“vars/CentOS.yml”,緊接著是“/var/os_defaults.yml”,如果這個文件不存在.而且在列表中沒有找到,就會報錯.在Debian系統(tǒng)中,最先查看的將是“vars/Debian.yml”而不是“vars/CentOS.yml”,如果沒找到,則尋找默認(rèn)文件“vars/os_defaults.yml”.
◆with_first_found◆
有些時候,我們想基于不同的操作系統(tǒng),選擇不同的配置文件,及配置文件的存放路徑,可以借助with_first_found來解決:
- name: template a filetemplate: src={{ item }} dest=/etc/myapp/foo.confwith_first_found:- files:- {{ ansible_distribution }}.conf- default.confpaths:- search_location_one/somedir/- /opt/other_location/somedir/◆failed_when◆
failed_when其實是ansible的一種錯誤處理機(jī)制,是由fail模塊使用了when條件語句的組合效果.示例如下:
- name: this command prints FAILED when it failscommand: /usr/bin/example-command -x -y -zregister: command_resultfailed_when: "'FAILED' in command_result.stderr"我們也可以直接通過fail模塊和when條件語句,寫成如下:
- name: this command prints FAILED when it failscommand: /usr/bin/example-command -x -y -zregister: command_resultignore_errors: True- name: fail the play if the previous command did not succeedfail: msg="the command failed"when: "'FAILED' in command_result.stderr"◆changed_when◆
當(dāng)我們控制一些遠(yuǎn)程主機(jī)執(zhí)行某些任務(wù)時,當(dāng)任務(wù)在遠(yuǎn)程主機(jī)上成功執(zhí)行,狀態(tài)發(fā)生更改時,會返回changed狀態(tài)響應(yīng),狀態(tài)未發(fā)生更改時,會返回OK狀態(tài)響應(yīng),當(dāng)任務(wù)被跳過時,會返回skipped狀態(tài)響應(yīng).我們可以通過changed_when來手動更改changed響應(yīng)狀態(tài),示例如下:
- shell: /usr/bin/billybass --mode="take me to the river"register: bass_resultchanged_when: "bass_result.rc != 2" # 只有該條task執(zhí)行以后,bass_result.rc的值不為2時,才會返回changed狀態(tài)# 永遠(yuǎn)不會報告“改變”的狀態(tài) - shell: wall 'beep'changed_when: False # 當(dāng)changed_when為false時,該條task在執(zhí)行以后,永遠(yuǎn)不會返回changed狀態(tài)PlayBook循環(huán)語句
在使用Ansible做自動化運(yùn)維的時候,免不了的要重復(fù)執(zhí)行某些操作,如:添加幾個用戶,創(chuàng)建幾個MySQL用戶并為之賦予權(quán)限,操作某個目錄下所有文件等等.好在playbook支持循環(huán)語句,可以使得某些需求很容易而且很規(guī)范的實現(xiàn).
◆with_items◆
with_items是playbooks中最基本也是最常用的循環(huán)語句.
tasks: - name:Secure config filesfile: path=/etc/{{ item }} mode=0600 owner=root group=rootwith_items:- my.cnf- shadow- fstab# 或with_items:"{{ somelist }}"上面的例子說明在/etc下創(chuàng)建權(quán)限級別為0600,屬主屬組都是root三個文件,分別為my.cnf、shadow、fstab.
使用with_items迭代循環(huán)的變量可以是個單純的列表,也可以是一個較為復(fù)雜的數(shù)據(jù)結(jié)果,如字典類型:
◆with_nested嵌套循環(huán)◆
tasks: - name: give users access to multiple databasesmysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foowith_nested:- [ 'alice', 'bob' ]- [ 'clientdb', 'employeedb', 'providerdb' ]item[0]是循環(huán)的第一個列表的值['alice','bob'].
item[1]是第二個列表的值,表示循環(huán)創(chuàng)建alice和bob兩個用戶,并且為其賦予在三個數(shù)據(jù)庫上的所有權(quán)限.
也可以將用戶列表事先賦值給一個變量:
tasks: - name: here, 'users' contains the above list of employeesmysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foowith_nested:- "{{users}}"- [ 'clientdb', 'employeedb', 'providerdb' ]◆with_dict◆
with_dict可以遍歷更復(fù)雜的數(shù)據(jù)結(jié)構(gòu),假如有如下變量內(nèi)容:
users:alice:name: Alice Appleworthtelephone: 123-456-7890bob:name: Bob Bananaramatelephone: 987-654-3210現(xiàn)在需要輸出每個用戶的用戶名和手機(jī)號:
tasks:- name: Print phone recordsdebug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"with_dict: "{{ users }}"◆with_fileglob文件匹配遍歷◆
可以指定一個目錄,使用with_fileglob可以循環(huán)這個目錄中的所有文件,示例如下:
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 fileassemble: src=/root/.sshkeys dest=/root/.ssh/authorized_keysmode=0600 owner=root group=root◆with_subelement遍歷子元素◆
假如現(xiàn)在需要遍歷一個用戶列表,并創(chuàng)建每個用戶,而且還需要為每個用戶配置以特定的SSH key登錄,變量文件內(nèi)容如下:
users:- name: aliceauthorized:- /tmp/alice/onekey.pub- /tmp/alice/twokey.pubmysql:password: mysql-passwordhosts:- "%"- "127.0.0.1"- "::1"- "localhost"privs:- "*.*:SELECT"- "DB1.*:ALL"- name: bobauthorized:- /tmp/bob/id_rsa.pubmysql:password: other-mysql-passwordhosts:- "db1"privs:- "*.*:SELECT"- "DB2.*:ALL"[playbook中定義如下:] - user: name={{ item.name }} state=present generate_ssh_key=yeswith_items: "`users`" - authorized_key: "user={{ item.0.name }} key='{{ lookup('file', item.1) }}'"with_subelements:- users- authorized[也可以遍歷嵌套的子列表:] - name: Setup MySQL usersmysql_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循環(huán)整數(shù)序列◆
with_sequence可以生成一個自增的整數(shù)序列,可以指定起始值和結(jié)束值,也可以指定增長步長. 參數(shù)以key=value的形式指定,format指定輸出的格式.數(shù)字可以是十進(jìn)制、十六進(jìn)制、八進(jìn)制:
- hosts: alltasks:# create groups- group: name=evens state=present- group: name=odds state=present# create some test users- user: name={{ item }} state=present groups=evenswith_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=directorywith_sequence: start=4 end=16 stride=2 # stride用于指定步長# a simpler way to use the sequence plugin# create 4 groups- group: name=group{{ item }} state=presentwith_sequence: count=4◆with_random_choice隨機(jī)選擇◆
從列表中隨機(jī)取一個值:
- debug: msg={{ item }}with_random_choice:- "go through the door"- "drink from the goblet"- "press the red button"- "do nothing"◆do-Util循環(huán)◆
重復(fù)執(zhí)行shell模塊,當(dāng)shell模塊執(zhí)行的命令輸出內(nèi)容包含"all systems go"的時候停止,重試5次,延遲時間10秒.retries默認(rèn)值為3,delay默認(rèn)值為5,任務(wù)的返回值為最后一次循環(huán)的返回結(jié)果.
- action: shell /usr/bin/fooregister: resultuntil: result.stdout.find("all systems go") != -1retries: 5delay: 10◆循環(huán)注冊變量◆
在循環(huán)中使用register時,保存的結(jié)果中包含results關(guān)鍵字,該關(guān)鍵字保存模塊執(zhí)行結(jié)果的列表.
---- hosts: alltasks:- shell: echo "{{ item }}"with_items:- one- tworegister: echo變量內(nèi)容如下:
TASK [command] ******************************************************* skipping: [192.168.10.20] => (item=one) skipping: [192.168.10.20] => (item=two)PLAY RECAP *********************************************************** 192.168.10.20 : ok=1 changed=0 unreachable=0 failed=0遍歷注冊變量的結(jié)果:
- name: Fail if return code is not 0fail:msg: "The command ({{ item.cmd }}) did not have a 0 return code"when: item.rc != 0with_items: "`echo`.`results`"◆with_together遍歷數(shù)據(jù)并行集合◆
- hosts: allremote_user: rootvars:alpha: [ 'a','b','c','d']numbers: [ 1,2,3,4 ]tasks:- debug: msg="{{ item.0 }} and {{ item.1 }}"with_together:- "{{ alpha }}"- "{{ numbers }}"輸出執(zhí)行結(jié)果如下:
ok: [192.168.10.20] => (item=[u'a', 1]) => {"changed": false,"item": ["a",1],"msg": "a and 1" } ok: [192.168.10.20] => (item=[u'b', 2]) => {"changed": false,"item": ["b",2],"msg": "b and 2" } ok: [192.168.10.20] => (item=[u'c', 3]) => {"changed": false,"item": ["c",3],"msg": "c and 3" } ok: [192.168.10.20] => (item=[u'd', 4]) => {"changed": false,"item": ["d",4],"msg": "d and 4" }PlayBook部署實驗
Ansible的PlayBook文件格式為YAML語言,所以希望讀者在編寫PlayBook前對YAML語法有一定的了解,否則在運(yùn)行PlayBook的時候經(jīng)常碰到語法錯誤提示,這里我們通過介紹批量部署apache服務(wù)為例,介紹一下apache.yaml這個PlayBook的具體應(yīng)用寫法,如果你對YAML語言沒有了解的話,請自行去百度學(xué)習(xí).
1.首先在當(dāng)前目錄下創(chuàng)建一個目錄,用來保存與apache相關(guān)的配置文件和程序,這里要注意的是,我們應(yīng)該在本機(jī)搭建一個環(huán)境,調(diào)試好后再進(jìn)行部署,調(diào)試環(huán)節(jié)此處略過.
[root@localhost ~]# mkdir playbook[root@localhost ~]# ll total 0 drwxr-xr-x. 2 root root 6 Dec 2 06:43 playbook[root@localhost playbook]# cp -a /etc/httpd/conf/httpd.conf ./ [root@localhost playbook]# cp -a /var/www/html/index.html ./ [root@localhost playbook]# ls -l total 16 -rw-r--r--. 1 root root 11755 Dec 2 06:46 httpd.conf -rw-r--r--. 1 root root 15 Dec 2 06:44 index.html2.通過編寫一個apache.yaml劇本,來實現(xiàn)批量部署的案例,這里是Yum安裝所以很簡單,首先我們先來看一下這個PlayBook的代碼:
[root@localhost playbook]# cat apache.yaml1 --- #表示該文件是YAML文件,非必需2 - hosts: all #playbook針對的主機(jī),all表示所有3 tasks: #表示一個集合4 - name: Install httpd #說明信息,安裝httpd服務(wù)5 yum: name=httpd state=present #通過YUM模塊安裝信息67 - name: Copy httpd.conf #說明信息,復(fù)制配置文件8 template: src=./httpd.conf dest=/etc/httpd/conf/httpd.conf owner=root group=root mode=0755910 - name: Copy index.html #說明文件,復(fù)制網(wǎng)頁目錄11 template: src=./index.html dest=/var/www/html/index.html owner=root group=root mode=075512 notify: #發(fā)送消息,當(dāng)上一條語句執(zhí)行完畢后,執(zhí)行下面的模塊13 - Restart Apache Server1415 handlers: #消息發(fā)送到這里,指定主機(jī)執(zhí)行模塊16 - name: Restart Apache Server #與上面的notify內(nèi)容相對應(yīng)17 service: name=httpd state=restarted #重啟httpd服務(wù)3.當(dāng)我們配置完成YAML劇本以后,再來配置一個Inventory的hosts主機(jī)文件,也就是指定要執(zhí)行此操作的主機(jī)列表,此處我們定義以下主機(jī)列表:
[root@localhost playbook]# cat hosts[apache] #指定Apache這個組,組中有成員192.168.10.20/30兩個IP 192.168.10.20 192.168.10.30#[test] #192.168.10.1..100 #也可以這樣指定一個范圍 #192.168.10.1[0:100] #也可以這樣寫[apache:vars] #指定我們使用的Python解釋器路徑 ansible_python_interpreter=/usr/bin/python2.74.接下來我們對apache.yaml使用 --syntax-check 命令參數(shù),檢查一下PlayBook語法是否正確:
[root@localhost playbook]# ansible-playbook apache.yaml --syntax-checkplaybook: apache.yaml5.緊接著使用--list-task參數(shù)顯示apache.yaml,PlayBook文件中所有的task名稱如下所示:
[root@localhost playbook]# ansible-playbook apache.yaml --list-taskplaybook: apache.yamlplay #1 (all): all TAGS: []tasks:Install httpd TAGS: []Copy httpd.conf TAGS: []Copy index.html TAGS: []5.緊接著使用--list-hosts參數(shù)顯示apache.yaml,PlayBook文件中所有的task名稱如下所示:
[root@localhost playbook]# ansible-playbook apache.yaml --list-hostsplaybook: apache.yamlplay #1 (all): all TAGS: []pattern: [u'all']hosts (2):192.168.10.20192.168.10.306.確認(rèn)過以后,直接使用下面的命令一鍵部署,我們寫好的PlayBook劇本,此時我們等它一會.
[root@localhost playbook]# ansible-playbook -i hosts apache.yamlPLAY RECAP ****************************************************************************** 192.168.10.20 : ok=5 changed=4 unreachable=0 failed=0 192.168.10.30 : ok=5 changed=4 unreachable=0 failed=0當(dāng)然playbook還支持交互式地執(zhí)行 task 我們可以指定 -step 參數(shù)即可,apache.yaml 是一個相對簡單的 Playbook 文件,在我們的實際工作中可能會遇到各種復(fù)雜的需求,但 Playbook 的靈活性非常強(qiáng)大,下面我們來看幾個常用參數(shù),如下:
--- - hosts: 192168.10.10:192.168.10.100 #指定一個主機(jī)執(zhí)行范圍patternsremote_user: root #指定遠(yuǎn)程SSH認(rèn)證用戶sudo: yes #啟用Sudo操作sudo_user: lyshark #指定Sudo的用戶gather_facts: no #設(shè)置facts信息收集accelerate: no #設(shè)置accelerate模式accelerate_port: 5099 #設(shè)置accelerate端口max_fail_percentage: 30 #設(shè)置失敗百分比connection: local #設(shè)置遠(yuǎn)程連接方式serial: 15 #設(shè)置變量Ansible 的 playbook 寫法很豐富,功能很強(qiáng)大,只有掌握了 playbook 每一個參數(shù)之后,我們才能寫出強(qiáng)大而且靈活性很高的 Playbook ,這也是我們在工作中接觸和使用最多的地方.
總結(jié)
以上是生活随笔為你收集整理的Ansible PlayBook语法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 听说火狐浏览器的图标里没了狐狸,网友们开
- 下一篇: 新概念英语3完美笔记doc版