文章目录
- 一、 循环语句
- 1.1 单量循环
- 1.2 多量循环
- 1.3 老版本用法
- 1.4 loop+register
- 二、条件判断
- 2.1 根据变量状态判断
- 2.2 根据变量是否存在判断
- 2.3 根据事实判断
- 2.4 多条件判断
- 2.4.1 and用法
- 2.4.2 or用法
- 2.5 循环判断
- 2.6 根据上个任务结果判断
- 三、handlers处理程序
- 四、任务失败处理方法
- 4.1 忽略失败任务
- 4.2 强制执行失败任务
- 4.3 自定义报错显示信息
- 4.4 自定义“changed”出现时机
- 4.5 Ansible块和错误处理
一、 循环语句
- 可以使用item循环变量+loop模块组合使用,实现普通循环。
- 常用于一键操作,比如一键安装所有组件服务。
1.1 单量循环
- 只对一个变量进行循环
1.一键停止受控机上的两个服务。
[root@localhost playbook]# cat qingjun.yml
---
- name: 一键操作
hosts: 192.168.130.161
tasks:
- name: 停止服务
service:
name: "{{ item }}" ##引用循环变量。
state: stopped
loop: ##使用loop模块,要操作的对象列表。
- postfix
- crond
2.推荐写法,定义变量,一键启动受控机上的两个服务。
[root@localhost playbook]# cat qingjun.yml
---
- name: 一键操作
vars:
service_name: ##定义变量,填写操作对象列表。
- crond
- postfix
hosts: 192.168.130.161
tasks:
- name: 停止服务
service:
name: "{{ item }}"
state: started
loop: "{{ service_name }}" ##模块直接引用变量。
3.使用变量文件定义,停止受控机上的两个服务。
1.2 多量循环
- 对多个变量进行循环。
1.在受控机上创建两个用户,baimu1和baimu2,属组分别是root、qingjun。
[root@localhost playbook]# cat qingjun.yml
---
- name: 一键操作
hosts: 192.168.130.161
tasks:
- name: 停止服务
user:
name: "{{ item.name }}" ##循环变量。
group: "{{ item.group }}"
state: present
loop:
- name: baimu1
group: root
- name: baimu2
group: qingjun
1.3 老版本用法
- 之前版本的循环是通过以下字段来实现的。
循环关键字 | 描述 |
---|---|
with_items | 行为与简单列表的loop关键字相同,例如字符串列表或散列/字典列表。但与loop不同的是,如果为with_items提供了列表的列表,它们将被扁平化为单级列表。循环变量item保存每次迭代过程中使用的列表项。 |
with_file | 此关键字需要控制节点文件名列表。循环变量item在每次迭代过程中保存文件列表中相应文件的内容。 |
with_sequence | 此关键字不需要列表,而是需要参数来根据数字序列生成值列表。循环变量item在每次迭代过程中保存生成的序列中的一个生成项的值。 |
1.列出受控机的/opt目录下文件内容。
2.主控机编写剧本,验证查看。
[root@localhost playbook]# cat qingjun.yml
---
- name: 一键操作
hosts: 192.168.130.161
vars:
qingjun: ##定义变量qingjun。
- /opt/baimu.txt
- /opt/baimu1.txt
- /opt/baimu2.txt
tasks:
- name: 获取文件内容
shell: |
cat "{{ item }}" ##循环变量。
with_items: "{{ qingjun }}" ##使用with_items循环模块,应用变量qingjun,作用与loop模块相似。
register: jis ##将获取的结果注册到变量jis里去。
- debug: var=jis ##将jis变量内容打印出来。
1.4 loop+register
- 配合打印循环。
1.打印出“zhangsan喜欢吃牛排”、“lisi喜欢吃牛排”的的信息。
[root@localhost playbook]# cat qingjun.yml
---
- name: 一键操作
hosts: 192.168.130.161
tasks:
- name: 获取文件内容
shell: |
echo "{{ item }}" 特别喜欢吃牛排! ##循环变量,依次跟局loop循环模块列表来执行。
loop:
- zhangsan
- lisi
register: qingjun ##将获取的结果注册到循环变量里去。
- name: Print
debug:
var: qingjun ##打印输出。
2.从打印的结果再取值。
[root@localhost playbook]# cat qingjun.yml
---
- name: 一键操作
hosts: 192.168.130.161
tasks:
- name: 获取文件内容
shell: |
echo "{{ item }}" 特别喜欢吃牛排!
loop:
- zhangsan
- lisi
register: qingjun
- name: Print
debug:
msg: |
"结果是:{{ item.start }}" ##取results里面的start的值,再进行打印。
loop: "{{ qingjun['results'] }}" ##从打印的整体结果里卖弄再取results部分里面的值。
##loop: "{{ qingjun.results }}" ##第二种写法。
二、条件判断
- 使用when语句来进行条件判断,若条件满足,则运行任务;若条件不满足,则跳过任务。
- when语句放在任务名称和模块(及模块参数)的后面。
标识符 | 示例 |
---|---|
等于(值为字符串) | ansible_machine == “x86_64” |
等于(值为数字) | max_memory == 512 |
小于 | min_memory < 128 |
大于 | min_memory > 256 |
小于等于 | min_memory <= 256 |
大于等于 | min_memory >= 512 |
不等于 | min_memory != 512 |
变量存在 | min_memory is defined |
变量不存在 | min_memory is not defined |
布尔变量是True。1、True或yes的求值为True | memory_available |
布尔变量是False。0、False或no的求值为False | not memory_available |
第一个变量的值存在,作为第二个变量的列表中的值 | ansible_distribution in supported_distros |
2.1 根据变量状态判断
1.根据某个变量的取值状态判断是否执行某个任务。这里根据变量service_status是否等于true来判断是否要安装httpd服务。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
vars:
service_status: true ##定义一个变量。
tasks:
- name: 判断apache是否安装
yum:
name: httpd
state: present
when: service_status ##判断这个变量,条件语句里不能存在{{ }}引用符号。
2.2 根据变量是否存在判断
1.根据上下文是否定义某个变量来判断是否执行某个任务。这里根据是否定义了变量service_name来判断是否要安装httpd服务。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
tasks:
- name: 判断apache是否安装
yum:
name: "{{ service_name }}" ##虽然引用变量,但此变量未被定义。
state: absent
when: service_name is defined
2.变量service_name为空,则不执行任务。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
vars:
service_name: " " ##定义变量,但值为空。
tasks:
- name: 判断apache是否安装
yum:
name: "{{ service_name }}" ##引用变量。
state: absent
when: service_name is defined ##判断变量是否为空,为空则不执行任务。
3.变量存在,执行任务,卸载httpd。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
vars:
service_name:
- httpd
tasks:
- name: 判断apache是否安装
yum:
name: "{{ service_name }}"
state: absent
when: service_name is defined
2.3 根据事实判断
- 根据取出的事实来判断是否在对受控机执行任务。
1.先取受控机的事实相关参数,比如这里我就根据系统版本来判断。
[root@localhost ansible]# ansible all -m setup -i inventory.ini |less
2.当系统版本等于7.5时,才执行条件循环语句,依次停止变量列表中的服务。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
vars:
service_name:
- firewalld
tasks:
- name: 判断apache是否安装
service:
name: "{{ item }}"
state: started
loop: "{{ service_name }}"
when: ansible_facts['distribution_version'] == "7.5"
2.4 多条件判断
- 使用and表示所有条件满足时,才执行任务。
- or表示满足任意一个条件时,就可执行任务。
2.4.1 and用法
1.当系统版本为7.5,且python版本大于2.7时才会执行任务。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
vars:
service_name:
- firewalld
tasks:
- name: 判断apache是否安装
service:
name: "{{ item }}"
state: stopped
loop: "{{ service_name }}"
when: ansible_facts['distribution_version'] == "7.5" and ansible_facts['python_version'] > 2.7
2.也可以多行写法。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
vars:
service_name:
- firewalld
tasks:
- name: 判断apache是否安装
service:
name: "{{ item }}"
state: started
loop: "{{ service_name }}"
when:
- ansible_facts['distribution_version'] == "7.5"
- ansible_facts['python_version'] > 2.7
2.4.2 or用法
1.当系统版本为7.5,或python版本大于3时才会执行任务。我这里只满足了第一个条件,所以可以执行任务。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
vars:
service_name:
- firewalld
tasks:
- name: 判断apache是否安装
service:
name: "{{ item }}"
state: started
loop: "{{ service_name }}"
when: ansible_facts['distribution_version'] == "7.5" or ansible_facts['python_version'] > 3
2.也可以多行写法。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
vars:
service_name:
- firewalld
tasks:
- name: 判断apache是否安装
service:
name: "{{ item }}"
state: stopped
loop: "{{ service_name }}"
when:
ansible_facts['distribution_version'] == "7.5"
or
ansible_facts['python_version'] > 3
2.5 循环判断
- 结合loop模块实现循环判断。
1.循环事实里的条件,条件满足执行任务。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
tasks:
- name: 判断apache是否安装
yum:
name: httpd
state: present
loop: "{{ ansible_facts['mounts'] }}" ##取事实里的mounts内的内容,其下面有多个挂载点,根据when条件来判断是否执行任务。
when:
- item.mount == "/" ##ansible_facts['mounts']['mount'] 的值是否为“/”。
- item.size_available > 700 ##ansible_facts['mounts'][’size_avaliable‘]的值是否大于700.
2.6 根据上个任务结果判断
- 根据上个任务结果来判断是否要执行后面的任务。
- 比如,第一个任务是安装服务,若是安装成功则执行第二步启动服务;若是安装失败,就不需要继续执行任务了。
1.rc是任务执行后输出的一个值,当rc=0时,代表任务执行成功。
2.当第一步安装httpd服务成功时,会输出rc=0,之后才会执行第二步。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
tasks:
- name: 判断服务是否安装
yum:
name: httpd
state: present
ignore_errors: yes ##任务执行失败直接跳过,可以继续执行后面任务。
register: qingjun ##获取任务输出的结果,讲结果注册到变量qingjun里去。
- name: 启动服务
service:
name: httpd
state: started
when:
- qingjun.rc == 0 ##变量qingjun里的内容有个rc值,rc=0时才会执行第二步。
三、handlers处理程序
基本了解:
- handlers和tasks功能一样,也是用于处理任务的,只是它常用于更改服务配置文件后需要重启服务这种场景常用。
- handlers和tasks是同级别,需要在tasks任务里定义notify语句,当notify语句所在的任务结果显示为changed时,才会触发handlers任务。
注意事项:
- 当tasks中有多个任务定义notify语句时,触发任务会按照handlers任务里的顺序来执行。
- 若tasks中定义多个notify语句具备相同的名称时,最终只会触发一个handlers任务。
- 即使有多个任务通知处理程序,该处理程序依然仅运行一次。如果没有任务通知处理程序,它就不会运行。
1.主控机的playbook目录下有个专门存放受控机配置文件的目录,此时配置文件http.conf是原文件,监听端口未被修改。
2.编写playbook,当notify所在的任务发生了改变就会触发handlers任务,从而重启服务。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
tasks:
- name: 判断服务是否安装
yum:
name: httpd
state: present
ignore_errors: yes
- name: 更改服务配置文件
template:
src: ./file/httpd.conf ##将主控机上的文件传到受控机上,若文件内容发生改变则触发任务。
dest: /etc/httpd/conf/httpd.conf
notify:
- restart httpd ##需要与handlers名称一致。
- name: 启动服务
service:
name: httpd
state: started
handlers:
- name: restart httpd ##需与notify名称一致。
service:
name: httpd
state: restarted
3.执行playbook,此时主控机上的http.conf文件内容未被修改,传到受控机上也不会出现覆盖动作,所以notify是直接返回的ok,此时不触发handlers任务。
4.访问受控机安装的httpd服务,默认80端口。
5.此时修改主控机上的http.conf文件内容,将监听端口改成88,执行playbook会触发handlers任务,因为两边文件内容不一样,传过去会覆盖受控机上的原文件。
6.再次访问受控机安装的httpd服务,需要88端口。
四、任务失败处理方法
4.1 忽略失败任务
- 添加参数ignore_errors: yes,可以让playbook在任务失败时继续执行。
1.安装一个不存在的任务会失败,添加ignore_errors会忽略继续执行后面任务。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
tasks:
- name: 判断服务是否安装
yum:
name: httpds
state: present
ignore_errors: yes
- name: 更改服务配置文件
template:
src: ./file/httpd.conf
dest: /etc/httpd/conf/httpd.conf
notify:
- restart httpd
- name: 启动服务
service:
name: httpd
state: started
handlers:
- name: restart httpd
service:
name: httpd
state: restarted
4.2 强制执行失败任务
- 添加参数force_handlers: yes,针对性的适用handlers处理程序的场景。
- 正常情况下,当某一个任务执行失败时就会停止其他程序处理;当使用此参数时,即时前面有任务执行失败,后面的handlers也会在notify函数所在的任务发生改变时正常执行。
1.中间的启动服务步骤会失败,但不影响后面的handlers任务正常执行。当修改配置文件监听端口为90,notify语句会触发handlers执行程序任务,从而重启服务,可以使用90端口访问web页。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
force_handlers: yes
tasks:
- name: 判断服务是否安装
yum:
name: httpd
state: present
- name: 更改服务配置文件
template:
src: ./file/httpd.conf
dest: /etc/httpd/conf/httpd.conf
notify:
- restart httpd
- name: 启动服务
service:
name: httpds
state: started
handlers:
- name: restart httpd
service:
name: httpd
state: restarted
2.使用受控机ip:90访问web。
4.3 自定义报错显示信息
- 使用failed_when指定任务失败条件,再使用fail模块指定要输出的错误信息。
- 常用于输出的错误信息太多,而我们只需要简短的错误提示即可。或者是运行的脚本里有多个任务,但其中有个任务执行失败但最终输出的信息还是changed,并非一片飘红。
1.当playbook里需要受控机执行主控机上的脚本时,使用script模块来定义。若脚本中有某个任务执行失败,且最后一个任务执行成功,则playbook最后输出的是changed,没有错误信息。
##受控机要执行的脚本。
[root@localhost playbook]# cat file/qingjun.sh
#!/bin/bash
echo "qingjun" > 111.txt
ls wqlasdas ##任务执行失败。
ls 111.txt ##任务执行成功
##playbook。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
gather_facts: no ##关闭收集事实。
tasks:
- name: 判断服务是否安装
script: file/qingjun.sh
register: qingjun
- debug:
var: qingjun
2.此时我们可以自定义输出错误信息,加以提示。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
gather_facts: no
tasks:
- name: 判断服务是否安装
script: file/qingjun.sh
register: qingjun
ignore_errors: yes
- name: 自定义错误信息
fail:
msg: "有错误,请立即检查脚本!!!"
failed_when: "'No such file or directory' in qingjun.stdout"
4.4 自定义“changed”出现时机
- 特殊场景时需要playbook输出信息一直是changed,或是ok,可以使用。
1.如下使用template模块时,第一次传到受控机会发生改变则输出changed,之后文件没有发生改变则一直输出ok。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
gather_facts: no
tasks:
- name: 判断服务是否安装
template:
src: file/httpd.conf
dest: /opt/
2.此时我想让后面的输出信息一直是changed,则可以添加 changed_when,为True时则输出changed。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
gather_facts: no
tasks:
- name: 判断服务是否安装
template:
src: file/httpd.conf
dest: /opt/
changed_when: True
3.改为False时,一直输出ok。
4.5 Ansible块和错误处理
基本了解:
- block:定义要运行的主要任务,block块中的任务是绑定在一起的,要么一起执行,要么一起不执行。
- rescue:定义要在block子句中定义的任务失败时运行的任务。
- always:定义始终都独立运行的任务,不论block和rescue子句中定义的任务是成功还是失败。
用法:
- 若block块中存在任意一个失败任务,随后会执行rescue块中的任务。
- block执行失败,则rescue执行;block成功执行,则rescue不会执行;always始终执行。
1.测试一,block块中存在失败任务,则rescue块任务执行。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
gather_facts: no
tasks:
- name: 判断服务是否安装
block:
- name: block中的任务
command: echo "block"
- name: 失败任务
command: ls sajkdha ##该任务会执行失败,出触发rescue块任务。
rescue:
- name: rescue中的任务
command: echo "rescue"
always:
- name: always中的任务
command: echo "always"
2.测试二,block块所有任务全部执行成功,则rescue块任务不执行。而always任务从头到尾都是一直执行的,不受其他块任务影响。
[root@localhost playbook]# cat qingjun.yml
---
- name: 安装服务
hosts: 192.168.130.161
gather_facts: no
tasks:
- name: 判断服务是否安装
block: ##所有任务执行成功,不会触发rescue任务的执行。
- name: block中的任务
command: echo "block"
- name: 失败任务
command: echo " 123 " > qqq.txt
rescue:
- name: rescue中的任务
command: echo "rescue"
always:
- name: always中的任务
command: echo "always"