Playbook 核心元件
官方文檔
https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.
html#playbook-keywords
一個playbook 中有多個元件組成,其中所用到的常見組進類型如下:
- Hosts 執行的遠端主機清單
- Tasks 任務集,由多個task的元素組成的清單實作,每個task是一個字典,一個完整的代碼塊功能最少需包括 name和task,一個name隻能包括一個task
- Variables 内置變量或自定義變量在playbook中調用
- Templates 模闆,可替換模闆檔案中的變量并實作一些簡單邏輯的檔案
- Handlers 和 notify 結合使用,由特定條件觸發的操作,滿足條件才執行,否則不執行
- tags 标簽,指定某條任務執行,用于選擇運作playbook中的部分代碼。ansible具有幂等性,是以會自動跳過沒有變化的部分,即便如此,有些代碼為測試其确實沒有發生變化的時間依然會非常的長。此時,如果确信其沒有變化,就可以通過tags跳過此代碼片段
1.1 playbook 元件
1.1.1 hosts 元件
Hosts:playbook中的每一個play的目的都是為了讓特定主機以某個指定的使用者身份執行任務,hosts用于指定要執行指定任務的主機,須事先定義在主機清單中
Websrvs:dbsrvs #或者,兩個組的并集
Websrvs:&dbsrvs #與,兩個組的交集
webservers:!dbsrvs #在websrvs組,但不在dbsrvs組
#案例
- hosts: websrvs:appsrvs
1.1.2 remote_user 元件
remote_user:可用于host和task中,也可以通過指定其通過sudo的方式在遠端主機上執行任務,其可用于play全局或某個任務;此外,甚至可以在sudo時使用sudo_user指定sudo時切換的使用者
- hosts: websrvs
remote_user: root
tasks:
- name: test connection
ping:
remote_user: lee
sudo: yes #預設sudo為root
sudo_user: lee #sudo為lee
1.1.3 task清單和action元件
play的主體部分是task list,task list中有一個或多個task,各個task按次序逐個在hosts中指定的所有主機上執行,即在所有主機上完成第一個task後,再開始第二個task
task的目的是使用指定的參數執行子產品,而在子產品參數中可以使用變量,子產品執行是幂等的,這意味着多次執行是安全的,因為結果均一緻
每個task都應該有其name,用于playbook的執行結果輸出,具有其内容能清晰地描述任務執行步驟,如果未提供name,則action的結果将用于輸出。
task的兩種格式:
acion: module arguments # action: shell echo hello
module: arguments #建議使用此方式, shell: echo hello
注意: shell和command子產品後面跟指令,而非key=value
範例:
[root@ansible playbook]#cat hello.yml
---
- hosts: web
remote_user: root
gather_facts: no #不收集系統資訊,提高執行效率
tasks:
- name: test network connection
ping:
- name: Excute command
command: wall "hello,world, yaml!"
[root@ansible playbook]#cat install_httpd.yml
---
- hosts: web:!10.0.0.202
remote_user: root
gather_facts: no
tasks:
- name: install httpd
yum: name=httpd
- name: start httpd
service: name=httpd state=started enabled=yes
[root@ansible playbook]#cat remove_httpd.yaml
---
- hosts: web:!10.0.0.202
remote_user: root
gather_facts: no
tasks:
- name: remove httpd
yum: name=httpd state=absent
1.1.4 其他元件說明
某任務的狀态再運作後為changed時,可通過 "notify" 通知給相應的handlers任務,還可以通過 "tags" 給task 打标簽,可在ansible-playbook指令上使用 -t 指定進行調用
1.1.5 Shell 腳本 VS playbook 案例
#SHELL腳本實作
#!/bin/bash
# 安裝Apache
yum install --quiet -y httpd
# 複制配置檔案
cp /tmp/httpd.conf /etc/httpd/conf/httpd.conf
cp/tmp/vhosts.conf /etc/httpd/conf.d/
# 啟動Apache,并設定開機啟動
systemctl enable --now httpd
#Playbook實作
---
- hosts: websrvs
remote_user: root
gather_facts: no
tasks:
- name: "安裝Apache"
yum: name=httpd
- name: "複制配置檔案"
copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/
- name: "複制配置檔案"
copy: src=/tmp/vhosts.conf dest=/etc/httpd/conf.d/
- name: "啟動Apache,并設定開機啟動"
service: name=httpd state=started enabled=yes
1.1.6 playbook 指令
格式
ansible-playbook [options] <filename.yml>
#常見選項
--syntax-check #文法檢查,可縮寫成--syntax,相當于bash -n
-C --check #模拟執行,隻檢測可能發生的改變,但不真正執行操作
--list-hosts #列出運作任務的主機
--list-tags #列出tag
--list-tasks #列出task
--limit 主機清單 #隻針對主機清單中的特定主機執行
-i inventory #指定主機清單檔案,通常一個項目對應一個主機清單檔案
--start-at-task start_at_task #從指定task開始執行,而非從頭開始,start_at_task為任務的name
-v -vv -vvv #顯示過程
1.2 Playbook 初步
1.2.1 利用 playbook 建立mysql使用者
---
- hosts: db
remote_user: root
gather_facts: no
tasks:
- name: create group
group: name=mysql system=yes gid=306
- name: create user
user: name=mysql shell=/sbin/nologin system=yes group=mysql uid=306 home/data/mysql create_home=no
1.2.2 利用playbook 安裝 / 解除安裝 nginx
---
# install nginx
- hosts: web
remote_user: root
tasks:
- name: add group nginx
group: name=nginx state=present
- name: add user nginx
user: name=nginx state=present group=nginx
- name: Install Nginx
yum: name=nginx state=present
# 目前目錄拷貝nginx.conf到files,并且建立index.html頁面檔案
- name: Config file
copy: src=files/nginx.conf dest=/etc/nginx/nginx.conf
- name: web page
copy: src=files/index.html dest=/usr/share/nginx/html/index.html
- name: start Nginx
service: name=nginx state=started enabled=yes
---
# remove nginx
- hosts: web
remote_user: root
tasks:
- name: stop nginx
service: name=nginx state=stopped
- name: del user nginx
user: name=nginx state=absent
- name: remove Nginx
yum: name=nginx state=absent
- name: remove web page
file: name=/usr/share/nginx/index.html state=absent
- name: remove Config file
file: name=/etc/nginx/nginx.conf state=absent
1.2.3 忽略錯誤 ignore_errors
如果一個task出錯,預設将不會繼續執行後續的其他task
利用 ignore_errors: yes 可以忽略此task錯誤,繼續向下執行其他task
---
- hosts: web
remote_user: root
ignore_errors: yes #忽略錯誤
tasks:
- name: error
command: /bin/false
- name: continue
command: wall continue
1.2.4 playbook 中使用handlers 和notify
Handlers 主要用于當關注的資源發生變化時,才會采取一定的操作,而notify對應的action可用于在每個play的最後被觸發,這樣可避免多次有改變發生時每次都執行指定的操作,僅在所有的變化發生完成後一次性的執行指定操作。
注意:
- 如果多個task通知了相同的handlers,此handlers僅會在所有task結束後運作一次
- 隻有notifly對應的task發生改變了才會通知hangdlers,沒有改變則不會觸發handlers
- handlers 是在所有前面的task都成功執行才會執行,如果前面任何一個task失敗,會導緻handlers跳過執行,可以使用 force_handlers: yes 強制執行handler
---
# install nginx
- hosts: web:!10.0.0.202
remote_user: root
gather_facts: no
force_handlers: yes # task中任何一個action失敗,仍強制執行 handlers
tasks:
- name: add group nginx
group: name=nginx state=present
- name: add user nginx
user: name=nginx state=present group=nginx
- name: Install Nginx
yum: name=nginx state=present
- name: Config file
copy: src=files/nginx.conf dest=/etc/nginx/nginx.conf
notify:
- Restart Nginx
- name: web page
copy: src=files/index1.html dest=/usr/share/nginx/html/index.html
- name: start Nginx
service: name=nginx state=started enabled=yes
handlers:
- name: Restart Nginx
service: name=nginx state=restarted
1.2.5 playbook中使用tags
在playbook檔案中,可以利用tags元件,為特定 task 指定标簽, 當在執行playbook時,可以隻執行特定tags的task,而非整個 playbook檔案
可以一個task對應多個tag,也可以多個task對應一個tag
---
# install nginx
- hosts: web:!10.0.0.202
remote_user: root
gather_facts: no
force_handlers: yes
tasks:
- name: add group nginx
group: name=nginx state=present
- name: add user nginx
user: name=nginx state=present group=nginx
- name: Install Nginx
yum: name=nginx state=present
- name: Config file
copy: src=files/nginx.conf dest=/etc/nginx/nginx.conf
tags: conf # 指定tag 标簽
notify:
- Restart Nginx
- name: web page
copy: src=files/index.html dest=/usr/share/nginx/html/index.html
- name: start Nginx
service: name=nginx state=started enabled=yes
handlers:
- name: Restart Nginx
service: name=nginx state=restarted
# 指定執行 tag标簽 conf, 其他的task不會重複執行
[root@ansible playbook]#ansible-playbook -t conf install_nginx.yml
PLAY [web:!10.0.0.202] *********************************************************************************************************************<strong>
TASK [Config file] </strong>***********************************************************************************************************************<strong>
changed: [10.0.0.7]
changed: [10.0.0.17]
RUNNING HANDLER [Restart Nginx] </strong>**********************************************************************************************************<strong>
changed: [10.0.0.7]
changed: [10.0.0.17]
PLAY RECAP </strong>*********************************************************************************************************************************
10.0.0.17 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.0.0.7 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1.2.6 playbook中使用變量
playbook中同樣支援變量
變量名: 僅能由字母,數字和下劃線組成,且隻能以字母開頭
變量調用方式:
通過 {{ variable_name }} 調用變量,且變量名前後建議加空格,有時用"{{ variable_name }}" 才生效
變量來源:
- ansible 的 setup子產品 遠端主機的所有變量都可以直接調用
- 通過指令行指定變量,優先級最高
ansible-playbook -e varname=value test.yml
- 在playbook檔案中定義
- 在獨立的變量yaml 檔案中定義
- 在主機清單檔案中定義
- 在項目中針對主機和主機組定義
- 在role中定義
變量的優先級從高到低如下
-e 選項定義變量 --> playbook中vars_files --> playbook中vars變量定義 --> host_vars/主機名檔案 --> 主機清單中主機變量 --> group_vars/主機組名檔案 --> group_vars/all檔案 --> 主機清單組檔案
1.2.6.1 使用setup 子產品中的變量
[root@ansible ~]#ansible 10.0.0.27 -m setup -a "filter=ansible_default_ipv4"
10.0.0.27 | SUCCESS => {
"ansible_facts": {
"ansible_default_ipv4": {
"address": "10.0.0.27",
"alias": "eth0",
"broadcast": "10.0.0.255",
"gateway": "10.0.0.2",
"interface": "eth0",
"macaddress": "00:0c:29:aa:78:a9",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "10.0.0.0",
"type": "ether"
},
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false
}
[root@ansible playbook]#vim var1.yml
---
- hosts: web
remote_user: root
gather_facts: yes # 預設 yes,可不寫
tasks:
- name: create log file
file: name=/data/{{ ansible_nodename }}.log state=touch
1.2.6.2 在playbook指令行中定義變量,-e指定變量
[root@ansible playbook]#vim var2.yml
---
- hosts: web:!10.0.0.202
remote_user: root
tasks:
- name: install package
yum: name={{ pkname }} state=present
[root@ansible playbook]#ansible-playbook -e pkname=vsftpd var2.yml
1.2.6.3 在playbook檔案中定義變量
[root@ansible playbook]#vim var3.yml
---
- hosts: web:!10.0.0.202
remote_user: root
vars:
username: user1
groupname: group1
tasks:
- name: Create {{ groupname }}
group: name={{ groupname }} state=present
- name: Create {{ username }}
user: name={{ username }} group={{ groupname }} state=present
# -e 指令行指定的變量優先級更高
[root@ansible playbook]#ansible-playbook -e "username=user2 groupname=group2" var3.yml
1.2.6.4 使用變量檔案
可以在一個獨立的playbook檔案中定義變量,在另一個playbook檔案中引用檔案中的變量,比playbook中定義的變量優先級高
[root@ansible playbook]#vim vars.yml
---
var1: vsftpd
var2: httpd
# var4 中引用 vars檔案中的變量
[root@ansible playbook]#vim var4.yml
---
- hosts: web:!10.0.0.202
vars_files: vars.yml
tasks:
- name: touch apps dir
file: path=/apps state=directory
- name: Create vsftpd log
file: name=/apps/{{ var1 }}.log state=touch
- name: Create httpd log
file: name=/apps/{{ var2 }}.log state=touch
1.2.6.5 針對主機和主機組的變量
- 在主機清單中針對所有項目的主機和主機組定義變量
所有項目的主機變量
在inventory 主機清單檔案中為指定的主機定義變量,在playbook中使用
[root@ansible playbook]#vim /etc/ansible/hosts
[web]
10.0.0.7 hname=www1 domain=magedu.io # 為單個主機設定變量
10.0.0.17 hname=www2
10.0.0.202
[web:vars] # 給web組設定變量
mark="-"
[all:vars] # 為所有組設定變量
domain=magedu.org
[root@ansible playbook]#ansible web -m hostname -a 'name={{ hname }}{{ mark }}{{ domain }}'
10.0.0.202 | FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: 'hname' is undefined"
}
10.0.0.17 | CHANGED => {
"ansible_facts": {
"ansible_domain": "org",
"ansible_fqdn": "www2-magedu.org",
"ansible_hostname": "www2-magedu",
"ansible_nodename": "www2-magedu.org",
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"name": "www2-magedu.org"
}
10.0.0.7 | CHANGED => {
"ansible_facts": {
"ansible_domain": "io",
"ansible_fqdn": "www1-magedu.io",
"ansible_hostname": "www1-magedu",
"ansible_nodename": "www1-magedu.io",
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"name": "www1-magedu.io"
}
- 針對目前項目的主機和主機組的變量
生産建議在項目目錄中建立額外的兩個變量目錄,分别是 host_vars 和 group_vars
host_vars下面的檔案名和主機清單主機名一緻,針對單個主機進行變量定義,格式:host_vars/hostname
group_vars下面的檔案名和主機清單中組名一緻,針對單個組進行變量定義,格式:group_vars/groupname
group_vars/all 檔案内定義的變量對所有組都有效
[root@ansible ansible]#tree
.
├── group_vars
│ ├── all
│ └── web
├── host_vars
│ ├── 10.0.0.17
│ └── 10.0.0.7
└── project_vars.yml
2 directories, 5 files
[root@ansible ansible]#cat host_vars/10.0.0.17
id: 2
[root@ansible ansible]#cat host_vars/10.0.0.7
id: 1
[root@ansible ansible]#cat group_vars/web
name: web
[root@ansible ansible]#cat group_vars/all
domain: magedu.cn
[root@ansible ansible]#vim project_vars.yml
---
- hosts: web:!10.0.0.202
remote_user: root
tasks:
- name: Get variable
command: echo "{{name}}{{id}}.{{domain}}"
register: result # 定義一個名為 result變量
- name: print variable
debug:
msg: "{{result.cmd}}"
[root@ansible ansible]#ansible-playbook project_vars.yml
PLAY [web:!10.0.0.202] *********************************************************************************************************************<strong>
TASK [Gathering Facts] </strong>*******************************************************************************************************************<strong>
ok: [10.0.0.17]
ok: [10.0.0.7]
TASK [Get variable] </strong>**********************************************************************************************************************<strong>
changed: [10.0.0.7]
changed: [10.0.0.17]
TASK [print variable] </strong>********************************************************************************************************************<strong>
ok: [10.0.0.7] => {
"msg": [
"echo",
"web1.magedu.cn"
]
}
ok: [10.0.0.17] => {
"msg": [
"echo",
"web2.magedu.cn"
]
}
PLAY RECAP </strong>*********************************************************************************************************************************
10.0.0.17 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.0.0.7 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1.2.6.6 register 注冊變量
在playbook中可以使用register将捕獲的輸出儲存在臨時變量中,然後使用 debug 子產品進行顯示輸出
範例: 利用debug 子產品輸出變量
[root@ansible ansible]#vim register.yml
---
- hosts: web:!10.0.0.202
tasks:
- name: get variable
shell: hostname
register: name
- name: print variable
debug:
msg: "{{ name }}" #輸出register注冊的name變量的全部資訊,注意變量要加" "引起來
#msg: "{{ name.cmd }}" #顯示指令
#msg: "{{ name.rc }}" #顯示指令成功與否
#msg: "{{ name.stdout }}" #顯示指令的輸出結果為字元串形式
#msg: "{{ name.stdout_lines }}" #顯示指令的輸出結果為清單形式
#msg: "{{ name.stdout_lines[0] }}" #顯示指令的輸出結果為清單中的第一個元素
#說明
第一個 task中,使用了 register注冊變量名為 name,當shell子產品執行完畢後,會将資料放到該變量中
第二個 task中,使用了 debug子產品,并從變量 name中擷取資料
[root@ansible ansible]#ansible-playbook register.yml
PLAY [web:!10.0.0.202] *********************************************************************************************************************<strong>
TASK [Gathering Facts] </strong>*******************************************************************************************************************<strong>
ok: [10.0.0.17]
ok: [10.0.0.7]
TASK [get variable] </strong>**********************************************************************************************************************<strong>
changed: [10.0.0.7]
changed: [10.0.0.17]
TASK [print variable] </strong>********************************************************************************************************************<strong>
ok: [10.0.0.7] => {
"msg": {
"changed": true,
"cmd": "hostname", #顯示指令
"delta": "0:00:00.005647",
"end": "2022-06-12 22:12:28.347194",
"failed": false,
"rc": 0, #顯示指令成功與否
"start": "2022-06-12 22:12:28.341547",
"stderr": "", #顯示指令的錯誤輸出結果,為字元串形式
"stderr_lines": [], #顯示指令的錯誤輸出結果,為清單形式
"stdout": "www1-magedu.io", #顯示指令的輸出結果,為字元串形式
"stdout_lines": [ #顯示指令的輸出結果,為清單形式
"www1-magedu.io"
]
}
}
ok: [10.0.0.17] => {
"msg": {
"changed": true,
"cmd": "hostname",
"delta": "0:00:00.004464",
"end": "2022-06-12 22:12:28.440166",
"failed": false,
"rc": 0,
"start": "2022-06-12 22:12:28.435702",
"stderr": "",
"stderr_lines": [],
"stdout": "www2-magedu.org",
"stdout_lines": [
"www2-magedu.org"
]
}
}
PLAY RECAP </strong>*********************************************************************************************************************************
10.0.0.17 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.0.0.7 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
範例: 使用 register 注冊變量建立檔案
[root@ansible ansible]#vim register1.yml
---
- hosts: 10.0.0.7
tasks:
- name: get variable
shell: hostname
register: name
- name: create file
file: dest=/data/{{ name.stdout }}.log state=touch
[root@ansible ansible]#ansible-playbook register1.yml
# register 和 debug 子產品
[root@ansible apps]#vim debug_register.yml
---
- hosts: 10.0.0.202
tasks:
- name: get var1
shell: echo hello world
register: say_hi
- name: get var2
shell: "awk -F: 'NR==1{print $1}' /etc/passwd"
register: user
- debug: var=say_hi.stdout #自定義輸出變量代替msg
- debug: var=user.stdout #自定義輸出變量代替msg
[root@ansible apps]#ansible-playbook debug_register.yml
PLAY [10.0.0.202] **************************************************************************************************************************<strong>
TASK [Gathering Facts] </strong>*******************************************************************************************************************<strong>
ok: [10.0.0.202]
TASK [get var1] </strong>**************************************************************************************************************************<strong>
changed: [10.0.0.202]
TASK [get var2] </strong>**************************************************************************************************************************<strong>
changed: [10.0.0.202]
TASK [debug] </strong>*****************************************************************************************************************************<strong>
ok: [10.0.0.202] => {
"say_hi.stdout": "hello world"
}
TASK [debug] </strong>*****************************************************************************************************************************<strong>
ok: [10.0.0.202] => {
"user.stdout": "root"
}
PLAY RECAP </strong>*********************************************************************************************************************************
10.0.0.202 : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1.2.6.7 案例: 利用變量批量部署MySQL5.7 或8.0
[root@ansible ansible]#cat install_mysql5.7or8.0.yml
---
# install mysql-5.7.17-linux-glibc2.5-x86_64.tar.gz
# install mysql-8.0.19-linux-glibc2.12-x86_64.tar.xz
#
- hosts: db
remote_user: root
gather_facts: yes
vars:
mysql_version: 5.7.17
mysql_file: mysql-{{mysql_version}}-linux-glibc2.5-x86_64.tar.gz
mysql_root_password: 123456
tasks:
- name: install packages
yum: name=libaio,numactl-libs state=latest
- name: create mysql group
group: name=mysql gid=306
- name: create mysql user
user: name=mysql uid=306 group=mysql shell=/sbin/nologin system=yes create_home=no home=/data/mysql
- name: 複制MySQL壓縮包到遠端主機并解壓縮到指定目錄
unarchive: src=/data/files/{{ mysql_file }} dest=/usr/local/ owner=root group=root
- name: 建立軟連結 /usr/local/mysql
file: src=/usr/local/mysql-{{mysql_version}}-linux-glibc2.5-x86_64 dest=/usr/local/mysql state=link
- name: data dir
shell: /usr/local/mysql/bin/mysqld --initialize-insecure --user=mysql --datadir=/data/mysql
tags: data
- name: Config my.cnf
copy: src=/data/files/my.cnf dest=/etc/my.cnf
- name: service script
shell: /bin/cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
- name: Path variable
copy: content='PATH=/usr/local/mysql/bin:$PATH' dest=/etc/profile.d/mysql.sh
- name: 使 Path variable生效
shell: /bin/source /etc/profile.d/mysql.sh
- name: enable service
shell: chkconfig --add mysqld;/etc/init.d/mysqld start
tags: service
- name: change password
shell: /usr/local/mysql/bin/mysqladmin -uroot password {{mysql_root_password}}
1.3 template 模闆
子產品是一個文本檔案,可以做為生成檔案的模闆,并且模闆中還可嵌套jinja2文法
template功能:可以根據和參考子產品檔案,動态生成相類似的配置檔案
template檔案必須存放于template目錄下,且命名為 .j2 結尾
yaml / yml 檔案需和template目錄平級,目錄結構如下示例:
./
├── temnginx.yml
└── templates
└── nginx.conf.j2
範例: 利用template 同步nginx配置檔案
#準備 templates/nginx.conf.j2檔案
[root@ansible ansible]#mkdir templates
[root@ansible ansible]#cp files/nginx.conf templates/nginx.conf.j2
[root@ansible ansible]#tree
.
├── ansible.cfg
├── files
│ ├── index.html
│ └── nginx.conf
├── install_nginx.yml
├── remove_nginx.yml
└── templates
└── nginx.conf.j2
[root@ansible ansible]#vim install_nginx.yml
---
# install nginx
- hosts: web:!10.0.0.202
remote_user: root
gather_facts: yes
force_handlers: yes
tasks:
- name: add group nginx
group: name=nginx state=present
- name: add user nginx
user: name=nginx state=present group=nginx
- name: Install Nginx
yum: name=nginx state=present
- name: Config file
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
tags: conf
notify:
- Restart Nginx
- name: web page
copy: src=files/index.html dest=/usr/share/nginx/html/index.html
- name: start Nginx
service: name=nginx state=started enabled=yes
handlers:
- name: Restart Nginx
service: name=nginx state=restarted
[root@ansible ansible]#vim templates/nginx.conf.j2
...
worker_processes {{ ansible_processor_vcpus+2 }};
...
1.3.1 template中使用流程控制 for 和 if
1.3.1.1 for 循環
格式
{% for i in EXPR %}
...
{% endfor %}
範例:
#templates/nginx.conf1.j2
{% for vhost in nginx_vhosts %}
server {
listen {{ vhost }}
}
{% endfor %}
#for.yml
---
- hosts: web
remote_user: root
vars:
nginx_vhosts:
- 81
- 82
- 83
tasks:
- name: template config
template: src=nginx.conf1.j2 dest=/data/nginx.conf
[root@ubuntu2004 data]#cat nginx.conf
server {
listen 81
}
server {
listen 82
}
server {
listen 83
}
範例: for 通過key=value讀取變量值
#
[root@ansible ansible]#vim for1.yml
---
- hosts: web
remote_user: root
vars:
nginx_vhosts:
- listen: 8080
server_name: "web1.abc.com"
root: "/var/www/nginx/web1/"
- listen: 8081
server_name: "web2.abc.com"
root: "/var/www/nginx/web2/"
- {listen: 8082, server_name: "web3.abc.com",root: "/var/www/nginx/web3/"}
tasks:
- name: template config
template: src=for1.j2 dest=/data/for1.conf
[root@ansible ansible]#vim templates/for1.j2
{% for vhost in nginx_vhosts %}
server {
listen {{ vhost.listen }}
server_name {{ vhost.server_name }}
root {{ vhost.root }}
}
{% endfor %}
#執行結果
[root@ansible ansible]#ansible-playbook for1.yml --limit 10.0.0.202
[root@ubuntu2004 ~]#cat /data/for1.conf
server {
listen 8080
server_name web1.abc.com
root /var/www/nginx/web1/
}
server {
listen 8081
server_name web2.abc.com
root /var/www/nginx/web2/
}
server {
listen 8082
server_name web3.abc.com
root /var/www/nginx/web3/
}
1.3.1.2 if 條件判斷
在模闆檔案使用 if 條件判斷, 決定是否生成相關的配置資訊
範例:
[root@ansible ansible]#vim if.yml
---
- hosts: web
remote_user: root
gather_facts: yes
vars:
nginx_vhosts:
- web1:
listen: 8080
root: "/var/www/nginx/web1/"
- web2:
listen: 8080
root: "/var/www/nginx/web2/"
- web3:
listen: 8080
server_name: "web3.abc.com"
root: "/var/www/nginx/web3/"
tasks:
- name: template config
template: src=if.conf.j2 dest=/data/if.conf
# if.conf.j2
[root@ansible ansible]#vim templates/if.conf.j2
{% for vhost in nginx_vhosts %}
server {
listen {{ vhosts.listen }}
{% if vhosts.server_name is defined %}
server_name {{ vhosts.server_name }} #注意不縮進,頂格
{% endif %}
root {{ vhosts.root }} #注意不縮進,頂格
}
{% endfor %}
#生成的結果
[root@ubuntu2004 ~]#cat /data/if.conf
server {
listen 8080
root /var/www/nginx/web1/
}
server {
listen 8080
root /var/www/nginx/web2/
}
server {
listen 8080
server_name web3.abc.com
root /var/www/nginx/web3/
}
1.4 使用循環疊代
疊代:當有需要重複性執行的任務時,可以使用疊代機制
1.4.1 疊代 with_items (loop)
對疊代項的引用,固定内置變量名為 "item"
要在task中使用 with_items給定要疊代的元素類别
注意:ansible2.5版本後,可以用loop代替with_items
清單元素格式:
- 字元串
- 字典
範例:
# loop.yml
---
- hosts: web
remote_user: root
tasks:
- name: add server users
user: name={{ item }} state=present
loop: # ansible2.5版本前使用 with_items
- user1
- user2
- user3
#上面語句功能等同于下面的語句
- name: add server users
user: name=testuser1 state=present
- name: add several users
user: name=testuser2 state=present
- name: add several users
user: name=testuser3 state=present
1.4.2 疊代嵌套子變量
在疊代中,還可以嵌套子變量,關聯多個變量在一起使用
示例: 批量建立使用者
# 通過item 鍵值對取子變量
[root@ansible ansible]#vim items_user.yml
---
- hosts: web
tasks:
- name: add groups
group: name={{ item }} state=present
with_items:
- nginx
- mysql
- redis
- name: add users
user: name={{ item.user }} group={{ item.group }} uid={{ item.uid }} state=present
with_items:
- { user: 'nginx', group: 'nginx', uid: '80' }
- { user: 'mysql', group: 'mysql', uid: '3306' }
- { user: 'redis', group: 'redis', uid: '6379' }
1.4.3 until 循環
範例:
# until 為false時才會執行循環,為true則 退出循環
[root@ansible ansible]#vim until.yml
---
- hosts: localhost
gather_facts: false
tasks:
- debug: msg="until"
until: false
retries: 3 #預設值,重複3次
delay: 1 #延遲1S
1.4.4 with_lines 逐行處理
# with_lines 逐行處理
[root@ansible ansible]#vim with_lines.yml
- hosts: localhost
tasks:
- debug: msg={{ item }}
with_lines: free -h #類似 with_items集合
1.5 playbook 中使用 when
when語句可以實作條件測試
# when的清單形式表示and關系
[root@ansible ansible]#vim when.yml
---
#關閉Ubuntu版本的主機
- hosts: web
tasks:
- name: 關閉 Ubuntu 主機
reboot:
when:
- ansible_facts['distribution'] == "Ubuntu"
- ansible_facts['distribution_major_version'] == "20" # and關系
1.6 分組 block
當想在滿足一個條件下,執行多個任務時,就需要分組,而不再每個任務都用when
[root@ansible ansible]#vim block.yml
---
- hosts: web
tasks:
- block:
- debug: msg="first"
- debug: msg="second"
when:
- ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "7"
[root@ansible ansible]#ansible-playbook block.yml
...
TASK [debug] *******************************************************************************************************************************<strong>
ok: [10.0.0.7] => {
"msg": "first"
}
ok: [10.0.0.17] => {
"msg": "first"
}
skipping: [10.0.0.202]
TASK [debug] </strong>*******************************************************************************************************************************
ok: [10.0.0.7] => {
"msg": "second"
}
ok: [10.0.0.17] => {
"msg": "second"
}
skipping: [10.0.0.202]
...
1.7 changed_when
檢查task傳回結果,決定是否繼續向下執行
[root@ansible ansible]#vim changed_when.yml
---
- hosts: 10.0.0.17
remote_user: root
gather_facts: yes
tasks:
- name: install nginx
yum: name=nginx
- name: config file
template: src=nginx.conf.j2 dest="/etc/nginx/nginx.conf"
notify: restart nginx
- name: check config
shell: /usr/sbin/nginx -t
register: check_nginx_config
changed_when:
- (check_nginx_config.stdout.find('successful')) #如果/usr/sbin/nginx -t執行結果中有successful字元串,則繼續執行,沒有則停止向下執行
- false #/usr/sbin/nginx -t 每次成功執行是changed狀态,關閉此change狀态
- name: start service
service: name=nginx state=started enabled=yes
handlers:
- name: restart nginx
service: name=nginx state=restarted
1.8 yaml 檔案的互相調用
利用include 或 include_tasks 可以在某個task中調用其他的隻有task内容的yaml 檔案
[root@ansible ansible]#vim a.yml
---
- hosts: web
tasks:
- name: run a job
command: wall run a job
- name: join b.yml
include: b.yml #調用另一個隻有task内容的yaml檔案
[root@ansible ansible]#vim b.yml
- name: run b job
command: wall run b job
使用 import_playbook 将多個包含完整内容的yml檔案由一個yml統一調用
[root@ansible ansible]#cat total_tasks.yml
- import_playbook: task1.yml
- import_playbook: task2.yml
[root@ansible ansible]#cat task1.yml task2.yml
---
- hosts: web
gather_facts: false
tasks:
- name: run task1 job
command: wall run task1 job
---
- hosts: web
gather_facts: false
tasks:
- name: run task2 job
command: wall run task2 job
2 roles 角色
roles就是通過分别将變量、檔案、任務、模闆及處理器放置在單獨的目錄中,并可以使用便捷地include它們的一種機制。角色一般用于基于主機建構服務的場景中,也可以用于建構守護程序等場景中。
roles: 多個角色的集合目錄,可以将多個role,分别放在roles目錄下的獨立子目錄中,代碼複用度高
[root@ansible ansible]#tree roles/
roles/
├── mysql
│ ├── files
│ │ └── main.yml
│ ├── handlers
│ │ └── main.yml
│ ├── services
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ ├── templates
│ │ └── main.yml
│ └── vars
│ └── main.yml
└── nginx
├── files
│ └── main.yml
├── handlers
│ └── main.yml
├── services
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
│ └── main.yml
└── vars
└── main.yml
預設存放roles路徑
/root/.ansible/roles
/usr/share/ansible/roles
/etc/ansible/roles
2.1 建立 role
- 建立roles的目錄結構,在以roles命名目錄下分别建立以各角色名稱命名的目錄,如nginx,mysql等,在每個角色命名的目錄中分别建立相關目錄和檔案,如tasks、files、handlers、templates、vars等目錄;
- 編寫和準備role的功能檔案
- 編寫playbook檔案調用需要的角色應用于指定的主機
[root@ansible ansible]#mkdir roles/{nginx,mysql}/{tasks,files,templates,vars,handlers} -pv
[root@ansible ansible]#touch roles/{nginx,mysql}/{tasks,vars,templates,files,handlers}/main.yml
2.2 playbook 調用角色
[root@ansible ansible]#vim role.yml # 和roles目錄平級建立調用角色檔案
---
- hosts: localhost
roles:
- nginx
- mysql
2.3 案例: nginx 角色
#建立task檔案
[root@ansible nginx]#cat tasks/main.yml
- include: group.yml
- include: user.yml
- include: install.yml
- include: config.yml
- include: data.yml
- include: service.yml
[root@ansible nginx]#cat tasks/user.yml
- name: add user nginx
user: name=nginx state=present group=nginx
[root@ansible nginx]#cat tasks/install.yml
- name: Install Nginx
yum: name=nginx state=present
[root@ansible nginx]#cat tasks/config.yml
- name: Config file
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
tags: conf
notify:
- Restart Nginx
[root@ansible nginx]#cat tasks/data.yml
- name: web page
copy: src=index.html dest=/usr/share/nginx/html/index.html
[root@ansible nginx]#cat tasks/service.yml
- name: start Nginx
service: name=nginx state=started
#建立handlers檔案
[root@ansible nginx]#cat handlers/main.yml
- name: Restart Nginx
service: name=nginx state=restarted
#建立file檔案,也可以跨角色調用檔案
[root@ansible nginx]#cat files/index.html
<h1> nginx websize </h1>
#建立templates檔案
[root@ansible nginx]#vim templates/nginx.conf.j2
...省略...
user {{ web_user }};
worker_processes {{ ansible_processor_vcpus+2 }};
...省略...
#建立vars變量檔案
[root@ansible nginx]#cat vars/main.yml
web_user: nginx
#目錄結構如下
[root@ansible nginx]#tree
.
├── files
│ └── index.html
├── handlers
│ └── main.yml
├── tasks
│ ├── config.yml
│ ├── data.yml
│ ├── group.yml
│ ├── install.yml
│ ├── main.yml
│ ├── service.yml
│ └── user.yml
├── templates
│ └── nginx.conf.j2
└── vars
└── main.yml
#在playbook中調用角色
[root@ansible ~]#vim /data/ansible/role_nginx.yml
---
# nginx role
- hosts: web
roles:
- nginx