实现任务控制,可以处理一些预料之中的错误,让程序更加高效执行,完成更复杂的项目
1、loops循环
使用循环可以灵活的管理很多数据
1.1 通过变量文件写loop循环
[student@workstation ansible-var]$ cat group_vars/all
# 定义变量矩阵,存储相关信息
---
packages:
- name: httpd
state: started
- name: firewalld
state: started
[student@workstation ansible-var]$ cat install.yml
# 使用循环安装软件包和服务
---
- name: define var in playbook
hosts: all
# 由于是通过group_vars目录下指定的all文件,所以此步骤可以忽略
# vars_files:
# - group_vars/all
tasks:
- name: install server
yum:
name: "{{ item['name'] }}"
state: latest
loop: "{{ packages }}"
- name: start service
service:
name: "{{ item['name'] }}"
state: "{{ item.state }}"
enabled: yes
loop: "{{ packages }}"
[student@workstation ansible-var]$ ansible-playbook install.yml
PLAY [define var in playbook] ***************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************************************
ok: [serverd]
ok: [servera]
ok: [serverc]
ok: [serverb]
TASK [install server] ***********************************************************************************************************************************************************************************************************************************
ok: [serverb] => (item={'name': 'httpd', 'state': 'started'})
ok: [serverc] => (item={'name': 'httpd', 'state': 'started'})
ok: [serverd] => (item={'name': 'httpd', 'state': 'started'})
ok: [servera] => (item={'name': 'httpd', 'state': 'started'})
ok: [serverb] => (item={'name': 'firewalld', 'state': 'started'})
ok: [serverd] => (item={'name': 'firewalld', 'state': 'started'})
ok: [serverc] => (item={'name': 'firewalld', 'state': 'started'})
ok: [servera] => (item={'name': 'firewalld', 'state': 'started'})
TASK [start service] ************************************************************************************************************************************************************************************************************************************
changed: [servera] => (item={'name': 'httpd', 'state': 'started'})
changed: [serverb] => (item={'name': 'httpd', 'state': 'started'})
changed: [serverc] => (item={'name': 'httpd', 'state': 'started'})
changed: [serverd] => (item={'name': 'httpd', 'state': 'started'})
ok: [servera] => (item={'name': 'firewalld', 'state': 'started'})
ok: [serverb] => (item={'name': 'firewalld', 'state': 'started'})
changed: [serverc] => (item={'name': 'firewalld', 'state': 'started'})
changed: [serverd] => (item={'name': 'firewalld', 'state': 'started'})
PLAY RECAP **********************************************************************************************************************************************************************************************************************************************
servera : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverb : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverc : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverd : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1.2 直接在playbook定义变量循环
# 使用循环安装软件包和服务,直接定义在playbook中,生产环境下不建议使用
---
- name: define var in playbook
hosts: all
vars:
packages:
- name: httpd
state: started
- name: firewalld
state: started
tasks:
- name: install server
yum:
name: "{{ item['name'] }}"
state: latest
loop: "{{ packages }}"
- name: start service
service:
name: "{{ item['name'] }}"
state: "{{ item.state }}"
enabled: yes
loop: "{{ packages }}"
1.3 with循环
在ansible2.5之前,使用的是with循环,该方法已经被淘汰了,with的大部分用法可以被loop替代
loop键 | 描述 |
---|---|
with_items | 可以无缝替换成loop,遍历一串字符 |
with_file | 列出控制节点文件名,使用文件列表的相应内容 |
with_sequence | 在每次迭代期间以生成的顺序生成项 |
[student@workstation ansible-var]$ cat items.yml
---
- name: "were prefixed with:with_"
hosts: all
gather_facts: no
vars:
home:
- xiaoming
- xiaohong
- xiaoming
- mmx
tasks:
- name: use with
debug:
msg: "An item: {{ item }}"
with_items: "{{ home }}"
[student@workstation ansible-var]$ ansible-playbook items.yml -C
PLAY [were prefixed with:with_] *************************************************************************************************************************************************************************************************************************
TASK [use with] *****************************************************************************************************************************************************************************************************************************************
ok: [servera] => (item=xiaoming) => {
"msg": "An item: xiaoming"
}
ok: [servera] => (item=xiaohong) => {
"msg": "An item: xiaohong"
}
ok: [servera] => (item=xiaoming) => {
"msg": "An item: xiaoming"
}
ok: [servera] => (item=mmx) => {
"msg": "An item: mmx"
}
ok: [serverc] => (item=xiaoming) => {
"msg": "An item: xiaoming"
}
ok: [serverc] => (item=xiaohong) => {
"msg": "An item: xiaohong"
}
ok: [serverc] => (item=xiaoming) => {
"msg": "An item: xiaoming"
}
ok: [serverc] => (item=mmx) => {
"msg": "An item: mmx"
}
ok: [serverb] => (item=xiaoming) => {
"msg": "An item: xiaoming"
}
ok: [serverd] => (item=xiaoming) => {
"msg": "An item: xiaoming"
}
ok: [serverd] => (item=xiaohong) => {
"msg": "An item: xiaohong"
}
ok: [serverd] => (item=xiaoming) => {
"msg": "An item: xiaoming"
}
ok: [serverd] => (item=mmx) => {
"msg": "An item: mmx"
}
ok: [serverb] => (item=xiaohong) => {
"msg": "An item: xiaohong"
}
ok: [serverb] => (item=xiaoming) => {
"msg": "An item: xiaoming"
}
ok: [serverb] => (item=mmx) => {
"msg": "An item: mmx"
}
1.4 使用egister字段捕获task任务
[root@mmx_ansible ansible_taskcontol]# cat vars/hello.yml
---
hostname: localhost
family:
- name: xiaoming
age: 18
like: draw
- name: xiaogang
age: 22
like: sing
- name: mmx
age: 22
like: play games
[root@mmx_ansible ansible_taskcontol]# cat with_list.yml
---
- name: user loop
hosts: "{{ hostname }}"
vars_files:
- vars/hello.yml
tasks:
- name: one
debug:
msg: hello world
- name: two
debug:
msg: "family members have {{ item['name'] }}"
# loop: "{{ family }}"
with_list: "{{ family }}"
register: echo_results
- debug: var=echo_results
在"results": [……]字段中可以找到一些有用的信息
- item遍历的一些参数
- msg输出的一些结果
TASK [debug] *******************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"echo_results": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"failed": false,
# 这里列出了变量矩阵中的一些参数
"item": {
"age": 18,
"like": "draw",
"name": "xiaoming"
},
# 这里可以看到msg参数的输出结果
"msg": "family members have xiaoming"
},
{
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"item": {
"age": 22,
"like": "sing",
"name": "xiaogang"
},
"msg": "family members have xiaogang"
},
{
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"item": {
"age": 22,
"like": "play games",
"name": "mmx"
},
"msg": "family members have mmx"
}
],
"skipped": false
}
}
2、 task中的条件判断
通过判断系统的相关信息(比如可用内存),或者command中输出的命令,facts变量等参数,都能让计算机在通过条件判断的方式,做出对应策略
2.1 条件判断语法
使用when关键字指定需要判断的task
2.2 通过变量开关作为判断条件
值 | 状态 |
---|---|
True | 正确(开) |
yes | 正确(开) |
1 | 正确(开) |
False | 错误(关) |
no | 错误(关) |
0 | 错误(关) |
[root@mmx_ansible ansible_taskcontol]# cat conditional_syntax.yml
---
- name: 条件判断
hosts: all
vars:
# 通过这个开关来确定when是否执行
whether_run: yes
tasks:
- name: 输出hello
debug:
msg: hello!
register: mmx
when: whether_run
- debug: var=mmx
[root@mmx_ansible ansible_taskcontol]# ansible-playbook conditional_syntax.yml
PLAY [条件判断] ****************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
[WARNING]: Platform linux on host localhost is using the discovered Python interpreter at /usr/bin/python3.8, but future installation of another Python interpreter could change the meaning of that path. See
https://docs.ansible.com/ansible-core/2.12/reference_appendices/interpreter_discovery.html for more information.
ok: [localhost]
TASK [输出hello] ***************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "hello!"
}
TASK [debug] *******************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"mmx": {
"changed": false,
"failed": false,
"msg": "hello!"
}
}
PLAY RECAP *********************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2.3 通过defind或undefined作为判断条件
# 定义的变量文件
[root@mmx_ansible ansible_taskcontol]# cat vars/package.yml
---
packages:
- name: httpd
state: latest
- name: firewalld
state: present
[root@mmx_ansible ansible_taskcontol]# cat exist_not.yml
---
- name: 判断是否存在
hosts: all
vars_files:
- vars/package.yml
tasks:
- name: 判断字段是否存在1
debug:
msg: "名称 {{ item.name }}"
loop: "{{ packages }}"
when: item.name is defined
- name: 判断字段是否存在2
debug:
msg: "状态 {{ item.state }}"
loop: "{{ packages }}"
when: item.state is defined
- name: 判断字段是否存在3
debug:
msg: "版本 {{ item.version }}"
loop: "{{ packages }}"
when: item.version is defined
# 由于最后一个version在变量文件中不存在,所有不会执行
[root@mmx_ansible ansible_taskcontol]# ansible-playbook exist_not.yml
PLAY [判断是否存在] ************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [判断字段是否存在1] *******************************************************************************************************************************************************************************************************
ok: [localhost] => (item={'name': 'httpd', 'state': 'latest'}) => {
"msg": "名称 httpd"
}
ok: [localhost] => (item={'name': 'firewalld', 'state': 'present'}) => {
"msg": "名称 firewalld"
}
TASK [判断字段是否存在2] *******************************************************************************************************************************************************************************************************
ok: [localhost] => (item={'name': 'httpd', 'state': 'latest'}) => {
"msg": "状态 latest"
}
ok: [localhost] => (item={'name': 'firewalld', 'state': 'present'}) => {
"msg": "状态 present"
}
2.4 常用的判断条件
操作 | 示例 |
---|---|
是否等于(字符串) | ansible_machine == 'x86_64' |
是否等于(数字) | max_memory == 512 |
小于 | min_memory < 128 |
大于 | min_memory > 128 |
小于等于 | min_memory <= 128 |
大于等于 | min_memory >= 128 |
不等于 | min_memory != 128 |
已定义 | min_memory is defined |
未定义 | min_memory is undefined |
是否为真(1、True、yes) | memory_available |
是否为假(0、False、no) | not memory_available |
在其中包含 | ansible_distribution in supported_distors |
2.5 使用in参数
---
- name: 系统版本
hosts: all
vars:
supported_distros:
- RedHat
- Fedora
- Rocky
tasks:
- name: one
debug:
msg: "{{ ansible_distribution }}"
# 判断系统架构是否在支持列表里面
when: ansible_distribution in supported_distros
[root@mmx_ansible ansible_taskcontol]# ansible-playbook ainb.yml
PLAY [系统版本] ****************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
[WARNING]: Platform linux on host localhost is using the discovered Python interpreter at /usr/bin/python3.8, but future installation of another Python interpreter could change the meaning of that path. See
https://docs.ansible.com/ansible-core/2.12/reference_appendices/interpreter_discovery.html for more information.
ok: [localhost]
TASK [one] *********************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "Rocky"
}
PLAY RECAP *********************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2.6 多条件判断
# 判断系统和版本
[root@mmx_ansible ansible_taskcontol]# cat multiple.yaml
---
- name: 系统版本
hosts: all
vars:
supported_distros:
- RedHat
- Fedora
- Rocky
version:
- "8.0"
- "8.2"
- "8.5"
- "7.0"
tasks:
- name: one
debug:
msg: "distribution:{{ ansible_distribution }} version: {{ ansible_distribution_version }} "
when: ansible_distribution in supported_distros and ansible_distribution_version in version
[root@mmx_ansible ansible_taskcontol]# ansible-playbook multiple.yaml
PLAY [系统版本] ****************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
[WARNING]: Platform linux on host localhost is using the discovered Python interpreter at /usr/bin/python3.8, but future installation of another Python interpreter could change the meaning of that path. See
https://docs.ansible.com/ansible-core/2.12/reference_appendices/interpreter_discovery.html for more information.
ok: [localhost]
TASK [one] *********************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "distribution:Rocky version: 8.5 "
}
PLAY RECAP *********************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2.7 loop和条件判断混合使用
[student@workstation ansible-var]$ cat loop_conditional.yml
---
- name: loop和when混合使用
hosts: all
tasks:
- name: install package
debug:
msg: hello world
loop: "{{ ansible_mounts }}"
# 判断item.mount和item.size_available是否符合要求
when: item.mount == '/' and item.size_available > 300000000
[student@workstation ansible-var]$ ansible-playbook loop_conditional.yml
PLAY [loop和when混合使用] ***********************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
ok: [servera]
ok: [serverd]
ok: [serverb]
ok: [serverc]
TASK [install package] *********************************************************************************************************************************************************************************************************
ok: [servera] => (item={'mount': '/', 'device': '/dev/vda1', 'fstype': 'xfs', 'options': 'rw,seclabel,relatime,attr2,inode64,noquota', 'size_total': 10725863424, 'size_available': 9079398400, 'block_size': 4096, 'block_total': 2618619, 'block_available': 2216650, 'block_used': 401969, 'inode_total': 5242304, 'inode_available': 5196022, 'inode_used': 46282, 'uuid': '884f47c9-a69d-4c5b-915d-6b7c9c74c923'}) => {
"msg": "hello world"
}
ok: [serverb] => (item={'mount': '/', 'device': '/dev/vda1', 'fstype': 'xfs', 'options': 'rw,seclabel,relatime,attr2,inode64,noquota', 'size_total': 10725863424, 'size_available': 8889929728, 'block_size': 4096, 'block_total': 2618619, 'block_available': 2170393, 'block_used': 448226, 'inode_total': 5242304, 'inode_available': 5195789, 'inode_used': 46515, 'uuid': '884f47c9-a69d-4c5b-915d-6b7c9c74c923'}) => {
"msg": "hello world"
}
ok: [serverc] => (item={'mount': '/', 'device': '/dev/vda1', 'fstype': 'xfs', 'options': 'rw,seclabel,relatime,attr2,inode64,noquota', 'size_total': 10725863424, 'size_available': 9100800000, 'block_size': 4096, 'block_total': 2618619, 'block_available': 2221875, 'block_used': 396744, 'inode_total': 5242304, 'inode_available': 5196134, 'inode_used': 46170, 'uuid': '884f47c9-a69d-4c5b-915d-6b7c9c74c923'}) => {
"msg": "hello world"
}
ok: [serverd] => (item={'mount': '/', 'device': '/dev/vda1', 'fstype': 'xfs', 'options': 'rw,seclabel,relatime,attr2,inode64,noquota', 'size_total': 10725863424, 'size_available': 9102565376, 'block_size': 4096, 'block_total': 2618619, 'block_available': 2222306, 'block_used': 396313, 'inode_total': 5242304, 'inode_available': 5196136, 'inode_used': 46168, 'uuid': '884f47c9-a69d-4c5b-915d-6b7c9c74c923'}) => {
"msg": "hello world"
}
PLAY RECAP *********************************************************************************************************************************************************************************************************************
servera : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverb : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverc : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverd : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2.8 循环和判断练习
2.8.1 题目说明
- 启动环境
-
在database_dev主机组中安装MaiaDB并且开启服务
- 编辑playbook,加入变量mariadb_packages,两个值mariadb-server和python3-PyMySQL
- 使用yum模块通过loop的方式安装服务
- 使用service模块开启mariadb
- 运行playbook
- 在database_prod主机组中定义同样的任务,确保系统为RHEL时候才执行
- 编辑playbook
- 使用ad hoc方式查看系统版本
- 执行playbook
2.8.2 练习
[student@workstation ansible-var]$ lab control-flow start
Starting the lab on workstation:
· Verifying Ansible installation.............................. SUCCESS
· Creating working directory.................................. SUCCESS
· Deploying Ansible inventory................................. SUCCESS
· Deploying ansible.cfg....................................... SUCCESS
[student@workstation control-flow]$ cat ansible.cfg
[defaults]
inventory=inventory
remote_user=devops
[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False
[student@workstation control-flow]$ cat inventory
[database_dev]
servera.lab.example.com
[database_prod]
serverb.lab.example.com
# 第二题
[student@workstation control-flow]$ cat playbook.yml
---
- name: 开启MariaDB服务
hosts: database_dev
vars:
mariadb_packages:
- mariadb-server
- python3-PyMySQL
tasks:
- name: one
yum:
name: "{{ item }}"
state: present
loop: "{{ mariadb_packages }}"
- name: two
service:
name: mariadb
state: started
enabled: yes
[student@workstation control-flow]$ ansible-playbook playbook.yml
PLAY [开启MariaDB服务] *************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com]
TASK [one] *********************************************************************************************************************************************************************************************************************
changed: [servera.lab.example.com] => (item=mariadb-server)
ok: [servera.lab.example.com] => (item=python3-PyMySQL)
TASK [two] *********************************************************************************************************************************************************************************************************************
changed: [servera.lab.example.com]
PLAY RECAP *********************************************************************************************************************************************************************************************************************
servera.lab.example.com : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[student@workstation control-flow]$
[student@workstation control-flow]$ cat playbook.yml
---
- name: 开启MariaDB服务
hosts: database_dev
vars:
mariadb_packages:
- mariadb-server
- python3-PyMySQL
tasks:
- name: one
yum:
name: "{{ item }}"
state: present
loop: "{{ mariadb_packages }}"
- name: two
service:
name: mariadb
state: started
enabled: yes
- name: 运行mariadb
hosts: database_prod
vars:
mariadb_packages:
- mariadb-server
- python3-PyMySQL
tasks:
- name: one
yum:
name: "{{ item }}"
state: present
loop: "{{ mariadb_packages }}"
# 第三题
# 查看系统是不是RHEL
[student@workstation control-flow]$ ansible database_prod -a 'cat /etc/redhat-release' -u devops --become
serverb.lab.example.com | CHANGED | rc=0 >>
Red Hat Enterprise Linux release 8.0 (Ootpa)
[student@workstation control-flow]$ ansible-playbook playbook.yml
PLAY [开启MariaDB服务] *************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com]
TASK [one] *********************************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com] => (item=mariadb-server)
ok: [servera.lab.example.com] => (item=python3-PyMySQL)
TASK [two] *********************************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com]
PLAY [运行mariadb] ***************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
ok: [serverb.lab.example.com]
TASK [one] *********************************************************************************************************************************************************************************************************************
changed: [serverb.lab.example.com] => (item=mariadb-server)
changed: [serverb.lab.example.com] => (item=python3-PyMySQL)
PLAY RECAP *********************************************************************************************************************************************************************************************************************
servera.lab.example.com : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverb.lab.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
# 结束实验
[student@workstation ~]$ lab control-flow finish
Finishing up the lab on servera.lab.example.com:
· Removing packages on servera
· Removing mariadb-server................................... SUCCESS
· Removing python3-PyMySQL.................................. SUCCESS
· Removing packages on serverb
· Removing mariadb-server................................... SUCCESS
· Removing python3-PyMySQL.................................. SUCCESS
3、错误控制
一个playbook的执行,有的时候会出现一些预期的错误,playbook检测到错误之后就会停止运行,可以通过错误控制的方式忽略错误,或处理错误,来让playbook顺利执行
3.1 忽略错误
关键字: ignore_errors: yes
[student@workstation control-flow]$ cat mmx.yml
---
- name: ignore_errors关键字
hosts: database_dev
vars:
mariadb_packages:
- hello
- world
tasks:
- name: one
yum:
name: "{{ item }}"
state: present
ignore_errors: yes
loop: "{{ mariadb_packages }}"
- debug: msg="hello wold"
# 发现第一个task执行之后,报错了,后面task也能顺利运行
[student@workstation control-flow]$ ansible-playbook mmx.yml
PLAY [ignore_errors关键字] ********************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com]
TASK [one] *********************************************************************************************************************************************************************************************************************
failed: [servera.lab.example.com] (item=hello) => {"ansible_loop_var": "item", "changed": false, "failures": ["No package hello available."], "item": "hello", "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
failed: [servera.lab.example.com] (item=world) => {"ansible_loop_var": "item", "changed": false, "failures": ["No package world available."], "item": "world", "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
...ignoring
TASK [debug] *******************************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com] => {
"msg": "hello wold"
}
PLAY RECAP *********************************************************************************************************************************************************************************************************************
servera.lab.example.com : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
3.2 强制执行任务失败程序(handlers)
如果允许playbook失败了,可以加上force_handlers:yes这个参数,可以在重新运行playbook的时候一定执行该handlers字段
[student@workstation control-flow]$ cat force_handlers.yml
---
- name: 使用force_handles参数
hosts: all
force_handlers: yes
tasks:
- name: 无论如何都会执行
command: /bin/true
notify: restart the httpd
- name: 安装httpd服务
yum:
name: httpd
state: present
handlers:
- name: restart the httpd
service:
name: httpd
state: restarted
[student@workstation control-flow]$ ansible-playbook force_handlers.yml
PLAY [使用force_handles参数] ***
TASK [Gathering Facts] *****
ok: [servera.lab.example.com]
ok: [serverb.lab.example.com]
TASK [无论如何都会执行] ****
changed: [serverb.lab.example.com]
changed: [servera.lab.example.com]
TASK [安装httpd服务] ***
ok: [servera.lab.example.com]
ok: [serverb.lab.example.com]
RUNNING HANDLER [restart the httpd] ****
changed: [serverb.lab.example.com]
changed: [servera.lab.example.com]
PLAY RECAP *****
servera.lab.example.com : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverb.lab.example.com : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
### 3.3 handlers练习
#### 3.3.1 实验说明
1. 开启实验环境
2. 使用configure_db.yml的playbook文件,安装db_packages,开启db_service服务
3. 下载配置文件config_file_url到目标主机config_file_dst,所有者和所有组为mysql
4. 在yum模块下加入notify: 设置数据库密码,在get_url模块下加入notify:重启数据库,在下方使用handlers通过对应名称执行相应操作
```yaml
vars:
db_packages:
- mariadb-server
- python3-PyMySQL
db_service: mariadb
resources_url: http://materials.example.com/labs/control-handlers
config_file_url: "{{ resources_url }}/my.cnf.standard"
config_file_dst: /etc/my.cnf
3.3.2 练习
[student@workstation ~]$ lab control-handlers start
Starting lab control-handlers on workstation:
· Verifying Ansible installation.............................. SUCCESS
· Creating working directory.................................. SUCCESS
· Deploying Ansible inventory................................. SUCCESS
· Deploying ansible.cfg....................................... SUCCESS
· Download configure_db.yml................................... SUCCESS
[student@workstation ~]$ ls
control-errors control-flow control-handlers
[student@workstation ~]$ cd control-handlers/
[student@workstation control-handlers]$ ls
ansible.cfg configure_db.yml inventory
[student@workstation control-handlers]$ cat ansible.cfg
[defaults]
inventory=inventory
remote_user=devops
[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False
[student@workstation control-handlers]$ cat configure_db.yml
---
- name: Installing MariaDB server
hosts: databases
vars:
db_packages:
- mariadb-server
- python3-PyMySQL
db_service: mariadb
resources_url: http://materials.example.com/labs/control-handlers
config_file_url: "{{ resources_url }}/my.cnf.standard"
config_file_dst: /etc/my.cnf
tasks:
[student@workstation control-handlers]$ cat inventory
[databases]
servera.lab.example.com
[student@workstation control-handlers]$ cat configure_db.yml
---
- name: Installing MariaDB server
hosts: databases
vars:
db_packages:
- mariadb-server
- python3-PyMySQL
db_service: mariadb
resources_url: http://materials.example.com/labs/control-handlers
config_file_url: "{{ resources_url }}/my.cnf.standard"
config_file_dst: /etc/my.cnf
tasks:
- name: install {{ db_packages }}
yum:
name: "{{ db_packages }}"
state: present
notify:
- set db password
- name: start db_service
service:
name: "{{ db_service }}"
state: started
enabled: 1
- name: download {{ config_file_url }}
get_url:
url: "{{ config_file_url }}"
dest: "{{ config_file_dst }}"
owner: mysql
group: mysql
force: yes
notify:
- restart db service
handlers:
- name: restart db_service
service:
name: "{{ db_service }}"
state: restart
- name: set db password
mysql_user:
name: root
password: redhat
[student@workstation control-handlers]$ ansible-playbook configure_db.yml
PLAY [Installing MariaDB server] *****************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [servera.lab.example.com]
TASK [install ['mariadb-server', 'python3-PyMySQL']] *********************************************************************************************
ok: [servera.lab.example.com]
TASK [start db_service] **************************************************************************************************************************
ok: [servera.lab.example.com]
TASK [download http://materials.example.com/labs/control-handlers/my.cnf.standard] ***************************************************************
ok: [servera.lab.example.com]
PLAY RECAP ***************************************************************************************************************************************
servera.lab.example.com : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
3.4 指定结果“change”
参数: changed_when:false 可以让指定状态。通常在大量shell脚本的时候加入该参数,能发现预期错误
[student@workstation control-flow]$ cat specify_change.yml
---
- name: specify changed
hosts: all
tasks:
- name: "pint (hello world)"
shell:
cmd: echo 123
register: command_result"
# 如果输出结果有123,changed就为false
changed_when: "'123' in command_result['stdout']"
[student@workstation control-flow]$ ansible-playbook specify_change.yml
PLAY [specify changed] *********************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com]
ok: [serverb.lab.example.com]
TASK [pint (hello world)] ******************************************************************************************************************************************************************************************************
fatal: [servera.lab.example.com]: FAILED! => {"msg": "The conditional check ''123' in command_result['stdout']' failed. The error was: error while evaluating conditional ('123' in command_result['stdout']): Unable to look up a name or access an attribute in template string ({% if '123' in command_result['stdout'] %} True {% else %} False {% endif %}).\nMake sure your variable name does not contain invalid characters like '-': argument of type 'AnsibleUndefined' is not iterable"}
fatal: [serverb.lab.example.com]: FAILED! => {"msg": "The conditional check ''123' in command_result['stdout']' failed. The error was: error while evaluating conditional ('123' in command_result['stdout']): Unable to look up a name or access an attribute in template string ({% if '123' in command_result['stdout'] %} True {% else %} False {% endif %}).\nMake sure your variable name does not contain invalid characters like '-': argument of type 'AnsibleUndefined' is not iterable"}
PLAY RECAP *********************************************************************************************************************************************************************************************************************
servera.lab.example.com : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
serverb.lab.example.com : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
3.5 使用block的方式处理错误
block相当于把task作为一个逻辑组,把一些可能出问题的task放里面
参数 | 说明 |
---|---|
block | 作为一个逻辑组 |
rescue | 如果出错了,执行rescue组的任务 |
always | 无论是否出错,都会执行该区域 |
[student@workstation control-flow]$ !vim
vim block.yml
[student@workstation control-flow]$ cat block.yml
---
- name: 使用block处理错误
hosts: all
tasks:
- name: 使用block作为一个逻辑组
block:
- name: 升级数据库
shell:
cmd: /usr/local/lib/upgrade-database
rescue:
- name: 回滚数据库
shell:
cmd: /usr/local/lib/revert-database
always:
- name: 重启数据库
service:
name: mariadb
state: restarted
[student@workstation control-flow]$ ansible-playbook block.yml
PLAY [使用block处理错误] *************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
ok: [serverb.lab.example.com]
ok: [servera.lab.example.com]
TASK [升级数据库] *******************************************************************************************************************************************************************************************************************
fatal: [servera.lab.example.com]: FAILED! => {"changed": true, "cmd": "/usr/local/lib/upgrade-database", "delta": "0:00:00.002169", "end": "2022-07-30 21:12:52.595032", "msg": "non-zero return code", "rc": 127, "start": "2022-07-30 21:12:52.592863", "stderr": "/bin/sh: /usr/local/lib/upgrade-database: No such file or directory", "stderr_lines": ["/bin/sh: /usr/local/lib/upgrade-database: No such file or directory"], "stdout": "", "stdout_lines": []}
fatal: [serverb.lab.example.com]: FAILED! => {"changed": true, "cmd": "/usr/local/lib/upgrade-database", "delta": "0:00:00.002147", "end": "2022-07-30 21:12:52.596637", "msg": "non-zero return code", "rc": 127, "start": "2022-07-30 21:12:52.594490", "stderr": "/bin/sh: /usr/local/lib/upgrade-database: No such file or directory", "stderr_lines": ["/bin/sh: /usr/local/lib/upgrade-database: No such file or directory"], "stdout": "", "stdout_lines": []}
TASK [回滚数据库] *******************************************************************************************************************************************************************************************************************
fatal: [servera.lab.example.com]: FAILED! => {"changed": true, "cmd": "/usr/local/lib/revert-database", "delta": "0:00:00.002238", "end": "2022-07-30 21:12:52.882217", "msg": "non-zero return code", "rc": 127, "start": "2022-07-30 21:12:52.879979", "stderr": "/bin/sh: /usr/local/lib/revert-database: No such file or directory", "stderr_lines": ["/bin/sh: /usr/local/lib/revert-database: No such file or directory"], "stdout": "", "stdout_lines": []}
fatal: [serverb.lab.example.com]: FAILED! => {"changed": true, "cmd": "/usr/local/lib/revert-database", "delta": "0:00:00.002155", "end": "2022-07-30 21:12:52.898294", "msg": "non-zero return code", "rc": 127, "start": "2022-07-30 21:12:52.896139", "stderr": "/bin/sh: /usr/local/lib/revert-database: No such file or directory", "stderr_lines": ["/bin/sh: /usr/local/lib/revert-database: No such file or directory"], "stdout": "", "stdout_lines": []}
TASK [重启数据库] *******************************************************************************************************************************************************************************************************************
fatal: [servera.lab.example.com]: FAILED! => {"changed": false, "msg": "Could not find the requested service mariadb: host"}
changed: [serverb.lab.example.com]
PLAY RECAP *********************************************************************************************************************************************************************************************************************
servera.lab.example.com : ok=1 changed=0 unreachable=0 failed=2 skipped=0 rescued=1 ignored=0
serverb.lab.example.com : ok=2 changed=1 unreachable=0 failed=1 skipped=0 rescued=1 ignored=0
3.6 错误控制练习
3.6.1 实验说明
- 开启实验环境
- 创建一个playbook.yml文件,包含两个tasks
- 第一个tasks,有三个变量:web_package:http,db_package:mariadb-server和db_service:mariadb,http的值是错误的
- 使用yum模块,安装软件包(变量方式)
- 运行playbook,发现错误,加入ignore_errors忽略错误,使用block+rescue方式解决错误
- 添加一个always把mariadb服务开启
- 第二个tasks查看本地时间
- 使用command模块:data查看时间,通过register抓取相关信息
- 使用debug模块打印command_result.stdout信息
- 使用changed_when:false 标记该任务已完成
- 运行playbook
- 在第一个安装web_package下使用faild_when: web_package == 'httpd'之后查看运行结果
3.6.2 练习
[student@workstation ~]$ lab control-errors start
Starting lab control-errors on workstation:
· Verifying Ansible installation.............................. SUCCESS
· Creating working directory.................................. SUCCESS
· Deploying Ansible inventory................................. SUCCESS
· Deploying ansible.cfg....................................... SUCCESS
[student@workstation ~]$ cd control-errors/
[student@workstation control-errors]$ ls
ansible.cfg inventory
[student@workstation control-errors]$ cat ansible.cfg
[defaults]
inventory=inventory
remote_user=devops
[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False
[student@workstation control-errors]$ cat inventory
[databases]
servera.lab.example.com
使用ignore忽略错误,让play能运行完成
[student@workstation control-errors]$ cat playbook.yml
---
- name: 错误控制练习
hosts: databases
vars:
web_package: http
db_package: mariadb-server
db_service: mariadb
tasks:
- name: install {{ web_package }}
yum:
name: "{{ web_package }}"
state: present
ignore_errors: yes
- name: install {{ db_package }}
yum:
name: "{{ db_package }}"
state: present
PLAY [错误控制练习] ******************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com]
TASK [install http] ************************************************************************************************************************************************************************************************************
fatal: [servera.lab.example.com]: FAILED! => {"changed": false, "failures": ["No package http available."], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
...ignoring
TASK [install mariadb-server] **************************************************************************************************************************************************************************************************
changed: [servera.lab.example.com]
PLAY RECAP *********************************************************************************************************************************************************************************************************************
servera.lab.example.com : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
使用block+rescue+always处理错误
[student@workstation control-errors]$ cat playbook.yml
---
- name: 错误控制练习
hosts: databases
vars:
web_package: http
db_package: mariadb-server
db_service: mariadb
tasks:
- name: use block
block:
- name: install {{ web_package }}
yum:
name: "{{ web_package }}"
state: present
ignore_errors: yes
rescue:
- name: install {{ web_package }}
vars:
web_package: httpd
yum:
name: "{{ web_package }}"
state: present
- name: install {{ db_package }}
yum:
name: "{{ db_package }}"
state: present
always:
- name: start {{ db_service }}
service:
name: "{{ db_service }}"
state: started
enabled: yes
[student@workstation control-errors]$ ansible-playbook playbook.yml
PLAY [错误控制练习] ******************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com]
TASK [install http] ************************************************************************************************************************************************************************************************************
fatal: [servera.lab.example.com]: FAILED! => {"changed": false, "msg": "Unsupported parameters for (dnf) module: ignore_errors Supported parameters include: allow_downgrade, autoremove, bugfix, conf_file, disable_excludes, disable_gpg_check, disable_plugin, disablerepo, download_dir, download_only, enable_plugin, enablerepo, exclude, install_repoquery, install_weak_deps, installroot, list, lock_timeout, name, releasever, security, skip_broken, state, update_cache, update_only, validate_certs"}
TASK [install httpd] ***********************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com]
TASK [install mariadb-server] **************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com]
TASK [start mariadb] ***********************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com]
PLAY RECAP *********************************************************************************************************************************************************************************************************************
servera.lab.example.com : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=1 ignored=0
# 最终结果
[student@workstation control-errors]$ cat playbook.yml
---
- name: 错误控制练习
hosts: databases
vars:
web_package: http
db_package: mariadb-server
db_service: mariadb
tasks:
- name: use block
block:
- name: install {{ web_package }}
yum:
name: "{{ web_package }}"
state: present
# 加入该语句,发现变量不为httpd直接跳过
failed_when: web_package == 'httpd'
ignore_errors: yes
rescue:
- name: install {{ web_package }}
vars:
web_package: httpd
yum:
name: "{{ web_package }}"
state: present
- name: install {{ db_package }}
yum:
name: "{{ db_package }}"
state: present
always:
- name: start {{ db_service }}
service:
name: "{{ db_service }}"
state: started
enabled: yes
tasks:
- name: 检查时间
command: date
register: command_result
changed_when: false
- name: print local time
debug:
var: command_result.stdout
[student@workstation control-errors]$ ansible-playbook playbook.yml
[WARNING]: While constructing a mapping from /home/student/control-errors/playbook.yml, line 2, column 3, found a duplicate dict key (tasks). Using last defined value only.
PLAY [错误控制练习] ******************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com]
TASK [检查时间] ********************************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com]
TASK [print local time] ********************************************************************************************************************************************************************************************************
ok: [servera.lab.example.com] => {
"command_result.stdout": "Sat Jul 30 22:04:45 CST 2022"
}
PLAY RECAP *********************************************************************************************************************************************************************************************************************
servera.lab.example.com : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
# 结束实验
[student@workstation control-errors]$ lab control-errors finish
Finishing lab control-errors on servera.lab.example.com:
· Removing packages
· Removing mariadb-server................................... SUCCESS
· Removing httpd............................................ SUCCESS