ansible基础-playbooks
?1. playbooks介紹
如果說ansible的modules是工具,inventory配置文件是原材料,那么playbook就是一封說明書,這里會(huì)記錄任務(wù)是如何如何執(zhí)行的,當(dāng)然如果你愿意,這里也可以定義一些變量、連接參數(shù)等等。
playbook可以由單個(gè)或者多個(gè)play組成。
單個(gè)play示例:
--- - hosts: webserversvars:http_port: 80max_clients: 200remote_user: roottasks:- name: ensure apache is at the latest versionyum:name: httpdstate: latest- name: write the apache config filetemplate:src: /srv/httpd.j2dest: /etc/httpd.confnotify:- restart apache- name: ensure apache is runningservice:name: httpdstate: started上面的示例中所有的任務(wù)作用于webservers所包含的主機(jī),通過root用戶連接到目的主機(jī),對(duì)apache服務(wù)進(jìn)行了安裝、配置、啟動(dòng)等操作,當(dāng)配置文件有更改時(shí),會(huì)觸發(fā)hanlders里的重啟apache操作,vars里定義的“http_port”和 “ max_clients”將會(huì)在模版文件“/srv/httpd.j2”中遵循Jinja2語法被使用到。
playbooks是使用yaml語法格式,所以看起來比較通俗易懂。通過上面的示例可以看出一個(gè)play可以包含如下內(nèi)容:
- hosts:主機(jī)組,后面定義的task將作用于該主機(jī)組的所有主機(jī)
- vars:變量定義,在后面的task中可以引用
- remote-user:連接參數(shù),例如remote-user,become,become-user等等,這些參數(shù)將會(huì)覆蓋ansible.cfg配置文件里的參數(shù)
- tasks:任務(wù),可以看作很多modules的集合,這些modules可以使用vars定義的變量
- handlers:觸發(fā)才會(huì)執(zhí)行的task,很多情況下,當(dāng)其他task被執(zhí)行并且狀態(tài)有改變后,我們希望會(huì)觸發(fā)一些任務(wù),那些被觸發(fā)的任務(wù)可以寫在這里
一個(gè)playbooks也可以編寫多個(gè)play,示例如下:
--- - hosts: webserversremote_user: roottasks:- name: ensure apache is at the latest versionyum:name: httpdstate: latest- name: write the apache config filetemplate:src: /srv/httpd.j2dest: /etc/httpd.conf - hosts: databasesremote_user: roottasks:- name: ensure postgresql is at the latest versionyum:name: postgresqlstate: latest- name: ensure that postgresql is startedservice:name: postgresqlstate: started?上面的示例中,第一個(gè)play通過root用戶連接到webservers主機(jī)組,進(jìn)行了apache服務(wù)的安裝和配置操作;第二個(gè)play通過root用戶鏈接到databases主機(jī)組,進(jìn)行了數(shù)據(jù)庫的安裝和啟動(dòng)操作。
2.可重復(fù)利用的playbooks
上一章節(jié)中我們說到,一個(gè)playbooks可以放置多個(gè)play,一個(gè)play里面可以有多個(gè)tasks(modules),但是,當(dāng)要管理的資源越來越多時(shí),我們發(fā)現(xiàn)將所有play都寫在一個(gè)yml文件里會(huì)很臃腫,不好維護(hù)。
此時(shí)我們可以通過“import_playbook”方法引用其他的playbooks文件;
此時(shí)我們可以通過“import_playbook”方法引用其他的playbooks文件;使用“import_tasks”、“include_tasks”、“import_role”、“include_role”、“roles”引用其他的tasks文件。
import_playbook
比較簡單,直接上示例,文件main.yml:
- import_playbook: webservers.yml - import_playbook: databases.yml上述示例中使用import_playbook將webservers.yml和databases.yml文件里的play引用到main.yml,和直接將兩個(gè)文件里的內(nèi)容直接粘過來是一樣的效果,執(zhí)行順序自然也會(huì)按照play定義的順序執(zhí)行。
import_tasks和include_tasks
可以參考筆者之前寫的文章?ansible中include_tasks和import_tasks
import_role和include_role
ansible2.3引入了include_role,ansible 2.4版本后,新增了import_role,通過這兩個(gè)方法可以在tasks里面導(dǎo)入role,示例如下:
--- - hosts: webserverstasks:- debug:msg: "before we run our role"- import_role:name: example- include_role:name: example- debug:msg: "after we ran our role"從上面的示例可以看出,在tasks中使用import_role和include_role方法導(dǎo)入了role example,role里面的task會(huì)按順序執(zhí)行。
?當(dāng)然我們也可以引用的同時(shí)定義變量:
--- - hosts: webserversroles:- common- role: foo_app_instancevars:dir: '/opt/a'app_port: 5000- role: foo_app_instancevars:dir: '/opt/b'app_port: 5001也可以給role打tag:
--- - hosts: webserverstasks:- import_role:name: footags:- bar- baz使用條件語句(后面有詳細(xì)寫when語句用法):
--- - hosts: webserverstasks:- include_role:name: some_rolewhen: "ansible_os_family == 'RedHat'"roles
除了使用import_role和include_role導(dǎo)入role,我們也可以直接使用roles方法來導(dǎo)入,示例如下:
--- - hosts: webservers roles: - common - webservers ###OR - hosts: webservers roles: - role: '/path/to/my/roles/common'?
和import和include方法相比,roles方法是僅僅可以導(dǎo)入role類型的playbook,而上述兩個(gè)方法可以在其他tasks中穿插一些role類型的playbook。
在生產(chǎn)中,roles是比較常用的所以后面的章節(jié)會(huì)有對(duì)roles的單獨(dú)講解,這里就不在展開了。
通過以上的總結(jié),我們可以看出,在ansible里,如果我們想復(fù)用其他文件的playbooks,可以使用include、import、roles三種方法,據(jù)我所知也只有這三種方法。?
2.1 動(dòng)態(tài)和靜態(tài)
至此,我們知道了可以使用import*和include*導(dǎo)入其他的playbooks,那么這兩者的區(qū)別是什么呢?
在ansible 2.4版本中引入了dynamic和static的概念,在這之前只能使用include來導(dǎo)入其他的tasks文件,現(xiàn)在include也能用,但官方在考慮在未來版本廢棄掉。
靜態(tài)指所有import*的方法,動(dòng)態(tài)指include*的方法。
關(guān)于動(dòng)態(tài)和靜態(tài)的兩點(diǎn)區(qū)別,總結(jié)如下:
- import_tasks(Static)方法會(huì)在playbooks解析階段將父task變量和子task變量全部讀取并加載
- include_tasks(Dynamic)方法則是在執(zhí)行play之前才會(huì)加載自己變量
- include_tasks方法調(diào)用的文件名稱可以加變量
- import_tasks方法調(diào)用的文件名稱不可以有變量
具體介紹可以參考筆者之前寫的文章ansible中include_tasks和import_tasks
3.playbooks變量
3.1定義變量
ansible中可以定義變量的地方可以有很多,在這里主要寫下playbooks里面的變量定義,其他部分的變量會(huì)在后續(xù)的“ansible基礎(chǔ)-變量”詳細(xì)闡述。?
變量的定義通常使用YAML語法格式,示例如下:
--- vars:field1: one 字典變量: --- foo:field1: onefield2: twoplay中定義全局變量
--- - hosts: webserversvars:http_port: 80上面示例中 http_port參數(shù)可以在這個(gè)play中的tasks、playbooks、roles中引用。
tasks中定義變量
當(dāng)然,我們也可以在某個(gè)task中定義局部變量,這個(gè)變量只能在本task內(nèi)使用,示例如下:
--- - hosts: node1gather_facts: falsetasks:- name: Use var debugvars:- name: weimeng- age: 26debug:var: name,ageinclude和import中定義變量
include_role定義變量只在被引用的role中生效:
- hosts: node1gather_facts: falsetasks:- include_role:name: role_Avars:age: 24include_tasks定義變量只在被引用的task中生效:
tasks: - import_tasks: wordpress.ymlvars:wp_user: timmy - import_tasks: wordpress.ymlvars:wp_user: alice - import_tasks: wordpress.ymlvars:wp_user: bobroles中定義變量
playbook引用role也可以直接定義變量,示例如下:
--- - hosts: webserversroles:- role: bartags: ["foo"]# using YAML shorthand, this is equivalent to the above- { role: foo, tags: ["bar", "baz"] }注冊變量
在playbook中,我們可以將一個(gè)task的執(zhí)行結(jié)果注冊為一個(gè)變量,供另外一個(gè)task使用。例如:
--- - hosts: webserversroles:- role: bartags: ["foo"]# using YAML shorthand, this is equivalent to the above- { role: foo, tags: ["bar", "baz"] }將一個(gè)task的結(jié)果注冊為一個(gè)變量,然后通過這個(gè)變量判斷另外一個(gè)task是否執(zhí)行,這是注冊變量很常用的方式。
通過命令行定義變量
在我們執(zhí)行playbook時(shí)可以在命令行中指定自定義變量,例如:
ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo”在同一個(gè)scope內(nèi),如果與其他地方的變量沖突,命令行指定的參數(shù)優(yōu)先級(jí)最高。
3.2引用變量文件
上面介紹了在playbook中如何定義變量,那么變量能否和task一樣定義在單獨(dú)的yml文件內(nèi),然后使用類似于include_tasks的語句引用過來呢? 答案是肯定的。
在playbook內(nèi)引用變量文件使用的是vars_files:語句,示例如下:
--- - hosts: allremote_user: rootvars:favcolor: bluevars_files:- /vars/external_vars.ymltasks:- name: this is just a placeholdercommand: /bin/echo foo在變量文件/vars/external_vars.yml中,我們只需要使用YAML語法格式進(jìn)行變量定義即可。
3.3 facts
除了我們自定義的變量,ansible還支持另外一種變量,這個(gè)變量類似于puppet的facter,ansible叫做fact。
ansible的fact會(huì)根據(jù)目的主機(jī)的系統(tǒng)信息生成一個(gè)json格式的變量集合,我們在play中可以直接引用。例如比較常用的變量:ip地址、主機(jī)名、操作系統(tǒng)類型等等。
puppet的facter依賴ruby的一個(gè)安裝包,通過ruby程序收集系統(tǒng)信息,而ansible的fact是通過python程序收集。
我們可以通過setup模塊來獲取目的主機(jī)的fact信息:
ansible hostname -m setupfact會(huì)在playbook執(zhí)行之前收集信息,默認(rèn)是打開的,我們也可以通過指定gather_fact參數(shù)為false/no/False關(guān)閉fact。在沒有配置fact cache的情況下,如果關(guān)閉fact,playbook的執(zhí)行速度會(huì)有一個(gè)顯著的提升,示例如下:
--- - hosts: whatevergather_facts: no3.4變量的使用
前面我們介紹了下變量的定義/引用方式和fact變量,那么在playbook中我們?nèi)绾问褂眠@些變量呢?
變量通常會(huì)在模版、條件判斷語句、新的變量定義等處能用到。
使用變量的方法很簡單,只需要將變量寫在兩個(gè)大括號(hào)內(nèi)并且前后都有空格即可,同時(shí)我們必須將這個(gè)大括號(hào)用雙引號(hào)引起來,如果變量穿插在字符串內(nèi)使用,雙引號(hào)也要將字符串部分引起來。
示例如下:
- hosts: app_serversvars:app_path: "{{ base_path }}/22"如果一個(gè)變量定義比較復(fù)雜,例如列表、字典或fact(json格式),我們可以通過如下方式訪問:
列表變量訪問:
{{ foo[0] }}字典變量訪問:?
{{ foo[name] }}?或
{{ foo.name }}json格式訪問變量訪問:
{{ansible_eth0["ipv4"]["address"] }}或
{{ansible_eth0.ipv4.address }}這里說一個(gè)小技巧,在我們排錯(cuò)過程中很多情況我們要debug一些變量。此時(shí),可以使用debug模塊輸出變量。
debug模塊有兩種使用方式,vars和msg :
--- - hosts: node1gather_facts: falsevars:- name: weimeng- age: 26tasks:- name: Use var debugdebug:var: name,age- name: Use msg debugdebug:msg: "my name is {{ name }},and my age is {{ age }}"輸入如下:
? lab-ansible ansible-playbook playbooks/task_vars.yml[WARNING]: Found variable using reserved name: namePLAY [node1] *******************************************************************TASK [Use var debug] *********************************************************** ok: [node1] => {"name,age": "(u'weimeng', 26)" }TASK [Use msg debug] *********************************************************** ok: [node1] => {"msg": "my name is weimeng,and my age is 26" }PLAY RECAP ********************************************************************* node1 : ok=2 changed=0 unreachable=0 failed=0通過對(duì)比我們可以看出,“vars”適用于直接debug變量,而“msg”可以摻雜一些字符串,我們可以根據(jù)實(shí)際情況來選擇使用。
本章節(jié)主要介紹了playbook的相關(guān)變量。ansible變量的知識(shí)點(diǎn)還是很多的,所以我計(jì)劃在后邊會(huì)單獨(dú)介紹ansible的變量,這里就點(diǎn)到為止。
4. 條件語句
ansible條件語句不是很多,比較常用的就是when語句和循環(huán)語句。
當(dāng)滿足一定的條件時(shí),我們想要跳過某個(gè)task,這時(shí)候when語句出場了。當(dāng)when語句的參數(shù)為true時(shí),才會(huì)執(zhí)行這個(gè)task,否則反之。
yum模塊的name可以以列表的形式指定多個(gè)安裝包,但是很多其他模塊是不支持列表的,例如file的path,copy的src,等等;或者說我們想迭代的將一個(gè)列表元素傳遞給某個(gè)模塊處理,如果有多少個(gè)元素寫多個(gè)task就很麻煩。此時(shí)我們可以使用ansible的循環(huán)語句loop(ansible 2.5以后),在2.5版本之前可以使用with_,loop類似于舊版本的with_list語句。
?4.1 when語句
ansible的when語句用于判斷是否執(zhí)行這個(gè)task,例如
tasks:- name: "shut down Debian flavored systems"command: /sbin/shutdown -t nowwhen: ansible_os_family == "Debian"# note that Ansible facts and vars like ansible_os_family can be used# directly in conditionals without double curly braces示例中如果系統(tǒng)的類型是“Debian”才會(huì)執(zhí)行/sbin/shutdown -t now命令。?
條件語句也可以使用“and”和“or”:
tasks:- name: "shut down CentOS 6 and Debian 7 systems"command: /sbin/shutdown -t nowwhen: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6") or(ansible_distribution == "Debian" and ansible_distribution_major_version == "7")條件也可以寫成列表的形式,這種形式和and語句起到一樣的效果:
tasks:- name: "shut down CentOS 6 systems"command: /sbin/shutdown -t nowwhen:- ansible_distribution == "CentOS"- ansible_distribution_major_version == "6"register變量條件語句
通過對(duì)某個(gè)task的執(zhí)行結(jié)果是否成功,決定另外一個(gè)task是否要執(zhí)行:
tasks:- command: /bin/falseregister: resultignore_errors: True- command: /bin/somethingwhen: result is failed# In older versions of ansible use ``success``, now both are valid but succeeded uses the correct tense.- command: /bin/something_elsewhen: result is succeeded- command: /bin/still/something_elsewhen: result is skipped變量是否被定義語句:
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 undefined4.2 循環(huán)語句
上面說到loop類似于舊版本的with_list語句,也就是說loop會(huì)將列表的元素逐個(gè)傳遞給上面的module,從而達(dá)到重復(fù)執(zhí)行的目的。
最簡單的形式:
--- tasks:- command: echo { item }loop: [ 0, 2, 4, 6, 8, 10 ]loop與when結(jié)合使用:
--- tasks: - command: echo { item } loop: [ 0, 2, 4, 6, 8, 10 ] when: item > 5通常loop語句會(huì)結(jié)合各式各樣的filter去使用,例如“ ?loop: “{ { [\'alice\', \'bob\'] |product([\'clientdb\', \'employeedb\', \'providerdb\'])|list }}””,這個(gè)例子和with_nested語句起到一樣的效果。也就是說舊版本的with_ + lookup() 所能實(shí)現(xiàn)的,新版本的loop+filter同樣能實(shí)現(xiàn)。
5. 執(zhí)行順序
一般playbook里的task執(zhí)行順序和python一樣,由上至下,定義的順序即執(zhí)行的順序。同樣的,使用include*和import*導(dǎo)入playbook或tasks也會(huì)安照導(dǎo)入順序執(zhí)行。
5.1 per_tasks和post_tasks
當(dāng)playbook中有使用roles導(dǎo)入task和自定義tasks時(shí),我們會(huì)發(fā)現(xiàn)ansible總會(huì)先執(zhí)行roles導(dǎo)入的task,然后執(zhí)行自定義的tasks,例如:
- hosts: localhostgather_facts: novars:- ff: 1- gg: 2tasks:- debug:var: ffroles:- role: role_B?輸出結(jié)果:
? lab-ansible ansible-playbook playbooks/roles_vars.ymlPLAY [localhost] ***************************************************************TASK [role_B : debug] ********************************************************** ok: [localhost] => {"a": 2 }TASK [debug] ******************************************************************* ok: [localhost] => {"ff": 1 }PLAY RECAP ********************************************************************* localhost : ok=2 changed=0 unreachable=0 failed=0從上面示例發(fā)現(xiàn),雖然我們將tasks定義在了前面,但是tasks任務(wù)還是在roles任務(wù)之后執(zhí)行。此時(shí)我們可以使用pre_task和post_task來強(qiáng)制指定執(zhí)行順序,例如:
--- - hosts: localhostgather_facts: novars:- ff: 1- gg: 2pre_tasks:- import_role:name: role_Avars:age: 23roles:- role: role_Btasks:- debug:var: ffpost_tasks:- debug:var: gg總結(jié)下playbook里任務(wù)的執(zhí)行順序:
-
使用“pre_tasks:”定義的任務(wù)
-
使用“roles:”引用的任務(wù)
-
使用“tasks:”自定義的任務(wù)
-
使用“post_tasks”定義的任務(wù)
5.2 handlers
在部署應(yīng)用時(shí),通常的步驟是安裝軟件包==>更改配置文件==>初始化數(shù)據(jù)庫==>啟動(dòng)(重啟)服務(wù);升級(jí)的步驟一般是:升級(jí)軟件包==>更改配置文件==>初始化數(shù)據(jù)庫==>重啟服務(wù)。我們發(fā)現(xiàn)不管是新部署還是升級(jí),最后一步都是要重新加載程序的,也就是說當(dāng)我們升級(jí)了軟件或者更改了配置文件都需要重啟一下應(yīng)用。
為了實(shí)現(xiàn)觸發(fā)服務(wù)重啟,ansible使用handlers方法定義重啟的動(dòng)作,handlers并不是每次執(zhí)行playbook都會(huì)觸發(fā),而是某些指定資源狀態(tài)改變時(shí)才會(huì)觸發(fā)指定的handlers(這里使用“資源”一詞借鑒于puppet)。
示例如下:
- name: template configuration filetemplate:src: template.j2dest: /etc/foo.confnotify:- restart memcached- restart apache上面的示例中,當(dāng)/etc/foo.conf文件內(nèi)容有改動(dòng)時(shí)(返回changed),會(huì)觸發(fā)重啟memcached和apache服務(wù),如果文件內(nèi)容沒有變化時(shí)(返回ok),則不會(huì)觸發(fā)handlers。
ansible執(zhí)行過程中并不會(huì)立即觸發(fā)handlers動(dòng)作,而是以play為單位,一個(gè)play執(zhí)行完后最后才會(huì)觸發(fā)handlers。
這樣設(shè)計(jì)也是很合理的,試想在一個(gè)play內(nèi),如果觸發(fā)一次就執(zhí)行一次handlers,那么除了最后一次的重啟,前面觸發(fā)的重啟都是無用功。
另外需要注意的一點(diǎn)是,handlers觸發(fā)的執(zhí)行順序是按照定義順序執(zhí)行,而不是按照notify指定的順序執(zhí)行。
當(dāng)然如果我們想要立即觸發(fā),也是可以的,在play定義“- meta: flush_handlers”即可。
另外需要注意的一點(diǎn)是,handlers觸發(fā)的執(zhí)行順序是按照定義順序執(zhí)行,而不是按照notify指定的順序執(zhí)行。
6.參考鏈接?
- https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#
- https://docs.ansible.com/ansible/2.6/user_guide/playbooks_variables.html
- https://docs.ansible.com/ansible/2.6/user_guide/playbooks_reuse.html
- https://docs.ansible.com/ansible/2.6/user_guide/playbooks_templating.html
- https://docs.ansible.com/ansible/2.6/user_guide/playbooks_conditionals.html
- https://docs.ansible.com/ansible/2.6/user_guide/playbooks_loops.html
?
歡迎大家關(guān)注我的公眾號(hào):
轉(zhuǎn)載于:https://www.cnblogs.com/mauricewei/p/10053649.html
總結(jié)
以上是生活随笔為你收集整理的ansible基础-playbooks的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 51nod 1073 约瑟夫环
- 下一篇: 作用域面试题