Ansible流程控制
Ansible流程控制
playbook条件语句
不管是shell还是各大编程语言中,流程控制,条件判断这些都是必不可少的,在我们使用Ansible的过程中,条件判断的使用频率极其高。
例如:
1.我们使用不同的系统的时候,可以通过判断系统来对软件包进行安装。
2.在nfs和rsync安装过程中,客户端服务器不需要推送配置文件,之前我们都是写多个play,会影响效率。
3.我们在源码安装nginx的时候,执行第二遍就无法执行了,此时我们就可以进行判断是否安装过。
官方语法
when: ansible_facts['hostname'] == 'web01'
判断多条件列表
[root@m01 ~/rsync]$ vim rsync.yml
# 因为有了判断,所以这里可以直接选择所有主机
- hosts: all
tasks:
- name: Install rsync server
yum:
name: rsync
state: present
# when语句可以判断安装在哪写机器上,这里代表backup和nfs(用列表就是和的意思)
when:
- ansible_hostname == "backup"
- ansible_hostname == "nfs"
判断分组
# 判断系统是sentos6或是debian7的(and:和 or:或)
when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6") or
(ansible_distribution == "Debian" and ansible_distribution_major_version == "7")
判断条件运算
# 取到major的值后,通过管道交给int转化为整形进行判断,这里是大于等于1
ansible_python['version']['major']|int >=1
# 查看主机所有内置变量
[root@m01 ~]$ ansible web01 -m setup
"ansible_python": {
"executable": "/usr/bin/python",
"has_sslcontext": true,
"type": "CPython",
"version": {
"major": 2,
"micro": 5,
"minor": 7,
"releaselevel": "final",
"serial": 0
}
#######################################################################################################################
# 官方示例
tasks:
- shell: echo "only on Red Hat 6, derivatives, and later"
when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb'] ['major_release']|int >= 6
模糊匹配
- hosts: all
tasks:
- name: Install Nginx
yum:
name: nginx
state: present
# 给以web开头的主机名安装nginx
when: ansible_hostname is match 'web*'
条件语句判断实战:rsync
- hosts: all
tasks:
- name: Install Rsync Server
yum:
name: rsync
# 客户端服务端都需要安装
when: ansible_hostname == 'nfs' or ansible_hostname == 'backup'
- name: rsyncd conf
copy:
src: /root/rsync/rsyncd.conf
dest: /etc/rsyncd.conf
# 推送配置文件只有backup需要
when: ansible_hostname == "backup"
- name: Create Rsync Passwd File
copy:
content: rsync_backup:123
dest: /etc/rsync.passwd
mode: 0600
# 服务端密码文件只有backup需要
when: ansible_hostname == 'backup'
- name: Create nfs Passwd File
copy:
content: '123'
dest: /etc/rsync.passwd
mode: 0600
# 客户端密码文件配给nfs
when: ansible_hostname == 'nfs'
- name: Start rsync server
service:
name: rsyncd
state: started
# 启动rsync服务也只有backup需要
when: ansible_hostname == "backup"
playbook循环语句
循环语法
[root@m01 ~/rsync]$ vim test.yml
- hosts: web_group
tasks:
- name: Install all
yum:
# 调用循环
name: "{{ item }}"
state: present
# 定义循环,先安装rsync,再安装nginx
with_items:
- rsync
- nginx
- name: start all
service:
name: "{{ item }}"
state: started
# 定义循环,先启动rsync,再启动nginx
with_items:
- rsyncd
- nginx
字典循环
创建用户
- hosts: all
tasks:
- name: Create Group
group:
# 创建组
name: "{{ item }}"
# 利用字典循环定义多个组名
with_items:
- linux
- av
- name: Create User
user:
# 创建多个用户
name: "{{ item.name }}"
# 给多个用户指定不同的组
group: "{{ item.group }}"
# 利用字典循环定义多个用户名及组名
with_items:
- {name: "zls",group: "linux"}
- {name: "cls",group: "av"}
推送文件
- name: copy conf and code
copy:
# 调用字典循环,这里相当于推送nginx.conf配置文件,再推送blog.conf站点文件
src: "{{ item.src }}"
# 循环接收文件
dest: "{{ item.dest }}"
# 循环设置文件权限
mode: "{{ item.mode }}"
# 字典循环,类似于层级变量,在with_items下面定义,这样一循环可以推送多个文件,可以加多个循环变量
with_items:
- { src: "/root/ansible/nginx.conf", dest: "/etc/nginx/nginx.conf", mode: "0644" }
- { src: "/root/ansible/blog.wj.com.conf", dest: "/etc/nginx/conf.d/blog.wj.com.conf", mode: "0644"}
#####################################################################################################################################
TASK [copy conf and code] *************************************************************************************************
changed: [web02] => (item={u'dest': u'/etc/nginx/nginx.conf', u'src': u'/root/ansible/nginx.conf', u'mode': u'0644'})
changed: [web01] => (item={u'dest': u'/etc/nginx/nginx.conf', u'src': u'/root/ansible/nginx.conf', u'mode': u'0644'})
ok: [web02] => (item={u'dest': u'/etc/nginx/conf.d/blog.wj.com.conf', u'src': u'/root/ansible/blog.wj.com.conf', u'mode': u'0644'})
ok: [web01] => (item={u'dest': u'/etc/nginx/conf.d/blog.wj.com.conf', u'src': u'/root/ansible/blog.wj.com.conf', u'mode': u'0644'})
playbook handlers
触发器
handler用来执行某些条件下的任务,比如当配置文件发生变化的时候,通过notify触发handler去重启服务。
在saltstack中也有类似的触发器,写法相对Ansible简单,只需要watch,配置文件即可
实践案例
- hosts: web_group
tasks:
- name: copy conf and code
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: "{{ item.mode }}"
with_items:
- { src: "/root/ansible/nginx.conf", dest: "/etc/nginx/nginx.conf", mode: "0644" }
- { src: "/root/ansible/blog.wj.com.conf", dest: "/etc/nginx/conf.d/blog.wj.com.conf", mode: "0644"}
# 还是用上面的playbook,在推送配置文件时,添加触发器,并定义一个名称“Restart nginx”,一旦传输的内容发生变化,就会触发下面的动作
notify:
- Restart nginx
# handlers接收触发器反馈的信息并作出动作,注意:必须和tasks在同一层级
handlers:
# 指定接收的触发器
- name: Restart nginx
# 做出的动作:重启服务
service:
name: nginx
state: restarted
注意:
1.无论多少个task通知了相同的handlers,handlers仅会在所有tasks结束后运行一次。
2.Handlers只有在其所在的任务被执行时,才会被运行;如果一个任务中定义了notify调用Handlers,但是由于条件判断等原因,该任务未被执行,那么Handlers同样不会被执行。
3.Handlers只会在每一个play的末尾运行一次;如果想在一个playbook中间运行Handlers,则需要使用meta模块来实现。例如: -meta: flush_handlers。
4.如果一个play在运行到调用Handlers的语句之前失败了,那么这个Handlers将不会被执行。我们可以使用meta模块的--force-handlers选项来强制执行Handlers,即使Handlers所在的play中途运行失败也能执行。
5.不能使用handlers替代tasks
playbook tags
默认情况下,Ansible在执行一个playbook时,会执行playbook中定义的所有任务,Ansible的标签(tag)功能可以给单独任务甚至整个playbook打上标签,然后利用这些标签来指定要运行playbook中的个别任务,或不执行指定的任务。
打标签的方式
1.对一个task打一个标签
2.对一个task打多个标签
3.对多个task打一个标签
打标签语法
- hosts: web_group
tasks:
- name: Install all
yum:
name: "{{ item }}"
state: present
with_items:
- rsync
- nginx
- name: start all
service:
name: "{{ item }}"
state: restarted
with_items:
- rsyncd
- nginx
# 给服务启动模块打一个标签,并指定名称
tags:
- manager_nginx_server
执行方式
- -t:执行指定的标签
- --skip-tags:跳过指定标签
# 只执行指定的标签模块
ansible-playbook rsync.yml -t manager_nginx_server
# 跳过指定标签,执行其他模块
ansible-playbook rsync.yml --skip-tags manager_nginx_server
playbook include - 剧本复用
在之前写playbook的过程中,我们发现,写多个playbook没有办法,一键执行,这样我们还要单个playbook挨个去执行,很鸡肋。所以在playbook中有一个功能,叫做include用来动态调用task任务列表。
只调用task:include_tasks
调用整个task文件:include (新版本:import_playbook)
在saltstack中,叫做top file入口文件。
剧本复用实践
# 1.创建一个项目专属目录
[root@m01 ~]$ mkdir /ansible_project
# 2.将不同服务或主机单独分为一个目录
[root@m01 /ansible_project]$ mkdir group_vars # 主机组变量文件
[root@m01 /ansible_project]$ mkdir host_vars # 单个主机变量文件
[root@m01 /ansible_project]$ mkdir mariadb # 数据库相关配置文件和数据库playbook存放目录
[root@m01 /ansible_project]$ mkdir nfs # nfs配置文件和nfs的playbook存放目录
[root@m01 /ansible_project]$ mkdir nginx # nginx相关配置文件和nginx的playbook存放目录
[root@m01 /ansible_project]$ mkdir php # php相关配置文件和php的playbook存放目录
[root@m01 /ansible_project]$ mkdir rsync # rsync相关配置文件和backup的playbook存放目录
[root@m01 /ansible_project]$ mkdir sersync # sersync相关配置文件存放目录
[root@m01 /ansible_project]$ touch lnmp.yml # 主剧本playbook文件,必须和其他项目目录同级
[root@m01 /ansible_project]$ tree .
├── group_vars
├── host_vars
├── lnmp.yml
├── mariadb
├── nfs
├── nginx
├── php
├── rsync
└── sersync
8 directories, 1 file
####################################################################################################################################
# 在rsync目录下配置各个playbook,直接从- name开始编写即可
## 安装服务的playbook
[root@m01 ansible_project]$ vim rsync/install_rsync.yml
- name: Install rsync
yum:
name:
- rsync
- nfs
state: absent
when: ansible_hostname == 'nfs' or ansible_hostname == 'backup'
## 推送rsync配置文件的playbook
[root@m01 ansible_project]$ vim rsync/config_rsync.yml
- name: Configure Rsync Server
copy:
src: ./rsyncd.conf
dest: /etc/rsyncd.conf
notify: Restart Rsync -------------------notify触发器必须在子playbook中,handlers在主playbook中
when: ansible_hostname == 'backup'
# 启动服务的playbook
[root@m01 ansible_project]$ vim rsync/start_rsync.yml
- name: Start Rsync
service:
name: rsyncd
state: started
enabled: yes
when: ansible_hostname == 'backup'
######################################################################################################################################
# 编辑主playbook
## 主playbook中需要- hosts
[root@m01 ansible_project]# cat task.yml
- hosts: all
# 在tasks下面直接include各个文件
tasks:
- include_tasks: rsync/install_rsync.yml ------服务安装playbook
- include_tasks: rsync/config_rsync.yml ------服务配置文件playbook
- include_tasks: rsync/start_rsync.yml ------启动服务的playbook
handlers:
- name: Restart Rsync
service:
name: rsyncd
playbook忽略错误
默认playbook会检测task执行的返回状态,如果遇到错误则会立即终止playbook的后续task执行,然鹅有些时候playbook即使执行错误了也要让其继续执行
加入参数:ignore_errors:yes 忽略错误
- hosts: web_group
# 不调用系统内置变量,节省时间
gather_facts: no
tasks:
- name: panduan php
# 判断php是否安装
shell: 'rpm -qa|grep php'
# 定义变量名panduan_php
register: panduan_php
# 忽略错误提示,不会有错误返回结果,但是如果第一次安装就没成功也不会有错误结果,所以需要下面的判断,是否安装成功
ignore_errors: yes
- name: Install php
shell: 'rpm -Uvh /tmp/*.rpm'
# 判断变量panduan_php.rc返回值为0时(没有安装时),就执行php安装程序
when: panduan_php.rc != 0
抑制changed
被管理主机没有发生变化,可以使用参数将change状态改为ok
就是将返回值为黄色的结果转为绿色的结果......
vim nginx.yml
- hosts: web_group
gather_facts: no
tasks:
# 检测nginx配置文件
- name: check nginx
shell: '/sbin/nginx -t'
# 定义变量check_nginx
register: check_nginx
changed_when:
# 取变量check_nginx下面的stderr_lines.0.find值,返回值为ok时,表示nginx配置文件没啥问题,就显示绿色
- check_nginx.stderr_lines.0.find('ok')
- false