之前詳細介紹了Ansible的安裝, 配置, 以及Ansible常用模塊的使用. 下面對Ansible的playbook用法做一小結。
為什么引入playbook?
一般運維人員完成一個任務, 比如安裝部署一個httpd服務會需要多個模塊(一個模塊也可以稱之為task)提供功能來完成。而playbook就是組織多個task的容器,它的實質就是一個文件,有着特定的組織格式,它采用的語法格式是YAML(Yet Another Markup Language)。YAML語法能夠簡單的表示散列表,字典等數據結構。簡單來說, playbook是由一個或多個模塊組成的,使用多個不同的模塊,完成一件事情。
Ansible核心功能
- pyYAML用於ansible編寫劇本所使用的語言格式(saltstack---python);
- rsync-ini語法, sersync-xml語法, nsible-pyYAML語法;
- paramiko遠程連接與數據傳輸;
- Jinja2用於編寫ansible的模板信息;
YAML三板斧
縮進: YAML使用一個固定的縮進風格表示層級結構,每個縮進由兩個空格組成, 不能使用tabs;
冒號: 以冒號結尾的除外,其他所有冒號后面所有必須有空格;
短橫線: 表示列表項,使用一個短橫杠加一個空格。多個項使用同樣的縮進級別作為同一列表;
YAML基本語法
Ansible-playbook采用YAML語法編寫。連續的項目(即列表)用 -減號來表示,key/value(字典)用冒號:分隔。
列表:每一個列表成員前面都要有一個短橫線和一個空格
fruits:
- Apple
- Orange
- Strawberry
- Mango
或者:
fruits: ['Apple', 'Orange', 'Strawberry', 'Mango']
字典:每一個成員由鍵值對組成,注意冒號后面要有空格
martin:
name: Martin D'vloper
job: Developer
skill: Elite
或者
martin: {name: Martin D'vloper, job: Developer, skill: Elite}
列表和字典可以混合使用
- martin:
name: Martin D'vloper
job: Developer
skills:
- python
- perl
- pascal
- tabitha:
name: Tabitha Bitumen
job: Developer
skills:
- lisp
- fortran
- erlang
示例如下:
[root@localhost ~]# cat httpd.yaml
---
- hosts: control-node #將要執行任務的主機,已經在hosts文件中定義好了,可是單個主機或主機組
remote_user: root #在目標主機上執行任務時的用戶身份
vars:
- pkg: httpd
tasks:
- name: "install httpd package."
yum: name={{ pkg }} state=installed
- name: "copy httpd configure file to remote host."
copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify: restart httpd #當這個任務執行狀態發生改變時,觸發handlers執行.
- name: "boot httpd service."
service: name=httpd state=started
handlers: #handlers與tasks是同一級別
- name: restart httpd
service: name=httpd state=restarted
playbook語法特性
1. 以 --- (三個減號)開始,必須頂行寫;
2. 次行開始寫Playbook的內容,但是一般要求寫明該playbook的功能;
3. 嚴格縮進,並且不能用Tab鍵縮進;
4. 縮進級別必須是一致的,同樣的縮進代表同樣的級別,程序判別配置的級別是通過縮進結合換行來實現的;
5. K/V的值可同行寫,也可換行寫。同行使用 :分隔,換行寫需要以 - 分隔;
playbook基礎組件
Hosts:運行執行任務(task)的目標主機
remote_user:在遠程主機上執行任務的用戶
tasks:任務列表
handlers:任務,與tasks不同的是只有在接受到通知時才會被觸發
templates:使用模板語言的文本文件,使用jinja2語法。
variables:變量,變量替換{{ variable_name }}
整個playbook是以task為中心,表明要執行的任務。hosts和remote_user表明在遠程主機以何種身份執行,其他組件讓其能夠更加靈活。下面介紹插件:
1. variable
變量定義在資產 (inventory) 中, 默認就是/etc/ansible/hosts文件中
主機變量: 192.168.200.136 http_port=808 maxRequestsPerChild=808 192.168.200.137 http_port=8080 maxRequestsPerChild=909 主機組變量: [websers] 192.168.200.136 192.168.200.137 [websers:vars] ntp_server=ntp.exampl.com proxy=proxy.exampl.com
變量定義在playbook中
- hosts: webservers
vars:
http_port: 80
使用facts變量
facts變量是由setup模塊獲取遠程主機的信息。 用法: # ansible 192.168.200.136 -m setup
在roles中定義變量, 這個后面會介紹到.
ansible-playbook 命令中傳入參數
使用 -e選項傳入參數 # ansible-playbook 192.168.200.136 -e "httpd_port=808" httpd04.yml
變量的引用
{{ var_name }}
2. templates
它是一個模塊功能,與copy不同的是他的文本文件采用了jinga2語法,jinga2基本語法如下:
字面量:
字符串:使用單引號或雙引號
數字:整型,浮點數
列表:{item1,item2,...}
字典:{key1:value1,key2:value2,...}
布爾型:true/false
算術運算:
+,-,*,/,//,%,**
比較運算:
==,!=,>,>=,<,<=
邏輯運算:
and,or,not
注意:template只能在palybook中使用。
3. tasks
執行的模塊命令
格式: action:模塊參數(此種方式只在較新的版本中出現) module:參數(已鍵值對的形式出現) 每一個task都有一個名稱,用於標記此任務。任務示例: name: install httpd yum: name=httpd state=present 注意:shell和command沒有參數,可在后面直接跟命令 shell: ss -tnl | grep :80 1)某任務的運行狀態為changed后,可通過相應的notify通知相應的handlers 2)任務可以通過tags打標簽,然后通過palybook命令-t選項調用.
playbook命令及調用方式
用法:
ansible-playbook <filename.yml> ... [options]
<filename.yml>: yaml格式的playbook文件路徑,必須指明
[options]: 選項
Options:
--ask-vault-pass
#ask for vault password
#加密playbook文件時提示輸入密碼
-C, --check
#don't make any changes; instead, try to predict some of the changes that may occur
#模擬執行,不會真正在機器上執行(查看執行會產生什么變化)。即並不在遠程主機上執行,只是測試。
-D, --diff
#when changing (small) files and templates, show the differences in those files; works great with --check
#當更新的文件數及內容較少時,該選項可顯示這些文件不同的地方,該選項結合-C用會有較好的效果
-e EXTRA_VARS, --extra-vars=EXTRA_VARS
#set additional variables as key=value or YAML/JSON
#在Playbook中引入外部參數變量
--flush-cache
#clear the fact cache
#清理fact緩存,將fact清除到的遠程主機緩存
--force-handlers
#run handlers even if a task fails
#強制運行handlers的任務,即使在任務失敗的情況下
-f FORKS, --forks=FORKS
#specify number of parallel processes to use(default=5)
#並行任務數。FORKS被指定為一個整數,默認是5
-h, --help
#show this help message and exit
#打開幫助文檔API
-i INVENTORY, --inventory-file=INVENTORY
#specify inventory host path (default=/etc/ansible/hosts) or comma separated host list.
#指定要讀取的Inventory清單文件
-l SUBSET, --limit=SUBSET
#further limit selected hosts to an additional pattern
#限定執行的主機范圍
--list-hosts
#outputs a list of matching hosts; does not execute anything else
#列出執行匹配到的主機,但並不會執行任何動作。
--list-tags
#list all available tags
#列出所有可用的tags
--list-tasks
#list all tasks that would be executed
#列出所有即將被執行的任務
-M MODULE_PATH, --module-path=MODULE_PATH
#specify path(s) to module library (default=None)
#要執行的模塊的路徑
--new-vault-password-file=NEW_VAULT_PASSWORD_FILE
#new vault password file for rekey
#
--output=OUTPUT_FILE
#output file name for encrypt or decrypt; use - for stdout
#
--skip-tags=SKIP_TAGS
#only run plays and tasks whose tags do not match these values
#跳過指定的tags任務
--start-at-task=START_AT_TASK
#start the playbook at the task matching this name
#從第幾條任務(START_AT_TASK)開始執行
--step
#one-step-at-a-time: confirm each task before running
#逐步執行Playbook定義的任務,並經人工確認后繼續執行下一步任務
--syntax-check
#perform a syntax check on the playbook, but do not execute it
#檢查Playbook中的語法書寫,並不實際執行
-t TAGS, --tags=TAGS
#only run plays and tasks tagged with these values
#指定執行該tags的任務
--vault-password-file=VAULT_PASSWORD_FILE
#vault password file
#
-v, --verbose
#verbose mode (-vvv for more, -vvvv to enable connection debugging)
#執行詳細輸出
--version
#show program's version number and exit
#顯示版本
############Connection Options,即下面時連接權限############
control as whom and how to connect to hosts
-k, --ask-pass
#ask for connection password
#
--private-key=PRIVATE_KEY_FILE, --key-file=PRIVATE_KEY_FILE
#use this file to authenticate the connection
#
-u REMOTE_USER, --user=REMOTE_USER
#connect as this user (default=None)
#指定遠程主機以USERNAME運行命令
-c CONNECTION, --connection=CONNECTION
#connection type to use (default=smart)
#指定連接方式,可用選項paramiko (SSH)、ssh、local,local方式常用於crontab和kickstarts
-T TIMEOUT, --timeout=TIMEOUT
#override the connection timeout in seconds(default=10)
#SSH連接超時時間設定,默認10s
--ssh-common-args=SSH_COMMON_ARGS
#specify common arguments to pass to sftp/scp/ssh (e.g.ProxyCommand)
#
--sftp-extra-args=SFTP_EXTRA_ARGS
#specify extra arguments to pass to sftp only (e.g. -f, -l)
#
--scp-extra-args=SCP_EXTRA_ARGS
#specify extra arguments to pass to scp only (e.g. -l)
#
--ssh-extra-args=SSH_EXTRA_ARGS
#specify extra arguments to pass to ssh only (e.g. -R)
#
############Privilege Escalation Options, 即下面時權限提升權限############
control how and which user you become as on target hosts
-s, --sudo
#run operations with sudo (nopasswd) (deprecated, use become)
#相當於Linux系統下的sudo命令
-U SUDO_USER, --sudo-user=SUDO_USER
#desired sudo user (default=root) (deprecated, use become)
#使用sudo,相當於Linux下的sudo命令
-S, --su
#run operations with su (deprecated, use become)
#
-R SU_USER, --su-user=SU_USER
#run operations with su as this user (default=root)(deprecated, use become)
-b, --become
#run operations with become (does not imply password prompting)
#
--become-method=BECOME_METHOD
#privilege escalation method to use (default=sudo),valid choices: [ sudo | su | pbrun | pfexec | doas |dzdo | ksu | runas ]
#
--become-user=BECOME_USER
#run operations as this user (default=root)
#
--ask-sudo-pass
#ask for sudo password (deprecated, use become)
#傳遞sudo密碼到遠程主機,來保證sudo命令的正常運行
--ask-su-pass
#ask for su password (deprecated, use become)
#
-K, --ask-become-pass
#ask for privilege escalation password
#
ansible-playbook需要注意的兩個命令
1)檢查語法,只檢查是否是yaml語法格式。並不做邏輯校驗。(記住這個要經常使用, 它是判斷語法是否正確!!!)
# ansible-playbook --syntax-check kevin.yml
2)模擬執行(不是真的執行)
# ansible-playbook -C kevin.yml
關閉Facts
如果不需要使用主機的任何fact數據,可以選擇關閉fact數據的獲取,這樣有利於增強Ansible面對大量系統的push模塊。
在playbook中關閉Facts方法(gather_facts: no):
--- - hosts: webserver gather_facts: no
palybook書寫格式
--- # 也可以不使用這一行。可以省略。
- hosts: 172.16.60.211 #處理指定服務器. - (空格)hosts:(空格)172.16.20.211
task: #劇本所要干的事情; (空格)(空格)task:
- name: #(兩個空格)-(空格)name。
command: echo hello clsn linux #(四個空格)command:(空格)
需要注意:
Task任務里的name可以省略不寫,將-(空格)放到下一行模塊牆面。例如:
---
- hosts: 172.16.60.211
task:
- command: echo hello clsn linux
小示例:
[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
remote_user: root
gather_facts: no
tasks:
- file: path=/opt/task1.txt state=touch
palybook格式示例
[root@ansible-server ~]# vim /etc/ansible/test.yaml
- hosts: 172.16.60.213
tasks:
- name: Install Rsync
yum: name=rsync state=installed
playbook檢查方法
[root@ansible-server ~]# ansible-playbook --syntax-check /etc/ansible/test.yaml
playbook: /etc/ansible/test.yaml
[root@ansible-server ~]# ansible-playbook -C /etc/ansible/test.yaml
PLAY [172.16.60.213] *******************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************
ok: [172.16.60.213]
TASK [Install Rsync] *******************************************************************************************************************
ok: [172.16.60.213]
PLAY RECAP *****************************************************************************************************************************
172.16.60.213 : ok=2 changed=0 unreachable=0 failed=0
上面兩個檢查命令, 第一個是進行playbook劇本配置信息語法檢查; 第二個是模擬playbook劇本執行(彩排)
palybook劇本文件示例
ansible-playbook編寫內容擴展:劇本任務編寫多個任務
- hosts: all
tasks:
- name: restart-network
cron: name='restart network' minute=00 hour=00 job='/usr/sbin/ntpdate time.nist.gov >/dev/null 2>&1'
- name: sync time
cron: name='sync time' minute=*/5 job="/usr/sbin/ntpdate pool.ntp.com >/dev/null 2>&1"
劇本編寫內容擴展:劇本任務編寫多個主機
- hosts: 172.16.60.7
tasks:
- name: restart-network
cron: name='restart network' minute=00 hour=00 job='/usr/sbin/ntpdate time.nist.gov >/dev/null 2>&1'
- name: sync time
cron: name='sync time' minute=*/5 job="/usr/sbin/ntpdate pool.ntp.com >/dev/null 2>&1"
- hosts: 172.16.60.31
tasks:
- name: show ip addr to file
shell: echo $(hostname -i) >> /tmp/ip.txt
playbook劇本編寫方式
- 多主機單任務編寫方式
- 多主機多任務編寫方式
- 不同主機多任務編寫方式
來看一個比較完整的ansible的yml文件寫法:
---
- host: webservers ###要管理的遠程服務器組名稱, 服務器地址維護在/etc/ansible/hosts 里, 也可以直接寫地址
vars:
port: 8081 ###定義了一個變量 端口號
remote_user: root ###遠程登錄后用什么用戶執行
pre_tasks: ###執行正式 task 之前執行的任務
- name: pre task ###任務名稱
shell: echo 'execute pre task' ###執行一行 shell 命令, 支持 >> 等符號
roles: ###引入 roles, 可以理解為引用了一個其他項目 ansible 包, 引用的 roles 可以是另一個完整的 ansible 腳本
- role: my_role ###要引用的 role 名稱
when: "ansible_os_family == 'RedHat'" ###判斷條件, ansible_os_family 是一個內置變量, 可直接使用
tasks: ###按順序執行以下 task
- include: my_tasks/some_task.yml ###可以引入其他 yml 文件
- name: get hostname ###這是一個 task, 名稱
command: cat log.log ###執行一行 command , 和 shell 類似, 但是不支持 >> 等操作符
register: result ###執行的結果, 設到 result 這個變量中, 后面可以使用
- name: set hostname
shell: cat {{result.stdout}} >> host.text
- name: task1
command: echo 'execute task1'
- name: task2 start apache
service: ###啟動 httpd 服務器, service 是一個 ansible 內置模塊, 讀者可以自行查看更多模塊, 包括下載復制等等
name: httpd
state: started
tags:
- apache ###這是一個標簽, 可以用 ansible-playbook main.yml --tags "apache" 指定只執行這個任務
- name: copy and set value of index.html
template: ###這是一個復制方法, 也叫模塊, 並且.j2文件中可以使用{{}}來設置需要替換的變量
src: templates/index.html.j2
dest: /etc/httpd/index.html
notify: ###喚醒執行后面的 handlers 中名字叫 restart apache 的任務
- restart apache
post_tasks: ###最后需要執行的任務
- name: posy task
shell: echo 'execute post task'
handlers:
- name: restart apache
debug: ###這是一個打印模塊
msg: start restart apche
palybook劇本中的方法
1. handlers 任務觸發
在需要被監控的任務(tasks)中定義一個notify,只有當這個任務被執行時,才會觸發notify對應的handlers去執行相應操作。例如配置文件被修改后,有可能需要重啟程序,此時我們可以配置一個handlers,類似觸發器。注意:handlers下的name名稱必須要和它對應的notify名稱相同!否則不會執行!!
[root@localhost ~]# cat httpd.yaml
---
- hosts: control-node
remote_user: root
vars:
- pkg: httpd
tasks:
- name: "install httpd package."
yum: name={{ pkg }} state=installed
- name: "copy httpd configure file to remote host."
copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify: restart httpd
- name: "boot httpd service."
service: name=httpd state=started
handlers:
- name: restart httpd
service: name=httpd state=restarted
######## 在使用handlers的過程中,需要注意下面幾點 ########
1. handlers只有在其所在的任務被執行完時,它才會被運行;如果一個任務中定義了notify調用Handlers,但由於條件判斷等原因,該任務未被執行,則Handlers同樣不會被執行。
2. handlers只會在Play的末尾運行一次;如果想在一個Playbook的中間運行handlers,則需要使用meta模塊來實現,例如:-meta: flush_handlers。
3. 可以直接在Handlers中使用notify選項,實現Handlers調用Handlers。
4. 可以使用listen關鍵字,在一個tasks任務中一次性notify多個handler。即將多個handler分為"一組",使用相同的"組名"即可,當notify對應的值為"組名"時,"組"內的所有handler都會被notify。
5. 如果一個Play在運行到調用handlers的語句之前失敗了,那么這個handlers將不會被執行。但是可以使用mega模塊的--force-handlers選項來強制執行handlers,即使在handlers所在Play中途運行失敗也能執行。需要注意:--force-handlers參數主要針對即使playbook執行失敗,也要執行代碼塊成功了的handlers(即執行成功的task任務), 如果代碼塊本身執行失敗(即執行失敗的task任務),那么它所對應的handlers應當不會被執行!
handlers可以理解成另一種tasks,handlers是另一種"任務列表",可以理解handlers和tasks是"平級關系",所以他們的縮進相同。handlers的任務會被tasks中的任務進行"調用",但是,被"調用"並不意味着一定會執行,只有當tasks中的任務"真正執行"以后,handlers中被調用的任務才會執行,如果tasks中的任務並沒有做出任何實際的操作,那么handlers中的任務即使被"調用",也並不會執行。handlers中可以有多個任務,被tasks中不同的任務notify。
場景1:headlers在所有tasks任務被執行完時才執行。
[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
remote_user: root
become: yes
become_method: sudo
tasks:
- name: make file task1
file: path=/opt/task1.txt state=touch
notify: task1
- name: make file task2
file: path=/opt/task2.txt state=touch
notify: task2
handlers:
- name: task1
file: path=/opt/task1.txt mode=777 owner=root group=root
- name: task2
file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes
執行結果:
[root@localhost ansible]# ansible-playbook haha.yaml
PLAY [test_host] *********************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.233]
ok: [172.16.60.234]
TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
TASK [make file task2] ***************************************************************************************************************************
changed: [172.16.60.233]
changed: [172.16.60.234]
RUNNING HANDLER [task1] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
RUNNING HANDLER [task2] **************************************************************************************************************************
changed: [172.16.60.233]
changed: [172.16.60.234]
PLAY RECAP ***************************************************************************************************************************************
172.16.60.233 : ok=5 changed=4 unreachable=0 failed=0
172.16.60.234 : ok=5 changed=4 unreachable=0 failed=0
從上面運行結果看出,Handlers執行的順序與Handlers在playbook中定義的順序是相同的,與"handler"被notify的順序無關。
場景2:使用meta模塊,headlers會在它所對應的task任務執行完后立即被觸發並執行,即在playbook的中間環節運行。
默認情況下,所有的task執行完畢后,才會執行各個handles,並不是執行完某個task后,立即執行相應的handler,如果想要在執行完某些task以后立即執行對應的handlre,那么需要使用meta模塊。
[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
remote_user: root
become: yes
become_method: sudo
tasks:
- name: make file task1
file: path=/opt/task1.txt state=touch
notify: task1
- meta: flush_handlers
- name: make file task2
file: path=/opt/task2.txt state=touch
notify: task2
handlers:
- name: task1
file: path=/opt/task1.txt mode=777 owner=root group=root
- name: task2
file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes
執行結果:
[root@localhost ansible]# ansible-playbook haha.yaml
PLAY [test_host] *********************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.234]
ok: [172.16.60.233]
TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
RUNNING HANDLER [task1] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
TASK [make file task2] ***************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
RUNNING HANDLER [task2] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
PLAY RECAP ***************************************************************************************************************************************
172.16.60.233 : ok=5 changed=4 unreachable=0 failed=0
172.16.60.234 : ok=5 changed=4 unreachable=0 failed=0
上面使用了meta模塊后,注意它的執行順序於場景1做下對比!
場景3:Handlers調用Handlers
若實現Handlers調用Handlers,則直接在Handlers中使用notify選項即可以。
[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
remote_user: root
become: yes
become_method: sudo
tasks:
- name: make file task1
file: path=/opt/task1.txt state=touch
notify: task1
- name: make file task2
file: path=/opt/task2.txt state=touch
handlers:
- name: task1
file: path=/opt/task1.txt mode=777 owner=root group=root
notify: task2
- name: task2
file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes
執行結果:
[root@localhost ansible]# ansible-playbook haha.yaml
PLAY [test_host] *********************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.234]
ok: [172.16.60.233]
TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.233]
changed: [172.16.60.234]
TASK [make file task2] ***************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
RUNNING HANDLER [task1] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
RUNNING HANDLER [task2] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
PLAY RECAP ***************************************************************************************************************************************
172.16.60.233 : ok=5 changed=4 unreachable=0 failed=0
172.16.60.234 : ok=5 changed=4 unreachable=0 failed=0
注意:上面執行的順序是:make file task1 > make file task2 > task1 > task2
====================================================================
也可以改成下面的方式:實現Handlers調用Handlers
[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
remote_user: root
become: yes
become_method: sudo
tasks:
- name: make file task1
file: path=/opt/task1.txt state=touch
notify: task1
handlers:
- name: task1
file: path=/opt/task1.txt mode=777 owner=root group=root
notify: task2
- name: task2
file: path=/opt/task2.txt state=touch
notify: task3
- name: task3
file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes
執行結果:
[root@localhost ansible]# ansible-playbook haha.yaml
PLAY [test_host] *********************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.233]
ok: [172.16.60.234]
TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.233]
changed: [172.16.60.234]
RUNNING HANDLER [task1] **************************************************************************************************************************
changed: [172.16.60.233]
changed: [172.16.60.234]
RUNNING HANDLER [task2] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
RUNNING HANDLER [task3] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
PLAY RECAP ***************************************************************************************************************************************
172.16.60.233 : ok=5 changed=4 unreachable=0 failed=0
172.16.60.234 : ok=5 changed=4 unreachable=0 failed=0
注意:上面的執行順序是:make file task1 > task1 > task2 > task3
場景4:使用listen關鍵字,在一個tasks任務中一次性notify多個handler
怎么才能一次性notify多個handler呢?如果嘗試將多個handler使用相同的name呢?其實這樣並不可行!因為當多個handler的name相同時,只有一個handler會被執行。要想實現一次notify多個handler,需要借助一個關鍵字,它就是"listen",可以把listen理解成"組名",可以把多個handler分成"組",當需要一次性notify多個handler時,只要將多個handler分為"一組",使用相同的"組名"即可,當notify對應的值為"組名"時,"組"內的所有handler都會被notify。需要注意:listen的名稱要和notify名稱保持一致!
[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
remote_user: root
become: yes
become_method: sudo
tasks:
- name: make file task1
file: path=/opt/task1.txt state=touch
notify: group1_handler
handlers:
- name: task1
listen: group1_handler
file: path=/opt/task1.txt mode=777 owner=root group=root
- name: task2
listen: group1_handler
file: path=/opt/task1.txt src=/opt/task1.txt dest=/opt/heihei state=link force=yes
- name: task3
listen: group1_handler
shell: echo "this is test,haha...." >> /opt/task1.txt
執行結果:
[root@localhost ansible]# ansible-playbook haha.yaml
PLAY [test_host] *********************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.233]
ok: [172.16.60.234]
TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
RUNNING HANDLER [task1] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
RUNNING HANDLER [task2] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
RUNNING HANDLER [task3] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
PLAY RECAP ***************************************************************************************************************************************
172.16.60.233 : ok=5 changed=4 unreachable=0 failed=0
172.16.60.234 : ok=5 changed=4 unreachable=0 failed=0
場景5:使用--force-handlers選項來強制執行handlers
當playbook劇本執行失敗以后,handlers可能並沒有被觸發,也就不會執行了!如果想不管task任務是否成功執行,都強制執行handlers。在這個時候,可以在執行playbook的時候,添加--force-handlers來強制執行handlers!但是必須要注意的是:--force-handlers參數主要針對即使playbook執行失敗,也要執行代碼塊成功了的handlers(即執行成功的task任務), 如果代碼塊本身執行失敗(即執行失敗的task任務),那么它所對應的handlers應當不會被執行!
[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
remote_user: root
become: yes
become_method: sudo
tasks:
- name: make file task1
file: path=/opt/task1.txt state=touch
notify: task1
- name: make file task2
file: path=/opt/kevin/task2.txt state=touch
notify: task2
handlers:
- name: task1
file: path=/opt/task1.txt mode=777 owner=root group=root
- name: task2
shell: ln -s /opt/task1.txt /opt/task2.txt
執行結果:
[root@localhost ansible]# ansible-playbook haha.yaml
PLAY [test_host] *********************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.234]
ok: [172.16.60.233]
TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.233]
changed: [172.16.60.234]
TASK [make file task2] ***************************************************************************************************************************
fatal: [172.16.60.234]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "Traceback (most recent call last):\r\n File \"/tmp/ansible_iNMDpU/ansible_module_file.py\", line 474, in <module>\r\n main()\r\n File \"/tmp/ansible_iNMDpU/ansible_module_file.py\", line 448, in main\r\n open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n", "msg": "MODULE FAILURE", "rc": 0}
fatal: [172.16.60.233]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "Traceback (most recent call last):\r\n File \"/tmp/ansible_OvTacW/ansible_module_file.py\", line 474, in <module>\r\n main()\r\n File \"/tmp/ansible_OvTacW/ansible_module_file.py\", line 448, in main\r\n open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n", "msg": "MODULE FAILURE", "rc": 0}
RUNNING HANDLER [task1] **************************************************************************************************************************
to retry, use: --limit @/etc/ansible/haha.retry
PLAY RECAP ***************************************************************************************************************************************
172.16.60.233 : ok=2 changed=1 unreachable=0 failed=1
172.16.60.234 : ok=2 changed=1 unreachable=0 failed=1
如上執行結果,由於/opt/kevin目錄不存在,導致task的第二個任務執行失敗,這個時候handler根本沒有被觸發,也就不會執行。
即使第一個任務執行成功,但是它對應的第一個handler也不會被執行!!
###################################################################################
接下來使用--force-handlers選項來強制執行handlers(強制執行的是:成功執行的task對應的handler)
[root@localhost ansible]# ansible-playbook haha.yaml --force-handlers
PLAY [test_host] *********************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.234]
ok: [172.16.60.233]
TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
TASK [make file task2] ***************************************************************************************************************************
fatal: [172.16.60.233]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "Traceback (most recent call last):\r\n File \"/tmp/ansible_rEJQHm/ansible_module_file.py\", line 474, in <module>\r\n main()\r\n File \"/tmp/ansible_rEJQHm/ansible_module_file.py\", line 448, in main\r\n open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n", "msg": "MODULE FAILURE", "rc": 0}
fatal: [172.16.60.234]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "Traceback (most recent call last):\r\n File \"/tmp/ansible_7CDxpp/ansible_module_file.py\", line 474, in <module>\r\n main()\r\n File \"/tmp/ansible_7CDxpp/ansible_module_file.py\", line 448, in main\r\n open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n", "msg": "MODULE FAILURE", "rc": 0}
RUNNING HANDLER [task1] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
to retry, use: --limit @/etc/ansible/haha.retry
PLAY RECAP ***************************************************************************************************************************************
172.16.60.233 : ok=3 changed=2 unreachable=0 failed=1
172.16.60.234 : ok=3 changed=2 unreachable=0 failed=1
如上執行結果,即使playbook執行中有task任務執行失敗,但是執行成功的task任務所調用的handler依然會被強制觸發並執行!但是執行失敗的task任務所調用的handler依然不會被執行。
即handlers中的task1會被執行,task2不會被執行!
2. tags任務標簽
tags用於讓用戶選擇運行playbook中的部分代碼。ansible具有冪等性,因此會自動跳過沒有變化的部分,即便如此,有些代碼為測試其確實沒有發生變化的時間依然會非常地長。此時如果確信其沒有變化,就可以通過tags跳過此些代碼片斷。tags可以看作是ansible的任務控制!
ansible的標簽(Tags)功能可以給角色(Roles)、文件、單獨的任務,甚至整個Playbook打上標簽,然后利用這些標簽來指定要運行Playbook中的個別任務,或不執行指定的任務。如果有一個很大的playbook劇本,而只想運行playbook其中的某個或部分task任務,而不是運行playbook中所有的任務,這個時候tags是你的最佳選擇。
2.1 ansible支持"tags:"屬性,執行playbook時,可以通過兩種方式根據"tags"過濾任務:
1. 在命令行上,使用或選項"--tags或 --skip-tags",后面使用空格或"="都可以。
2. 在ansible配置設置中,使用和選項"TAGS_RUN或TAGS_SKIP";
3. 可以使用"--list-tags"查看playbook中有哪些tags會被執行;
2.2 ansible系統中內置的特殊tags(目前有5個特殊的tags)
到ansible 2.5版本以后,目前系統內置的tags有以下幾個:
always: 除非--skip-tags指定這個標簽,否則該標記為always的task一直都會執行。"--tags always"只執行標記了always的tasks;
never: 除非--tags指定了這個標簽,否則該標記為never的task一直都不會執行。"--tags never"執行標記了always和never的tasks;
tagged: --tags tagged表示執行所有有tags標簽的tasks任務,但不包括tags標簽是never的tasks任務;--skip-tags tagged表示所有有tags標簽的tasks任務都跳過,即不會執行。
untagged: --tags untagged表示執行所有沒有tags標簽的tasks任務和tags標簽為always的tasks任務;--skip-tags untagged效果相反!
all:--tags all表示執行所有的tags標簽為非never的task,包括有tags標簽和無tags標簽的tasks。
執行ansible-playbook命令時,使用下面兩個參數的含義(自定義的tags可以是單個,也可以是多個,多個之間使用逗號隔開):
"--tags 自定義的tag" 表示執行tags為指定的標簽名的tasks和tags為always的tasks。如果執行命令ansible-playbook site.yml 時不指定tags,則會執行所有tags為非never的tasks
"--skip-tags 自定義tag" 表示執行所有非指定tag和非never的tasks
2.3 tags標簽配置語法有下面三種:
語法一: tags: - tag_test 語法二: tags: tag_test 語法三: tags: ['tag_test']
2.3 ansible的tags使用
1)最常見的使用形式。一個task任務添加一個tags標簽。
官方示例如下:
[root@localhost ansible]# vim example.yml
---
- hosts: all
remote_user: root
gather_facts: no
tasks:
- yum: name={{ item }} state=installed
with_items:
- httpd
- memcached
tags:
- packages
- template: src=templates/src.j2 dest=/etc/foo.conf
tags:
- configuration
此時如果希望只run其中的某個task,則run的時候指定tags即可。可以運行多個tags,中間使用逗號隔開;也可以運行單個tags。
[root@localhost ansible]# ansible-playbook example.yml --tags "configuration,packages" [root@localhost ansible]# ansible-playbook example.yml --tags configuration [root@localhost ansible]# ansible-playbook example.yml --tags packages 或者 [root@localhost ansible]# ansible-playbook example.yml --tags="configuration,packages" [root@localhost ansible]# ansible-playbook example.yml --tags=configuration [root@localhost ansible]# ansible-playbook example.yml --tags=packages
相反,也可以使用--skip-tags跳過某個task任務。
[root@localhost ansible]# ansible-playbook example.yml --skip-tags configuration 或者 [root@localhost ansible]# ansible-playbook example.yml --skip-tags=configuration
具體看下面示例(tags三種語法都用上):
[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
remote_user: root
gather_facts: no
tasks:
- name: task1
file: path=/opt/task1.txt state=touch
tags: make_task1
- name: task2
file: path=/opt/task2.txt state=touch
tags:
- make_task2
- name: task3
file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes
tags: ['link_task3']
只運行make_task1標簽的task任務
[root@localhost ansible]# ansible-playbook haha.yaml --tags make_task1
PLAY [test_host] *********************************************************************************************************************************
TASK [task1] *************************************************************************************************************************************
changed: [172.16.60.234]
PLAY RECAP ***************************************************************************************************************************************
172.16.60.234 : ok=1 changed=1 unreachable=0 failed=0
運行多個tags
[root@localhost ansible]# ansible-playbook haha.yaml --tags make_task1,make_task2
PLAY [test_host] *********************************************************************************************************************************
TASK [task1] *************************************************************************************************************************************
changed: [172.16.60.234]
TASK [task2] *************************************************************************************************************************************
changed: [172.16.60.234]
PLAY RECAP ***************************************************************************************************************************************
172.16.60.234 : ok=2 changed=2 unreachable=0 failed=0
跳過make_task2標簽的任務,其他任務正常執行
[root@localhost ansible]# ansible-playbook haha.yaml --skip-tags make_task2
PLAY [test_host] *********************************************************************************************************************************
TASK [task1] *************************************************************************************************************************************
changed: [172.16.60.234]
TASK [task3] *************************************************************************************************************************************
changed: [172.16.60.234]
PLAY RECAP ***************************************************************************************************************************************
172.16.60.234 : ok=2 changed=2 unreachable=0 failed=0
2)一個task任務添加多個tags標簽。
上面是一個task任務添加一個tags標簽,其實一個task任務可以添加多個標簽,而且不同的task任務可以使用相同的tags標簽。
一個任務添加多個tags標簽的語法仍然也有三種:
語法1:
tags:
- tag1
- tag2
語法2:
tags: tag1,tag2
語法3:
tags: ['tag1,tag2']
========================================================
具體示例如下:
[root@localhost ansible]# vim https.yml
---
- hosts: test_host
remote_user: root
tasks:
- name: install httpd package
tags:
- httpd
- package
yum:
name=httpd
state=latest
- name: start up httpd service
tags: httpd,service
service:
name: httpd
state: started
上面例子中每個任務都有多個標簽,而且上例中兩個任務都有一個共同的標簽,就是httpd標簽。
所以當執行"ansible-playbook httpd.yml --tags=httpd"時,上面兩個task任務都會被執行。
由於上面例子中的所有任務都有共同的httpd標簽,所以像這種情況,可以把httpd標簽提取出來並寫在play劇本中,示例如下:
[root@localhost ansible]# vim https.yml
---
- hosts: test_host
remote_user: root
tags:httpd
tasks:
- name: install httpd package
tags:
- package
yum:
name=httpd
state=latest
- name: start up httpd service
tags: ['service']
service:
name: httpd
state: started
需要注意:當tags寫在play劇本中而非寫在task任務中時,play中的所有task任務都會繼續當前paly中的tags,就像上例中,兩個任務都會繼承httpds的tag標簽,同時還擁有自己的tag標簽。
3)內置的特殊tags的用法
上面已經介紹了5個內置的特殊的tags,每個都有其自身的用意。如下以always關鍵字的tags為例:如果把任務的tags值指定為always時,那么這個任務就總是被執行,除非使用"--skip-tags"選項明確指定不執行對應任務的tags標簽。
[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
remote_user: root
gather_facts: no
tasks:
- name: task1
file: path=/opt/task1.txt state=touch
tags: make_task1
- name: task2
file: path=/opt/task2.txt state=touch
tags:
- always
- name: task3
file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes
tags: ['link_task3']
執行1:如下,雖然tags指定了執行標簽為make_task1的任務,但是由於任務2的標簽有關鍵字always,所以任務2也會被執行,這就是always的作用!
[root@localhost ansible]# ansible-playbook haha.yaml --tags=make_task1
PLAY [test_host] *********************************************************************************************************************************
TASK [task1] *************************************************************************************************************************************
changed: [172.16.60.234]
TASK [task2] *************************************************************************************************************************************
changed: [172.16.60.234]
PLAY RECAP ***************************************************************************************************************************************
172.16.60.234 : ok=2 changed=2 unreachable=0 failed=0
執行2: 只執行標簽為always的任務
[root@localhost ansible]# ansible-playbook haha.yaml --tags always
或者
[root@localhost ansible]# ansible-playbook haha.yaml --tags=always
PLAY [test_host] *********************************************************************************************************************************
TASK [task2] *************************************************************************************************************************************
changed: [172.16.60.234]
PLAY RECAP ***************************************************************************************************************************************
172.16.60.234 : ok=1 changed=1 unreachable=0 failed=0
執行3: 跳過標簽為always關鍵字的任務,這里明確指出跳過執行always標簽。
[root@localhost ansible]# ansible-playbook haha.yaml --skip-tags always
PLAY [test_host] *********************************************************************************************************************************
TASK [task1] *************************************************************************************************************************************
changed: [172.16.60.234]
TASK [task3] *************************************************************************************************************************************
ok: [172.16.60.234]
PLAY RECAP ***************************************************************************************************************************************
172.16.60.234 : ok=2 changed=1 unreachable=0 failed=0
其他四個特殊的tags標簽在這里就不做示例說明了。特殊tags標簽可以在ansible-playbook命令執行時直接使用。
4)tags標簽可以和role 結合使用
[root@localhost ansible]# cat test.yml
---
roles:
- { role: webserver, port: 5000, tags: [ 'web', 'foo' ] }
5)tags和include結合使用。
[root@localhost ansible]# cat test.yml --- - include: kevin.yml tags=web,foo
如上,對一個include任務打了兩個tags標簽,直接執行"ansible_playbook test.yml" 或 "ansible_playbook test.yml --tags=web" 或 "ansible_playbook test.yml --tags=foo" 命令則會將kevin.yml文件中所有task任務都執行。
再來看看一個include結合tags的示例:通過指定標簽(tags),來說明是安裝tomcat7還是tomcat8
tomcat.yml文件
--- - include: install_tomcat7.yml tags: tomcat7 - include: install_tomcat8.yml tags: tomcat8
install_tomcat7.yml文件
---
- name: "復制文件到遠程主機"
copy:
src={{ item.src }}
dest={{ item.dest }}
with_items:
- src: jdk-7u79-linux-x64.rpm
dest: /usr/local/src/
- src: java17.sh
dest: /etc/profile.d/
- name: "安裝jdk"
yum:
name: /usr/local/src/jdk-7u79-linux-x64.rpm
state: present
- name: "重新加載環境變量"
shell: "source /etc/profile.d/java17.sh"
- name: "復制tomcat文件到遠程服務器並解壓"
unarchive:
src=apache-tomcat-7.0.64.zip
dest=/data/
copy=yes
owner=staplesapp
group=admin
- name: "對解壓后的文件重命名"
shell: mv /data/apache-tomcat-7.0.64 /data/tomcat7
- name: "對tomcat進行相關配置"
shell: find /data/tomcat7/bin -name "*.sh" | xargs chmod +x
- name: "啟動tomcat"
shell: 'nohup /data/tomcat7/bin/startup.sh &'
install_tomcat8.yml文件
---
- name: "復制文件到遠程主機"
copy:
src={{ item.src }}
dest={{ item.dest }}
with_items:
- src: jdk-8u111-linux-x64.rpm
dest: /usr/local/src/
- src: java18.sh
dest: /etc/profile.d/
- name: "安裝jdk"
yum:
name: /usr/local/src/jdk-8u111-linux-x64.rpm
state: present
- name: "配置java環境變量"
shell: "source /etc/profile.d/java18.sh"
- name: "安裝tomcat"
unarchive:
src=apache-tomcat-8.0.30.tar.gz
dest=/data/
copy=yes
owner=staplesapp
group=admin
- name: "對解壓后的文件重命名"
shell: mv /data/apache-tomcat-8.0.30 /data/tomcat8
- name: "啟動tomcat"
shell: 'nohup /data/tomcat8/bin/startup.sh &'
下面開始執行命令:
安裝tomcat7: [root@localhost ansible]# ansible-playbook tomcat.yml --tags tomcat7 安裝tomcat8: [root@localhost ansible]# ansible-playbook tomcat.yml --tags tomcat8
這里需要特別注意:
在之前ansible版本中使用include 整合多個roles至統一入口結合tags標簽來管理roles劇本,但在ansible2.8版本之后將會刪除include語法,更改為import_playbook。如果還使用include語法也可以,只不過ansible-playbook執行結果中會有告警信息:"DEPRECATION WARNING]:'include' for playbook includes. You should use 'import_playbook' instead. This feature will be removed in version 2.8. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg."。所以,最好將上面tomcat.yml文件中的include語法改成import_playbook,如下:
[root@localhost ansible]# cat tomcat.yml --- - import_playbook: install_tomcat7.yml tags: tomcat7 - import_playbook: install_tomcat8.yml tags: tomcat8
3. include用法
如果想在playbook中重復使用任務列表,則可以使用include文件來執行此操作。 使用include的任務列表是定義系統將要實現的角色的好方法。主要清楚:ansible2.8版本之后include語法變成了import_playbook。如果還是使用include,則不會影響執行結果,只不過是有告警信息。ansible也可以將變量傳遞給include。示例如下:
示例1: 通過Include,可以在playbook中引用另一個playbook或者tasks
==============================================================
[root@localhost ansible]# cat install_MysqlAndPhp.yml
- yum:
name: mysql
state: present
- yum:
name: php-fpm
state: present
[root@localhost ansible]# cat lamp.yml
---
- hosts: test_host
remote_user: root
gather_facts: no
tasks:
- include: install_MysqlAndPhp.yml
- yum:
name: httpd
state: present
[root@localhost ansible]# cat lnmp.yml
---
- hosts: test_host
remote_user: root
gather_facts: no
tasks:
- include: install_MysqlAndPhp.yml
- yum:
name: nginx
state: present
示例2: 可以在handler中引用include
==============================================================
[root@localhost ansible]# cat test_include.yml
---
- hosts: test_host
remote_user: root
gather_facts: no
tasks:
- file:
path: /opt/ttt
state: touch
notify: test include handlers
handlers:
- name: test include handlers
include: include_handler.yml
[root@localhost ansible]# cat include_handler.yml
- debug:
msg: "task1 of handlers"
- debug:
msg: "task2 of handlers"
- debug:
msg: "task3 of handlers"
示例3: when在include中使用
==============================================================
[root@localhost ansible]# cat /etc/ansible/hosts
[db]
192.168.24.10
[app]
192.168.24.11
[root@localhost ansible]# cat install_client.yml
---
- hosts: '` hosts `'
user: ansible
sudo: yes
sudo_user:root
roles:
- install_client
[root@localhost ansible]# cat roles/install_client/tasks/main.yml
---
- include: db.yml
when: "hosts == 'db'"
- include: app.yml
when: "hosts == 'app'"
[root@localhost ansible]# cat roles/install_client/tasks/db.yml
---
- name: Touchdb file
shell: touch /tmp/db.txt
[root@localhost ansible]# cat roles/install_client/tasks/app.yml
---
- name: Touchdb file
shell: touch /tmp/db.txt
執行命令:
[root@localhost ansible]# ansible-playbook -i hosts install_client.yml --extra-vars "hosts=db"
[root@localhost ansible]# ansible-playbook -i hosts install_client.yml --extra-vars "hosts=app"
示例4: 可以在include中使用tags標簽,這個在上面已經介紹過了
==============================================================
4. role用法
角色(roles)是ansible自1.2版本開始引入的新特性,用於層次性,結構化地組織playbook。roles能夠根據層次型結構自動裝載變量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。簡單的說,roles就是通過分別將變量、文件、任務、模塊及處理器放置於單獨的目錄中、並可以便捷地include他們的一種機制。角色一般用於基於主機構建服務的場景中、但也可以是用於構建守護進程等場景中。role主要作用是重用playbook,例如無論安裝什么軟件都會安裝時間同步服務,那么每個playbook都要編寫ntp task,可以將ntp task寫好,等到用的時候再調用就行了。ansible中將其組織成role,它有着固定的組織格式,以便playbook調用。
4.1 role層級目錄結構
role以特定的層級目錄結構進行組織的tasks、variables、handlers、templates、files等;相當於函數的調用把各個功能切割成片段來執行。
roles/
role_name/:定義的role的名字
file/: 用於存放copy或script等模塊調用的函數
tasks/: 用於定義各種task,此目錄一定要有main.yml;其他文件需要main.yml包含調用
handlers/: 用於定義各種handlers,此目錄一定要有main.yml;其他文件需要main.yml包含調用
vars/: 用於定義variables,此目錄一定要有main.yml;其他文件需要main.yml包含調用
templates/:存儲由template模塊調用的模板文本;
meta/: 定義當前角色的特殊設定及其依賴關系,此目錄中至少應該有一個名為main.yml的文件;其它的文件需要由main.yml進行"包含"調用;
default/: 此目錄中至少應該有一個名為main.yml的文件,用於設定默認變量;
[root@localhost ansible]# ll roles/
total 40
drwkebor-kebor-kebo 8 root root 4096 Jul 29 22:13 web_Deploy
drwkebor-kebor-kebo 8 root root 4096 May 7 2019 web_Deploy_af
[root@localhost ansible]# ll roles/web_Deploy
total 25
-rw-r--r-- 1 root root 45 May 7 2019 web_Deploy.yml
drwkebor-kebor-kebo 2 root root 4096 Jul 10 19:09 defaults
drwkebor-kebor-kebo 2 root root 4096 May 7 2019 handlers
drwkebor-kebor-kebo 2 root root 4096 May 7 2019 meta
drwkebor-kebor-kebo 2 root root 4096 Dec 26 19:42 tasks
drwkebor-kebor-kebo 2 root root 4096 May 7 2019 templates
drwkebor-kebor-kebo 2 root root 4096 May 7 2019 vars
[root@localhost ansible]# ll roles/web_Deploy/tasks/
total 35
-rwkebor-kebor-kebo 1 root root 1542 Jun 24 2019 Auth.yml
-rwkebor-kebor-kebo 1 root root 1482 Oct 11 16:13 StartService.yml
-rwkebor-kebor-kebo 1 root root 963 Jun 18 2019 main.yml
-rwkebor-kebor-kebo 1 root root 1415 May 7 2019 StopService.yml
[root@localhost ansible]# cat roles/web_Deploy/tasks/main.yml
---
- include_tasks: Auth.yml
tags: userauth
- include_tasks: StopService.yml
tags: stopservice
- include_tasks: StartService.yml
tags: startservice
[root@localhost ansible]# cat roles/web_Deploy/web_Deploy.yml
---
- hosts: all
roles:
- web_Deploy
===================================================================================
再如下一個項目的role目錄結構:
site.yml
webservers.yml
fooservers.yml
roles/
common/
files/
templates/
tasks/
handlers/
vars/
defaults/
meta/
webservers/
files/
templates/
tasks/
handlers/
vars/
defaults/
meta/
再看下目錄解釋:
yml文件:用於定義此角色用到的各handler:在handler中使用include包含的其他的handler文件也應該位於此目錄中;
files目錄:存放由copy或script等模塊調用的文件;
templates目錄:templates模塊會自動在此目錄中尋找Jinja2模板文件;
tasks目錄:至少應該包含一個名為main.yml的文件,其定義了此角色的任務列表;此文件可以使用include包含其他的位於此目錄中的task文件;
handlers目錄:此目錄中應當包含一個main;
vars目錄:應當包含一個main.yml文件,用於定義此角色用到的變量;
meta目錄:應當包含一個main.yml文件,用於定義此角色的特殊設定及其依賴關系;ansible 1.3及其以后的版本才支持
default目錄:為當前角色設定默認變量時使用此目錄;應當包含一個main.yml文件;
那么一個playbook就可以這樣寫:
---
- hosts: webservers
roles:
- common
- webservers
這個playbook為一個角色"kebo"指定了如下的行為:
如果 roles/kebo/tasks/main.yml 存在, 其中列出的tasks將被添加到play中
如果roles/kebo/handlers/main.yml 存在, 其中列出的handlers將被添加到play中
如果roles/kebo/vars/main.yml 存在, 其中列出的variables將被添加到play中
如果roles/kebo/meta/main.yml 存在, 其中列出的 "角色依賴"將被添加到roles列表中 (1.3 andlater)
所有 copy tasks 可以引用 roles/kebo/files/ 中的文件,不需要指明文件的路徑。
所有 scripttasks 可以引用 roles/kebo/files/ 中的腳本,不需要指明文件的路徑。
所有 template tasks 可以引用roles/kebo/templates/ 中的文件,不需要指明文件的路徑。
所有 include tasks 可以引用roles/kebo/tasks/ 中的文件,不需要指明文件的路徑。
如果roles目錄下有文件不存在,這些文件將被忽略。比如 roles目錄下面缺少了"vars/"目錄,這也沒關系。
需要注意:
仍然可以在playbook中松散地列出tasks,vars_files 以及 handlers,這種方式仍然可用,但是roles是一種很好的具有組織性的功能特性,強烈建議使用它。
如果在playbook中同時使用roles和tasks,vars_files 或者 handlers,roles 將優先執行。
而且也可以使用參數化的roles,這種方式通過添加變量來實現,比如:
--
- hosts: webservers
roles:
- common
- { role: foo_app_instance, dir: '/opt/a', port: 5000 }
- { role: foo_app_instance, dir: '/opt/b', port: 5001 }
當一些事情不需要頻繁去做時,也可以為 roles 設置觸發條件,像這樣:
---
- hosts: webservers
roles:
- { role: some_role, when: "ansible_os_family == 'RedHat'" }
它的工作方式是:將條件子句應用到 role 中的每一個 task 上。
也可以給role分配指定的標簽,比如:
---
- hosts: webservers
roles:
- { role: foo, tags: ["bar", "baz"] }
如果play仍然包含有 "tasks" section,這些 tasks 將在所有 roles 應用完成之后才被執行。也可定義一些tasks,讓它們在roles之前以及之后執行,可以這樣做:
---
- hosts: webservers
pre_tasks:
- shell: echo 'hello'
roles:
- { role: some_role }
tasks:
- shell: echo 'still busy'
post_tasks:
- shell: echo 'goodbye'
注意:
pre_tasks: 執行正式 task 之前執行的任務
post_tasks:最后需要執行的任務
4.2 在playbook中調用role
role存放的路徑在配置文件/etc/ansible/ansible.cfg中定義。如下,發現ansible的roles目錄定義到/root/app/script/ansible/roles路徑下了!!
[root@localhost ansible]# cat /etc/ansible/ansible.cfg |grep roles_path
roles_path = /etc/ansible/roles:/root/app/script/ansible/roles
在playbook中調用role的方式有三種,如下:
第一種:
- hosts: HOSTS
remote_user: root
roles:
- ROLE_NAME1
- ROLE_NAME2
第二種:除了字典第一個元素指明調用的role,后面是傳遞給role的變量
- hosts: HOSTS
remote_user: root
roles:
- { role: ROLE_NAME1, VARIABLE1: VALUE1, ... }
第三種:when指明role調用的條件
- hosts: HOSTS
remote_user: root
roles:
- { role: ROLE_NAME1, when: CONDITIONS }
4.3 調用role示例
0) 先來看看role的路徑定義
[root@localhost ansible]# cat /etc/ansible/ansible.cfg|grep roles_path
roles_path = /etc/ansible/roles:/etc/ansible/roles
1)目錄結構
[root@localhost ansible]# tree /etc/ansible/roles
/etc/ansible/roles
└── httpd #palybook調用時role的名稱
├── defaults
├── files
└── handlers
│ └── main.yml #所有的目錄文件,並不一定要有,用時才創建
└── mata
└── tasks
│ └── main.yml
└── tamplates
└── httpd.conf.c6.j2 #centos6,centos7的配置文件
└── httpd.conf.c7.j2
2)tasks文件
[root@localhost ansible]# cat /etc/ansible/roles/httpd/tasks/main.yml
- name: install httpd package
yum: name=httpd state=present
- name: install configure file
template: src=httpd.conf.c{{ ansible_distribution_major_version }}.j2 dest=/etc/httpd/conf/httpd.conf
tags: instconf
notify: restart httpd service
- name: start httpd service
service: name=httpd state=started enabled=true
3) handlers文件
[root@localhost ansible]# cat /etc/ansible/roles/httpd/handlers/main.yml
- name: restart httpd service
service: name=httpd state=restarted
4) 模板文件
[root@localhost ansible]# grep ^Listen /etc/ansible/roles/httpd/templates/httpd.conf.c6.j2
Lister {{ httpd_port }}
5) 變量
[root@localhost ansible]# cat /etc/ansible/roles/httpd/vars/main.yml
httpd_port: 8088
6) playbook文件
[root@localhost ansible]# cat /etc/ansible/httpd_conf.yml
---
- hosts: webservers
remote_user: root
roles:
- { role: httpd }
7) 執行playbook文件,並查看httpd端口
[root@localhost ansible]# ansible-playbook -i /etc/ansible/hosts /etc/ansible/httpd_conf.yml
[root@localhost ansible]# ansible -i /etc/ansible/hosts webservers -m shell -a "ss -tnlp|grep :80"
========================================================================================================
再來看一例:
1.group: 創建用戶組nginx
2.user: 創建用戶nginx
3.yum: 安裝nginx
4.template: 配置文件更新nginx.conf
5.service: 啟動nginx
[root@localhost ~]# cat /etc/ansible/ansible.cfg|grep roles_path
roles_path = /etc/ansible/roles:/root/ansible/roles
[root@localhost ~]# cd /root/ansible/roles/nginx
[root@localhost nginx]# mkdir tasks templates
[root@localhost nginx]# cd tasks
[root@localhost tasks]# vim group.yml
- name: create group nginx
group: name=nginx gid=80
[root@localhost tasks]# vim user.yml
-name: create user nginx
user: name=nginx uid=80 group=nginx system=yes shell=/sbi/nologin
[root@localhost tasks]# vim install.yml
- name: install package
yum: name=nginx
[root@localhost tasks]# vim start.yml
- name: start service
service: name=nginx state=started enabled=yes
[root@localhost tasks]# vim restart.yml
- name: restart service
service: name=nginx state=restarted
[root@localhost tasks]# vim templ.yml
- name: copy conf
template: src=nginx.conf.j2 dest=/etc/nginx/conf/nginx.conf
[root@localhost tasks]# vim main.yml
- include: group.yml
- include: user.yml
- include: install.yml
- include: templ.yml
- include: start.yml
[root@localhost tasks]# cd ../templates && ls
nginx.conf.j2
[root@localhost tasks]# cd /root/ansible
[root@localhost ansible]# vim nginx_role.yml
- hosts: websrvs
remote_user: root
roles:
- role: nginx
執行命令:
[root@localhost ansible]# ansible-playbook nginx_role.yml
5. loop列表循環用法
在ansible 2.5版本之前,大多數人習慣使用"with_X"風格的關鍵字操作循環,從ansible 2.6版本開始,官方開始推薦使用"loop"關鍵字代替"with_X"風格關鍵字。下面通過一些小示例來說明使用loop關鍵字進行的列表循環操作。[loop、with_items、with_list 三者等同,效果是一樣的!]。ansible的循環使用,可以參考下面"循環變量"以及參考這里
playbook中的變量設置
######## 變量的優先級 ########
1. extra vars變量(在命令行中使用 -e);優先級最高
2. 在inventory中定義的連接變量(比如ansible_ssh_user);優先級第二
3. 大多數的其他變量(命令行轉換,play中的變量,include的變量,role的變量等);優先級第三
4. 在inventory定義的其他變量;優先級第四
5. 有系統發現的facts;優先級第五
6. "role默認變量",這個是最默認的值,很容易喪失優先權。優先級最小。
另外:在inventory清單列表里定義的變量:單個主機定義的變量優先級高於主機組定義的變量
經過實驗,ansible使用inventory定義變量的優先級順序從高到低為:
1. host_vars下定義變量
2. inventory中單個主機定義變量
3. group_vars下定義變量
4. inventory中組定義變量
playbook中定義變量,如下:
- hosts: webservers
vars:
http_port: 80
YAML陷阱
YAML語法要求如果值以{{ foo }}開頭的話,那么就需要將整行用雙引號包起來,這是為了確認你不是想聲明一個YAML字典。
如下面配置是不行的!!!
---
- hosts: app_servers
vars:
app_path: {{ base_path }}/data/web
應該改成下面這樣:
---
- hosts: app_servers
vars:
app_path: "{{ base_path }}/data/web"
######## Ansbile-playbook變量配置方法 ########
1. 在inventory主機清單文件中定義變量
可以直接定義在主機清單文件/etc/ansible/hosts中,表明該變量只對對應的主機或者組有效,對其余的主機和組無效。
有針對單個主機定義變量和組定義變量兩種方式。
1)向不同的單個主機傳遞不同的變量;
IP/HOSTNAME var1=value1 var2=value2
2)向組中的主機傳遞相同的變量;
[groupname:vars]
var1=value1
var2=value2
但是注意:
組定義變量的作用范圍是組下的所有主機。
當兩種定義方式同時存在時,ansible會優先采用單個主機定義的變量值!
[root@ss-server ansible]# pwd
/etc/ansible
[root@ss-server ansible]# cat hosts|egrep -v "^#|^$"
[kevin]
172.16.60.20 key=20180101
172.16.60.22 ansible_ssh_port=22288 key="niubility"
[root@ss-server ansible]# cat ansi.yml
---
- hosts: kevin
gather_facts: False
tasks:
- name: haha
debug: msg="the {{ inventory_hostname }} value is {{ key }}"
執行結果(注意inventory_hostname代表inventory列表列表里被控節點的主機名):
[root@ss-server ansible]# ansible-playbook ansi.yml
PLAY [kevin] *************************************************************************************************************************************
TASK [haha] **************************************************************************************************************************************
ok: [172.16.60.20] => {
"msg": "the 172.16.60.20 value is 20180101"
}
ok: [172.16.60.22] => {
"msg": "the 172.16.60.22 value is niubility"
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
172.16.60.22 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
再看下面一示例
[root@ss-server ansible]# tail -n 10 /etc/ansible/hosts
[webserver]
172.16.60.51 dir=/root/node2
172.16.60.80 dir=/root/node1
[node1]
172.16.60.80
[webserver:vars]
file=hostname.txt
[root@ss-server ansible]# cat playbook.yml
---
- hosts: webserver
remote_user: root
tasks:
- name: Create New Folder
file: name={{ dir }} state=directory
- name: Create New Folder
shell: echo `hostname` > {{dir}}/{{ file }}
執行結果:
[root@ss-server ansible]# ansible-playbook playbook.yml
PLAY [webserver] ************************************************************************
TASK [Gathering Facts] ***************************************************************
ok: [172.16.60.80]
ok: [172.16.60.51]
TASK [Create New Folder] *************************************************************
changed: [172.16.60.51]
changed: [172.16.60.80]
TASK [Create New Folder] *************************************************************
changed: [172.16.60.51]
changed: [172.16.60.80]
PLAY RECAP ***************************************************************************
172.16.60.51 : ok=3 changed=2 unreachable=0 failed=0
172.16.60.80 : ok=3 changed=2 unreachable=0 failed=0
此外:ansible還內置了一些固定的主機變量名,在inventory中定義其值, 在另一篇文章中介紹過。
2. 通過host_vars和group_vars目錄來定義變量
/etc/ansible/目錄是linux系統上ansible默認的配置文件目錄(Mac系統上的話,其默認配置目錄是在/usr/local/etc/ansible/),在該目錄下創建host_vars和group_vars兩個目錄用來存放定義變量的文件。
1)針對單個主機的變量
[root@ss-server ansible]# pwd
/etc/ansible
[root@ss-server ansible]# cat host_vars/172.16.60.20
---
user: root
pass: root@123
2)針對test組的變量
[root@ss-server ansible]# cat group_vars/kevin
---
user: work
pass: work@123
############ 在inventory清單列表文件里,單個主機定義的變量優先級高於主機組定義的變量 ############
############ 經過實驗,ansible使用變量的優先級順序從高到低為 #############
1. host_vars下定義變量
2. inventory中單個主機定義變量
3. group_vars下定義變量
4. inventory中組定義變量
示例如下:
[root@ss-server ~]# cat /root/ansible/hosts
[kevin]
172.16.60.20 ansible_ssh_port=22222
172.16.60.21 ansible_ssh_port=22288
[root@ss-server ~]# cat /root/ansible/group_vars/kevin
key=20191010
[root@ss-server ~]# cat /root/ansible/bo.yml
---
- hosts: kevin
remote_user: root
tasks:
- debug: msg='The key is {{key}} '
[root@ss-server ~]# ansible-playbook /root/ansible/bo.yml
PLAY [kevin] *************************************************************************************************************************************
TASK [haha] **************************************************************************************************************************************
ok: [172.16.60.20] => {
"msg": "The key is 20191010"
}
ok: [172.16.60.22] => {
"msg": "The key is 20191010"
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
172.16.60.22 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
3. 通過var_files定義變量
[root@ss-server ansible]# cat laoji.yml
---
key: jiayou
[root@ss-server ansible]# cat bo.yml
---
- hosts: kevin
gather_facts: False
vars_files:
- laoji.yml
tasks:
- name: display
debug: msg="the {{ inventory_hostname }} valus is {{ key }}"
執行結果:
[root@ss-server ansible]# ansible-playbook bo.yml
PALY [kevin] ****************************************************
TASK [display] ****************************************************
ok: [172.16.60.20] => {
"changed": false
"msg":"the 172.16.60.20 value is jiayou"
}
PLAY RECAP ****************************************************
172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0
4. 通過vars_prompt交互式傳入變量
在playbook中定義vars_prompt的變量名和交互式提示信息,就可以實現在運行playbook時,通過交互的傳入變量值。
private字段:用來定義交互時是否回顯輸入的值,默認private為yes;
default字段:用來定義變量的默認值。
[root@ss-server ansible]# cat prom.yml
---
- hosts: kevin
remote_user: root
vars_prompt:
- name: "var1"
prompt: "please input you name"
private: no
- name: "var2"
prompt: "please input you age"
private: yes
default: 'kevin var'
tasks:
- name: display var1
debug: msg="your name of var1 is {{ var1 }}"
- name: display var2
debug: msg="you age of var2 is {{ var2 }}"
執行結果:
[root@ss-server ansible]# ansible-playbook prom.yml
your name of var1: wangzhaojun #把輸入的內容傳遞給變量var1。輸入的值顯示出來!!
you age of var2 [kevin var]: #把輸入的內容傳遞給默認變量kevin var。但是輸入的值不顯示出來!! 比如這里輸入的30
PALY [kevin] ****************************************************
TASK [Gathering Facts] ****************************************************
OK: [172.16.60.20]
TASK [display var1] ****************************************************
ok: [172.16.60.20] =>{
"msg": "youn name of var1 is wangzhaojun"
}
TASK [display var2] ****************************************************
ok: [172.16.60.20] =>{
"msg": "youn name of var2 is 30"
}
PLAY RECAP ****************************************************
172.16.60.20 : ok=3 changed=0 unreachable=0 failed=0
接下來再來看一個"ansible 中prompt 交互變量的使用"的示例
[root@ss-server ansible]# cat haha.yml
---
- hosts: kevin
remote_user: root
vars_prompt:
- name: "your_name"
prompt: "what is your name"
private: no
- name: "your_age"
prompt: "how old are you"
private: no
- name: "solution"
prompt: "Choose the solution you want \n
A: solutionA\n
B: solutionB\n
C: solutionC\n"
private: no
default: A
- name: "user_name"
prompt: "Enter user name"
private: no
- name: "user_password"
prompt: "Enter user password"
private: no
encrypt: "sha512_crypt"
confirm: yes
tasks:
- name: "output vars"
debug: msg="your name is {{ your_name }},you are {{ your_age }} years old"
- name: "output solution"
debug: msg="the final solution is {{solution}}"
- name: "create_user"
user:
name: "{{user_name}}"
password: "{{user_password}}"
- name: "debug_create user"
debug: msg="create user {{user_name}} in"
執行結果為:
[root@ss-server ansible]# ansible-playbook haha.yml
what is your name: wangshibo
how old are you: 29
Choose the solution you want
A: solutionA
B: solutionB
C: solutionC
[A]: C
Enter user name: bobo
Enter user password: bobo123
confirm Enter user password: bobo123
PLAY [kevin] *************************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.20]
TASK [output vars] *******************************************************************************************************************************
ok: [172.16.60.20] => {
"msg": "your name is wangshibo,you are 29 years old"
}
TASK [output solution] ***************************************************************************************************************************
ok: [172.16.60.20] => {
"msg": "the final solution is C"
}
TASK [create_user] *******************************************************************************************************************************
changed: [172.16.60.20]
TASK [debug_create user] *************************************************************************************************************************
ok: [172.16.60.20] => {
"msg": "create user bobo in"
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.20 : ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
5. 通過ansible-playbook命令行定義變量!即參數傳入變量
除了"vars_prompt"和"vars_files",也可以通過Ansible命令行發送變量。如果想要編寫一個通用的發布playbook時則特別有用!你可以傳遞應用的版本以便部署。例如下面命令(注意: --extra-vars 相等於 -e)
[root@ss-server ~]# ansible-playbook release.yml --extra-vars "version=1.78.34 other_variable=fos"
其他場景中,通過參數傳入變量也是很有用的。比如為playbook設置主機群組或用戶。如下:
[root@ss-server ~]# vim exap.yml
---
- hosts: '{{hosts}}'
remote_user: '{{user}}'
tasks:
- name: "一個測試"
debug: msg="your hosts is {{hosts}}, user is {{user}}"
執行的時候,通過參數傳入變量(-e)。變量{{hosts}}可以是主機群組名稱,也可以是主機ip。
[root@ss-server ansible]# ansible-playbook exap.yml -e "hosts=kevin user=root"
[WARNING]: Found variable using reserved name: hosts
PLAY [kevin] *************************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.20]
TASK [一個測試] **************************************************************************************************************************************
ok: [172.16.60.20] => {
"msg": "your hosts is kevin, user is root"
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.20 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
在命令行里面傳值的方法:
[root@ss-server ansible]# ansible-playbook amp.yml --extra-vars "hosts=webserver user=root"
還可以使用json格式傳遞參數:
[root@ss-server ansible]# ansible-playbook amp.yml --extra-vars "{'hosts':'webserver', 'user':'root'}"
還可以將參數放在文件里面進行傳遞(注意命令行里要是用"@文件名"):
[root@ss-server ansible]# ansible-playbook amp.yml --extra-vars "@anhui.yml"
[root@ss-server ansible]# cat anhui.yml
---
hosts: webserver
user: root
例如下面這個添加用戶的plakbook劇本配置,用傳遞了json文件:
[root@ss-server ansible]# cat useradd.yml
---
- hosts: "{{ host }}"
gather_facts: no
remote_user: root
vars:
UserName: "{{ user }}"
UserPassword: "{{ pass }}"
tasks:
- name: create new user {{ UserName }}
user: name={{ UserName }} shell=/bin/bash password={{ UserPassword |password_hash('sha512') }} update_password=always append=yes
- name: Config /etc/sudoers
lineinfile: dest=/etc/sudoers state=present line='{{ item }}' validate='visudo -cf %s'
with_items:
- "{{ user }} ALL=(ALL) NOPASSWD:ALL,!/bin/rm,!/bin/su,!/usr/bin/passwd,!/usr/sbin/visudo,!/sbin/shutdown,!/sbin/reboot,!/sbin/halt"
- "Defaults: {{ user }} !requiretty"
[root@ss-server ansible]# ansible-playbook useradd.yml -e "host=172.16.60.20 user=kevin_bo pass=kevin@bo123"
在上例中,變量pass是密碼,如果變量pass里有特殊的字符,或者變量pass是一串數組的話,它將被轉義。若不想被轉義,可以使用如下方法:
[root@ss-server ansible]# cat user.json
host: webserver #ansible里面定義的主機組或者直接配置主機ip,如172.16.60.20
user: kevin_bo #添加的用戶名
pass: 'Fxx6unM$R%I$Jna&' #添加的用戶的密碼,可以用百度上隨機密碼生成器生成
指定user,json文件執行劇本 (使用JSON格式的文件即可,-e 傳遞變量,優先級最高)
[root@ss-server ansible]# ansible-playbook useradd.yml -e "@user.json"
刪除web組中所有用戶
[root@ss-server ansible]# ansible webserver -m user -a 'name=zhangsan state=absent remove=yes'
6. 在playbook劇本中定義變量
在playbook中定義變量需要用到Ansible的vars模塊,可以將所有需要用到的變量統一在vars模塊下定義,定義格式需要遵循YAML語言格式:
vars:
- var1: value1
- var2: value2
- var3: value3
- ....: .....
示例如下:
[root@ss-server ansible]# cat playbook.yml
---
- hosts: kevin
remote_user: root
vars:
- dir1: /root/Ansible
- dir2: /root/Ansible/test1
- dir3: /root/Ansible/test2
tasks:
- name: Create New Folder
file: name={{ dir1 }} state=directory
- name: Create New Folder
file: name={{ dir2 }} state=directory
- name: Create New Folder
file: name={{ dir3 }} state=directory
[root@ss-server ansible]# ansible-playbook playbook.yml
PLAY [kevin] *************************************************************************
TASK [Gathering Facts] ***************************************************************
ok: [172.16.60.20]
TASK [Create New Folder] *************************************************************
changed: [172.16.60.20]
TASK [Create New Folder] *************************************************************
changed: [172.16.60.20]
TASK [Create New Folder] *************************************************************
changed: [172.16.60.20]
PLAY RECAP ***************************************************************************
[172.16.60.20] : ok=4 changed=3 unreachable=0 failed=0
7. 通過roles角色定義變量
在Ansible的roles中定義變量,需要將變量及值的鍵值對形式寫到roles的vars目錄下的main.yml文件中,同樣適用YAML語言格式,格式如下:
var1: value1
var2: value2
var3: value3
但是請注意:
通過Roles定義的變量只適用於當前roles。如下是roles文件組織結構:
[root@ss-server roles]# tree test/
test/
├── files
├── handlers
├── playbook.retry
├── playbook.yml
├── tasks
│ └── main.yml
├── templates
└── vars
└── main.yml
5 directories, 4 files
[root@ss-server roles]# cat test/tasks/main.yml #任務文件
- name: Get IP Address
shell: echo `{{ cmd }}` >> {{ dir }}/{{ file }}
[root@ss-server roles]# cat test/vars/main.yml #定義變量cmd
cmd: hostname -I
[root@ss-server roles]# cat test/playbook.yml
---
- hosts: webserver
remote_user: root
roles:
- test
hosts清單列表里定義的變量,適用於所有roles。
[root@ss-server roles]# cat /etc/ansible/hosts
[webserver]
172.16.60.51 dir=/root/node2
172.16.60.80 dir=/root/node1
[node1]
172.16.60.80
[webserver:vars]
file=hostname.txt
playbook調用Roles,執行結果為:
[root@Centos7T test]#ansible-playbook playbook.yml
PLAY [websvr] ************************************************************************
TASK [Gathering Facts] ***************************************************************
ok: [172.16.60.80]
ok: [172.16.60.51]
TASK [test : Get IP Address] *********************************************************
changed: [172.16.60.51]
changed: [172.16.60.80]
PLAY RECAP ***************************************************************************
172.16.60.51 : ok=2 changed=1 unreachable=0 failed=0
172.16.60.80 : ok=2 changed=1 unreachable=0 failed=0
8. 使用Facts獲取的信息
還有其它地方可以獲取變量, 這些變量是自動發現的,而不是用戶自己設置的。Facts通過訪問遠程系統獲取相應的信息,一個很好的例子就是遠程主機的IP地址或者操作系統是什么。
使用以下命令可以查看哪些信息是可用的(kevin是上面在/etc/ansible/hosts列表文件中配置的主機群組):
[root@ss-server ansible]# ansible kevin -m setup
[root@ss-server ansible]# ansible kevin -m setup|grep "ansible_python_version"
"ansible_python_version": "2.7.5",
可以在playbook中這樣引用上面被控制主機的python版本: {{ ansible_python_version }}
[root@ss-server ansible]# ansible kevin -m setup|grep "ansible_nodename"
"ansible_nodename": "ss-server",
可以在playbook中這樣引用上面被控制主機的主機名: {{ ansible_nodename }}
[root@ss-server ansible]# ansible kevin -m setup|grep "ansible_hostname"
"ansible_hostname": "ss-server",
被控制主機的主機名變量還可以是: {{ ansible_hostname }}
訪問復雜變量數據
有些提供的facts, 比如網絡信息等, 是一個嵌套的數據結構。訪問它們使用簡單的 {{ foo }} 語法並不夠用,
但是也仍然很容易.如下所示:
{{ ansible_eth0["ipv4"]["address"] }}
或者這樣寫:
{{ ansible_eth0.ipv4.address }}
############ 如果關閉Facts,可以大大提高ansible的執行速度 ###########
關閉方法如下:
[root@ss-server ansible]# cat anhui.yml
---
- hosts: kevin
gather_facts: no
9. register注冊變量
變量的另一個主要用途是在運行命令時,把命令結果存儲到一個變量中,不同模塊的執行結果是不同的。運行playbook時使用-v選項可以看到可能的結果值,ansible執行任務的結果值可以保存在變量中,以便稍后使用它。register方式主要用於在task之間傳遞變量。
示例如下:
[root@ss-server ansible]# cat /etc/ansible/hosts
[kevin]
172.16.60.237
172.16.60.238
[root@ss-server ansible]# cat /root/register.yml
---
- hosts: kevin
remote_user: root
tasks:
- name: register bo_test
shell: hostname -I
register: info
- name: display info
debug: msg="this host ip is {{ info }}"
[root@ss-server ansible]# ansible-playbook /root/register.yml
PLAY [kevin] *************************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.238]
ok: [172.16.60.237]
TASK [register bo_test] **************************************************************************************************************************
changed: [172.16.60.238]
changed: [172.16.60.237]
TASK [display info] ******************************************************************************************************************************
ok: [172.16.60.237] => {
"msg": "this host ip is {'stderr_lines': [], u'changed': True, u'end': u'2019-12-15 22:07:18.431549', 'failed': False, u'stdout': u'172.16.60.237 ', u'cmd': u'hostname -I', u'rc': 0, u'start': u'2019-12-15 22:07:18.408235', u'stderr': u'', u'delta': u'0:00:00.023314', 'stdout_lines': [u'172.16.60.237 ']}"
}
ok: [172.16.60.238] => {
"msg": "this host ip is {'stderr_lines': [], u'changed': True, u'end': u'2019-12-15 22:07:18.430108', 'failed': False, u'stdout': u'172.16.60.238 ', u'cmd': u'hostname -I', u'rc': 0, u'start': u'2019-12-15 22:07:18.407184', u'stderr': u'', u'delta': u'0:00:00.022924', 'stdout_lines': [u'172.16.60.238 ']}"
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.237 : ok=3 changed=1 unreachable=0 failed=0
172.16.60.238 : ok=3 changed=1 unreachable=0 failed=0
這里注意下:
register定義的info變量在第二個task中用來查看前一個task中執行的hostname命令的結果。
可以看到playbook運行后的結果中,info返回的是一段python字典數據,如果只想看到stdout部分的信息的話,可以通過info[‘stdout’]來引用。
[root@ss-server ansible]# cat /root/register.yml
---
- hosts: kevin
remote_user: root
tasks:
- name: register bo_test
shell: hostname -I
register: info
- name: display info
debug: msg="this host ip is {{ info['stdout'] }}"
[root@ss-server ansible]# ansible-playbook /root/register.yml
PLAY [kevin] *************************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.238]
ok: [172.16.60.237]
TASK [register bo_test] **************************************************************************************************************************
changed: [172.16.60.237]
changed: [172.16.60.238]
TASK [display info] ******************************************************************************************************************************
ok: [172.16.60.237] => {
"msg": "this host ip is 172.16.60.237 "
}
ok: [172.16.60.238] => {
"msg": "this host ip is 172.16.60.238 "
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.237 : ok=3 changed=1 unreachable=0 failed=0
172.16.60.238 : ok=3 changed=1 unreachable=0 failed=0
10. hostvars 變量
該變量用於引用其他主機上收集的facts中的數據,或者引用其他主機的主機變量、主機組變量。即從一台遠程主機獲取另一台遠程主機的變量。
如下示例:
[root@ss-server ansible]# cat /etc/ansible/hosts
[kevin]
172.16.60.237 addr=beijing
172.16.60.238 user=shibo age=39
[root@ss-server ansible]# cat test.yml
---
- hosts: kevin
remote_user: root
gather_facts: False
tasks:
- name: this is test1
debug: msg="She is come from {{ hostvars['172.16.60.237']['addr'] }}"
- name: this is test2
debug: msg="I am {{ hostvars['172.16.60.238']['user'] }}, and age is {{ hostvars['172.16.60.238']['age'] }}"
或者將test.yml文件配置如下,即由 "[變量]"" 改為 ".變量"
兩種配置的執行結果都是一樣的!
[root@ss-server ansible]# cat test.yml
---
- hosts: kevin
remote_user: root
gather_facts: False
tasks:
- name: this is test1
debug: msg="She is come from {{ hostvars['172.16.60.237'].addr }}"
- name: this is test2
debug: msg="I am {{ hostvars['172.16.60.238'].user }}, and age is {{ hostvars['172.16.60.238'].age }}"
執行結果為:
[root@ss-server ansible]# ansible-playbook test.yml
PLAY [kevin] *************************************************************************************************************************************
TASK [this is test1] *****************************************************************************************************************************
ok: [172.16.60.237] => {
"msg": "She is come from beijing"
}
ok: [172.16.60.238] => {
"msg": "She is come from beijing"
}
TASK [this is test2] *****************************************************************************************************************************
ok: [172.16.60.237] => {
"msg": "I am shibo, and age is 39"
}
ok: [172.16.60.238] => {
"msg": "I am shibo, and age is 39"
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.237 : ok=2 changed=0 unreachable=0 failed=0
172.16.60.238 : ok=2 changed=0 unreachable=0 failed=0
############################# 這里需要注意下 ###########################
除了上面的配置,還可以如下配置,但是請注意:
1)debug后面必須使用"var"字段,即hostvar變量傳遞給var字段的變量。
2)var=hostvars[....],等於兩邊不能有空格!
[root@ss-server ansible]# cat test.yml
---
- hosts: kevin
remote_user: root
gather_facts: False
tasks:
- name: this is test1
debug: var=hostvars['172.16.60.237']['addr']
- name: this is test2
debug: var=hostvars['172.16.60.238']['user']
或者將test.yml文件配置如下,這兩個配置方法的執行結果是一樣的!
[root@ss-server ansible]# cat test.yml
---
- hosts: kevin
remote_user: root
gather_facts: False
tasks:
- name: this is test1
debug: var=hostvars['172.16.60.237'].addr
- name: this is test2
debug: var=hostvars['172.16.60.238'].user
執行結果為:
[root@ss-server ansible]# ansible-playbook test.yml
PLAY [kevin] *************************************************************************************************************************************
TASK [this is test1] *****************************************************************************************************************************
ok: [172.16.60.237] => {
"hostvars['172.16.60.237']['addr']": "beijing"
}
ok: [172.16.60.238] => {
"hostvars['172.16.60.237']['addr']": "beijing"
}
TASK [this is test2] *****************************************************************************************************************************
ok: [172.16.60.237] => {
"hostvars['172.16.60.238']['user']": "shibo"
}
ok: [172.16.60.238] => {
"hostvars['172.16.60.238']['user']": "shibo"
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.237 : ok=2 changed=0 unreachable=0 failed=0
172.16.60.238 : ok=2 changed=0 unreachable=0 failed=0
注意:如果同一個name里配置了多個debug,則默認執行最后一個debug內容! (多個task的話,也是執行最后一個task)
[root@ss-server ansible]# cat test.yml
---
- hosts: kevin
remote_user: root
gather_facts: False
tasks:
- name: this is test1
debug: var=hostvars['172.16.60.237']['addr']
- name: this is test2
debug: var=hostvars['172.16.60.238']['user']
debug: var=hostvars['172.16.60.238']['age']
執行結果:
[root@ss-server ansible]# ansible-playbook test.yml
[WARNING]: While constructing a mapping from /etc/ansible/test.yml, line 8, column 7, found a duplicate dict key (debug). Using last defined
value only.
PLAY [kevin] *************************************************************************************************************************************
TASK [this is test1] *****************************************************************************************************************************
ok: [172.16.60.237] => {
"hostvars['172.16.60.237']['addr']": "beijing"
}
ok: [172.16.60.238] => {
"hostvars['172.16.60.237']['addr']": "beijing"
}
TASK [this is test2] *****************************************************************************************************************************
ok: [172.16.60.237] => {
"hostvars['172.16.60.238']['age']": "39"
}
ok: [172.16.60.238] => {
"hostvars['172.16.60.238']['age']": "39"
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.237 : ok=2 changed=0 unreachable=0 failed=0
172.16.60.238 : ok=2 changed=0 unreachable=0 failed=0
11. 列表變量、循環變量、字典變量
1)ansible的變量不僅可以是單個的值,也可以為列表,即ansible傳列表作為變量。如下示例:
#################################################################################################################
[root@ss-server ansible]# vim test.yml
---
- hosts: test_host
gather_facts: no
vars:
- list: [1,2,3]
tasks:
- name: echo
debug: msg="{{ list }}"
執行結果:
[root@ss-server ansible]# ansible-palybook test.yml
TASK [echo] ********************************************************************
ok: [172.16.60.220] => {
"msg": [
1,
2,
3
]
}
再來看一例:
linux創建用戶,需要獲取的變量有用戶名,用戶密碼,用戶組,有時候需要創建多個用戶,那么傳遞給ansible的用戶肯定是列表,但每一組又有用戶名、密碼、組這些變量值。
做法如下:
[root@ss-server ~]# cat /etc/ansible/test.yml
---
- hosts: test_host
gather_facts: no
tasks:
- name: create or update account
user: name={{ item.user }} password={{ item.password }} groups={{ item.group }} system=no
with_items:
- '{{ user_list }}'
執行結果:
[root@ss-server ansible]# ansible-playbook /etc/ansible/test.yml -e '{"user_list":[{"user":"user1","password":"*******","group":"user1"}]}'
#################################################################################################################
2)循環列表
結合循環,這個特性就變得很有用;以參數傳遞列表給playbook,不用在playbook中固定循環的次數與內容。如下示例:
[root@ss-server ansible]# vim test.yml
---
- hosts: test_host
gather_facts: no
vars:
- list: [1,2,3]
tasks:
- name: this is loop
debug: msg="{{ item }}"
with_items: '{{list}}'
執行結果:
[root@ss-server ansible]# ansible-palybook test.yml
TASK [this is loop] ********************************************************************
ok: [172.16.60.220] => (item=1) => {
"item": 1,
"msg": 1
}
ok: [localhost] => (item=2) => {
"item": 2,
"msg": 2
}
ok: [localhost] => (item=3) => {
"item": 3,
"msg": 3
}
接着看下面一例:
loop 關鍵字表示循環,去讀循環體里的變量固定使用{{item}},item是個字典對象item.key=value。
需要注意:下面test.yml文件中的"loop"關鍵字 改為 "with_items"關鍵字,效果是一樣的!
ansible的循環用法:在ansible 2.5版本之前,大多數人習慣使用"with_X"風格的關鍵字操作循環,
從ansible 2.6版本開始,官方開始推薦使用"loop"關鍵字代替"with_X"風格關鍵字。
[root@ss-server ~]# cat /etc/ansible/test.yml
---
- name: this is test
hosts: test_host
connection: local
gather_facts: no
tasks:
- name: debug loop
debug:
msg: "{{item.A1}}"
loop: #這里將"loop"關鍵字 改為 "with_items"關鍵字,效果是一樣的!
- A: a
A1: a1
A2: a2
- B: b
A1: b1
A2: b2
- C: c
A1: c1
A2: c2
- D: d
A1: d1
A2: d2
執行結果:以上loop下的四個變量分別稱為一塊,即一個item,符號"-"為循環體塊的標志,{{item.A1}}的值,即分別為a1,b1,c1,d1
[root@ss-server ~]# ansible-playbook /etc/ansible/test.yml
PLAY [this is test] *********************************************************************************************************************************
TASK [debug loop] ********************************************************************************************************************************
ok: [172.16.60.20] => (item={u'A': u'a', u'A1': u'a1', u'A2': u'a2'}) => {
"msg": "a1"
}
ok: [172.16.60.20] => (item={u'A1': u'b1', u'B': u'b', u'A2': u'b2'}) => {
"msg": "b1"
}
ok: [172.16.60.20] => (item={u'A1': u'c1', u'C': u'c', u'A2': u'c2'}) => {
"msg": "c1"
}
ok: [172.16.60.20] => (item={u'A1': u'd1', u'A2': u'd2', u'D': u'd'}) => {
"msg": "d1"
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
再來看一例:
[root@ss-server ~]# cat /etc/ansible/test.yml
---
- name: this is test
hosts: test_host
connection: local
gather_facts: no
vars:
my_list:
- a
- b
- c
- 1
tasks:
- name: debug loop output
debug:
msg: "The {{index}} one is {{item}}"
loop: "{{my_list}}" # 或者使用with_items: "{{my_list}}" 或者 with_list: "{{my_list}}"
loop_control:
index_var: index
執行結果:
[root@ss-server ~]# ansible-playbook /etc/ansible/test.yml
PLAY [this is test] ******************************************************************************************************************************
TASK [debug loop output] *************************************************************************************************************************
ok: [172.16.60.20] => (item=a) => {
"msg": "The 0 one is a"
}
ok: [172.16.60.20] => (item=b) => {
"msg": "The 1 one is b"
}
ok: [172.16.60.20] => (item=c) => {
"msg": "The 2 one is c"
}
ok: [172.16.60.20] => (item=1) => {
"msg": "The 3 one is 1"
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
#################################################################################################################
3)字典變量
變量也可以為字典。如下示例, 用到了ansible的循環,循環關鍵字是"with_X", 改為loop關鍵字,效果是一樣的!
注意:在變量中使用循環時,vars下必須要有"-",符號"-"為循環體塊的標志!!
[root@ss-server ansible]# vim test.yml
---
- hosts: test_host
gather_facts: no
vars:
- lists:
list1: [1,2,3]
list2: [4,5]
tasks:
- name: loop
debug: msg="{{ item }}"
with_items: '{{lists["list1"]}}' #替換為 loop: '{{lists["list1"]}}', 或者 with_list: '{{lists["list1"]}}' 效果是一樣的
執行結果:
[root@ss-server ansible]# ansible-palybook test.yml
TASK [loop] ********************************************************************
ok: [172.16.60.220] => (item=1) => {
"item": 1,
"msg": 1
}
ok: [localhost] => (item=2) => {
"item": 2,
"msg": 2
}
ok: [localhost] => (item=3) => {
"item": 3,
"msg": 3
}
接着看下面一例:
[root@ss-server ~]# cat /etc/ansible/test.yml
---
- hosts: test_host
gather_facts: no
vars:
- users:
name: [xiaoming]
address: [anhui]
age: [28]
tasks:
- name: this is loop
debug: msg="{{ item }}"
loop: '{{users["name"]}}'
[root@ss-server ~]# ansible-playbook /etc/ansible/test.yml
PLAY [test_host] *************************************************************************************************************************************
TASK [this is loop] **************************************************************************************************************************************
ok: [172.16.60.20] => (item=xiaoming) => {
"msg": "xiaoming"
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
上面的示例,如果要想打印多個字典變量,修改如下:
[root@ss-server ~]# cat /etc/ansible/test.yml
---
- hosts: test_host
gather_facts: no
vars:
- users:
name: [xiaoming]
address: [anhui]
age: [28]
tasks:
- name: this is loop
debug: msg="{{ item }}"
loop: #這里用loop,with_items,with_list都可以
- '{{users["name"]}}'
- '{{users["address"]}}'
- '{{users["age"]}}'
執行結果:
[root@ss-server ~]# ansible-playbook /etc/ansible/test.yml
PLAY [test_host] *********************************************************************************************************************************
TASK [this is loop] ******************************************************************************************************************************
ok: [172.16.60.20] => (item=[u'xiaoming']) => {
"msg": [
"xiaoming"
]
}
ok: [172.16.60.20] => (item=[u'anhui']) => {
"msg": [
"anhui"
]
}
ok: [172.16.60.20] => (item=[28]) => {
"msg": [
28
]
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
###### 可以在一個yaml文件里放置多個hosts,將多個tasks任務一起執行 ########
[root@ss-server ansible]# cat /etc/ansible/hosts
172.16.60.237
[kevin]
172.16.60.238
[grace]
172.16.60.236
[root@ss-server ansible]# cat bo.yml
---
- hosts: kevin
remote_user: root
gather_facts: False
tasks:
- name: this is test1
shell: hostname -I
- hosts: 172.16.60.237
remote_user: root
tasks:
- name: this is test2
debug: msg="this server is {{ info }} "
- hosts: 172.16.60.238
remote_user: root
tasks:
- name: this is test3
debug: msg="this key is {{ haha }} "
執行結果:
[root@ss-server ansible]# ansible-playbook bo.yml -e "{'info':'my server','haha':'123123213123'}"
PLAY [kevin] *************************************************************************************************************************************
TASK [this is test1] *****************************************************************************************************************************
changed: [172.16.60.238]
PLAY [172.16.60.237] *****************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.237]
TASK [this is test2] *****************************************************************************************************************************
ok: [172.16.60.237] => {
"msg": "this server is my server "
}
PLAY [172.16.60.238] *****************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.238]
TASK [this is test3] *****************************************************************************************************************************
ok: [172.16.60.238] => {
"msg": "this key is 123123213123 "
}
PLAY RECAP ***************************************************************************************************************************************
172.16.60.237 : ok=2 changed=0 unreachable=0 failed=0
172.16.60.238 : ok=3 changed=1 unreachable=0 failed=0
############# 再來看下面一個測試環境用過的部署腳本配置 #########################
[root@localhost ansible]# cat /opt/ansible_cfg/deploy.yml
---
- hosts: webservers
tasks:
- name: Installed Httpd Server
yum: name=httpd state=present
- name: Start Httpd Server
systemd: name=httpd state=started enabled=yes
- name: Start Firewalld Server
systemd: name=firewalld state=started enabled=yes
- name: Configure Firewalld Server
firewalld: service=http immediate=yes permanent=yes state=enabled
- hosts: web01
tasks:
- name: Configure web01 Website
copy: content='This is Web01' dest=/var/www/html/index.html
- hosts: web02
tasks:
- name: Cofnigure webi-2 weisite
copy: content='This is Web02' dest=/var/www/html/index.html
- hosts: nfs01
tasks:
- name: Install NFS-utils Server
yum: name=nfs-utils state=present
- name: Configure Nfs-utils Server
copy: src=./exports.j2 dest=/etc/exports owner=root group=root mode=0644
- name: Create NFS Group
group: name=www gid=666
- name: Create NFS User
user: name=www uid=666 group=www create_home=no shell=/sbin/nologin
- name: Create Data Directory
file: path=/data state=directory owner=www group=www mode=0755 recurse=yes
- name: Start NFS Server
systemd: name=nfs state=started enabled=yes
- hosts: nfs01
tasks:
- name: Mount NFS Server
mount: path=/opt src=172.16.60.23:/data fstype=nfs opts=defaults state=mounted
###### Ansible-playbook如何正確獲取ip ######
[root@ansible ~]# vim /etc/ansible/hosts_all.yaml
---
- hosts: all
tasks:
- name: 將原有的hosts文件備份
shell: mv /etc/hosts /etc/hosts_bak
- name: 將ansible端的hosts復制到各自機器上
copy: src=/root/hosts dest=/etc/ owner=root group=root mode=0644
- name: 在新的hosts文件后面追加各自機器內網ip和hostname
lineinfile: dest=/etc/hosts line="`ansible_all_ipv4_addresses` `ansible_hostname`"
但是執行完ansible-playbook之后,ansible客戶機器上的/etc/hosts文件里ip和主機名對應關系如下(cat /etc/hosts):
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
[u'172.16.60.210'] redis-fun01.kevin.cn
實際想要的結果是(cat /etc/hosts):
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
172.16.60.210 redis-fun01.kevin.cn
解決辦法:
調整ansible服務端的hosts_all.yaml文件中獲取ip的變量
變量用 IP: "{{ ansible_eth0['ipv4']['address'] }}",而不是`ansible_all_ipv4_addresses`
修改后的yaml文件配置如下:
[root@ansible ~]# vim /etc/ansible/hosts_all.yaml
---
- hosts: all
vars:
IP: "{{ ansible_eth0['ipv4']['address'] }}"
tasks:
- name: 將原有的hosts文件備份
shell: mv /etc/hosts /etc/hosts_bak
- name: 將ansible端的hosts復制到各自機器上
copy: src=/root/hosts dest=/etc/ owner=root group=root mode=0644
- name: 在新的hosts文件后面追加各自機器內網ip和hostname
lineinfile: dest=/etc/hosts line="`IP` `ansible_hostname`"
######## 獲取ansible清單列表里對應組的ip、指定機器執行操作 [ --list-hosts、--limit ] ########
注意下面兩個參數的使用:
1)獲取hosts清單文件里指定組內的ip,使用"--list-hosts"
2)指定hosts清單文件里的ip執行操作,使用"--limit"
如下是ansible的一個清單文件:
[root@localhost ~]# cat /opt/kevin-bo.cfg
[kevin-bo_F]
172.16.60.65
[kevin-bo_A]
172.16.60.140
172.16.60.141
[kevin-bo:children]
kevin-bo_D
kevin-bo_F
kevin-bo_A
[kevin-bo:vars]
deploy_path=/opt/web/kevin-bo/
start_time_out=90
stop_time_out=60
module=kevin-bo
[kevin-bo_D]
172.16.60.195
現在需要獲取"kevin-bo_A" 和 "kevin-bo_D" 組下的ip,--list-hosts后面加不加引號都可以。
[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts kevin-bo_A
hosts (2):
172.16.60.140
172.16.60.141
[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts "kevin-bo_A"|grep -v "hosts"
172.16.60.140
172.16.60.141
[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts kevin-bo_D|grep -v "hosts"
172.16.60.195
獲取多個機器組內的ip,中間使用逗號隔開
[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts "kevin-bo_A,kevin-bo_D"
hosts (3):
172.16.60.140
172.16.60.141
172.16.60.195
[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts "kevin-bo_A,kevin-bo_D"|grep -v "hosts"
172.16.60.140
172.16.60.141
172.16.60.195
在執行ansible-playbook時,可以指定單個ip或group,也可以指定多個ip或group,也可以一起指定ip和group,多個中間使用逗號隔開!
--limit后面加不加引號都可以。
# ansible-playbook -i 清單文件 yml文件 --limit ip
# ansible-playbook -i 清單文件 yml文件 --limit ip1,iP2,ipn
# ansible-playbook -i 清單文件 yml文件 --limit group
# ansible-playbook -i 清單文件 yml文件 --limit group1,group2,groupn
# ansible-playbook -i 清單文件 yml文件 --limit ip1,ip2,group1,groupn
還可以將制定的ip或group放在一個文件里,如ip.txt,然后--limit后面跟"@ip.txt"
# ansible-playbook -i 列表文件 yml文件 --limit @ip.txt
# cat ip.txt
172.16.60.140
172.16.60.141
kevin-bo_F
如上,需要對清單文件"kevin-bo_A"組下的機器進行發布操作,由於發布過程中涉及服務啟停,為了保證發布中整體服務不中斷,需要一台一台的執行,不能所有機器一起執行。如下:
[root@localhost ~]# vim deploy.sh
#!/bin/bash
#設置變量,傳參等
BRANCH=$1
MODULE_NAME=$2
PRODUCT_PATH=$3
USER=$4
APP_NANE=${Deploy_App}
.....
for Next_Deploy_IP in $(ansible -i /opt/kevin-bo.cfg --list-hosts kevin-bo_A|grep -v "hosts")
do
ansible-playbook -i /opt/kevin-bo.cfg /root/ansible/web_deploy.yml --limit "${Next_Deploy_IP}" -e "user=${USER} app_name=${APP_NANE} package_name=... ..."
if [ $? -eq 0 ];then
echo "[`date +%Y%m%d-%H%M%S`]++++++++++++++++++++++++${Next_Deploy_IP}部署成功!++++++++++++++++++++++++++++++"
sleep 10
else
echo "[`date +%Y%m%d-%H%M%S`]++++++++++++++++++++++++${Next_Deploy_IP}部署失敗!++++++++++++++++++++++++++++++"
exit 1
fi
done
