自动化运维利器Ansible-Playbook的任务控制
文章目录
- 自动化运维利器Ansible-Playbook的任务控制
- 条件判断
- 示例1
- 示例2
- 练习
- 循环控制
- 模板
- 模板 new
- 循环加判断
- Tags 属性
- 测试文件是否存在
- Handlers 属性
- 练习
条件判断
示例1
[root@localhost ~]# cat hosts
[webservers]
192.168.116.145 ansible_ssh_port=2222
[root@localhost ~]# cat when.yml
---
- name: 条件判断的小示例
hosts: webservers
gather_facts: no
tasks:
- name: exec shell
shell: echo $UID
register: ret
# - name: show ret
# debug:
# var: ret
- name: 利用执行结果进行判断
when: ret.stdout == "0"
shell: touch /root/Neko.txt
...
[root@localhost ~]# ansible-playbook -i hosts when.yml
[root@localhost ~]# ls
a.sh hosts Neko.txt
示例2
[root@localhost ~]# cat hosts
[dbservers]
192.168.116.131
192.168.116.145 ansible_ssh_port=2222 ansible_ssh_user=liu
[root@localhost ~]# cat when.yml
---
- name: 条件判断的小示例
hosts: dbservers
gather_facts: no
tasks:
- name: exec shell
shell: echo $UID
register: ret
# - name: show ret
# debug:
# var: ret
- name: 利用执行结果进行判断
when: ret.stdout == "0"
shell: touch /root/Neko.txt
...
[root@localhost ~]# ansible-playbook -i hosts when.yml
练习
1 为什么要判断
- 根据业务需求判断
2 判断什么
- 命令执行是否成功,也就是 $? 的值
- 命令执行后的返回结果
需要知道命令返回结果的数据结构
debug - 使用 when 属性进行判断
[root@localhost ~]# cat when2.yml
---
- name: 判断是否存在用户
hosts: dbservers
gather_facts: no
tasks:
- name: 执行shell命令,获取用户信息
shell: id tom && echo "ok" || echo "no"
register: tom1
- name: touch a file
when: tom1.stdout_lines[-1] == "ok"
file:
path: /home/tom/love
state: touch
- name: 没有则创建这个用户
when: tom1.stdout_lines[-1] == "no"
user: name=tom
...
循环控制
循环 https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html
[root@localhost ~]# cat with.yml
---
- name: 循环打印
hosts: dbservers
gather_facts: no
vars:
nums:
- 1
- shark
- hello
tasks:
- name: 输出被循环的元素
with_items: "{{ nums }}"
debug:
var: item
...
模板
[root@localhost ~]# cat info.yml
---
info:
a: 1
b: 2
c: 3
infos:
- user: shark
age: 18
- user: tom
age: 19
#{"info": {"a":1, "b":2, "c":3}}
#{infos: [{"user": "shark", "age":18},{"user": "tom", "age": 19}}
...
[root@localhost ~]# cat infos.yml
---
- name: 循环打印
hosts: dbservers
gather_facts: no
vars:
nums:
- 1
- shark
- hello
vars_files:
- info.yml
tasks:
- name: 输出被循环的元素
with_items: "{{ nums }}"
debug:
var: item
- name: 循环字典
with_items: "{{ infos }}"
debug:
msg: "姓名是:{{item.user}},年龄是: {{item.age}}"
...
模板 new
#####
[root@localhost ~]# cat infox.yml
---
- name: 循环打印
hosts: dbservers
gather_facts: no
vars:
nums: [1, "1", shark, "hello"]
info: {a: 1, b: 2, c: 3}
infos: [{"user": shark, age: 18},{user: xiguatian, age: 19}]
tasks:
- name: 循环列表
with_items: "{{ nums }}"
debug:
var: item
- name: 循环字典
with_items: "{{ info | dict2items }}"
debug:
msg: "姓名是:{{item.key}}, 年龄是: {{item.value}}"
- name: 循环列表和字典
with_items: "{{ infos }}"
debug:
msg: "姓名是:{{item.user}}, 年龄是: {{item.age}}"
...
循环加判断
[root@localhost ~]# cat loop.yml
---
- name: 循环中使用判断
hosts: dbservers
gather_facts: no
vars:
nums:
- 1
- 2
- 3
tasks:
- name: 循环元素,只打印大于1的元素
loop: "{{ nums }}"
when: item > 1
debug:
var: item
...
这里我们给出一个实际场景应用案例去说明在PlayBook中,任务控制如何应用。
需求如下:
1 在下面的PlayBook中,我们创建了 tomcat、www 和 mysql 三个用户。
安装了Nginx 软件包、并同时更新了 Nginx 主配置文件和虚拟主机配置文件,最后让Nginx 服务处于启动状态。
playbook 如下:
[root@localhost ~]# cat when.nginx.yml
---
- name: task control playbook example
hosts: webservers
gather_facts: no
vars:
users: [tomcat, www, mysql]
tasks:
- name: create tomcat user
loop: "{{ users }}"
user:
name: "{{ item }}"
state: present
- name: copy nginx repo file
copy: src=nginx.repo dest=/etc/yum.repos.d/nginx.repo
- name: yum nginx webserver
yum: name=nginx state=present
- name: update nginx main config
copy: src=nginx.conf dest=/etc/nginx/
- name: add virtualhost config
copy: src=www.qfedu.com.conf dest=/etc/nginx/conf.d/
- name: check nginx syntax
shell: /usr/sbin/nginx -t
register: nginxsyntax
- name: print nginx syntax
debug: var=nginxsyntax
- name: start nginx server
when: nginxsyntax.rc == 0
service: name=nginx state=started
用到的配置文件
# cat nginx.conf
user www;
worker_processes 2;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile on;
tcp_nopush on;
keepalive_timeout 0;
gzip on;
gzip_min_length 1k;
gzip_buffers 8 64k;
gzip_http_version 1.0;
gzip_comp_level 5;
gzip_types text/plain application/x-javascript text/css application/json application/xml application/x-shockwave-flash application/javascript image/svg+xml image/x-icon;
gzip_vary on;
include /etc/nginx/conf.d/*.conf;
}
Nginx 子配置文件
# cat www.neko.com.conf
server {
listen 80;
server_name www.neko.com;
root /usr/share/nginx/html;
access_log /var/log/nginx/www.neko.com-access_log main;
error_log /var/log/nginx/www.neko.com-error_log;
add_header Access-Control-Allow-Origin *;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 1d;
}
location ~ .*\.(js|css)?$ {
expires 1d;
}
}
我们下面将以解决一个个问题的形式去优化上例中的PlayBook。
通过问题的解决,来达到我们学习任务控制的目的。
Tags 属性
[root@localhost ~]# cat tags.yml
---
- name: 使用 tags 执行指定的任务
hosts: webservers
gather_facts: no
vars:
a: 1
b: 2
c: 3
tasks:
- name: 输入 a
debug: var=a
tags: Neko
- name: 输入 b
debug: var=b
tags: Neko
- name: 输入 c
debug: var=c
tags: ohhh
- name: 输入 a and b
debug: msg="{{a}}{{b}}"
...
[root@localhost ~]# ansible-playbook -i hosts tags.yml
[root@localhost ~]# ansible-playbook -i hosts tags.yml -t Neko
[root@localhost ~]# ansible-playbook -i hosts tags.yml -t Neko,ohhh
测试文件是否存在
[root@localhost ~]# cat file.yml
---
- name: 测试文件是否存在
hosts: webservers
gather_facts: no
tasks:
- name: 测试
file:
path: /var/run/nginx.pid
state: file
register: ret
- name: debug
debug: var=ret
...
[root@localhost ~]# ansible-playbook -i hosts file.yml
Handlers 属性
观察当前的 Playbook,细心的你会发现,当我的配置文件没有发生变化时,每次依然都会去触发TASK “reload nginx server”。
如何能做到只有配置文件发生变化的时候才去触发TASK “reload nginx server”,这样的处理才是最完美的实现。此时可以使用 handlers 属性。
- name: test handlers
hosts: all
gather_facts: no
tasks:
- name: copy a file to remote server
copy:
src: a.txt
dest: /tmp/a.txt
notify:
- test ping
- ping a remote server
handlers:
- name: ping a remote server
ping:
- name: test ping
ping:
...
改进PlayBook
---
- name: task control playbook example
hosts: webservers
gather_facts: no
vars:
createuser:
- tomcat
- www
- mysql
tasks:
- name: create tomcat user
with_items: "{{ createuser }}"
user:
name: "{{ item }}"
state: present
- name: yum nginx webserver
yum: name=nginx state=present
- name: update nginx main config
copy: src=nginx.conf dest=/etc/nginx/nginx.conf
tags: updateconfig
notify: reload config file
- name: add virtualhost config
copy:
src: www.qfedu.com.conf
dest: /etc/nginx/conf.d/www.qfedu.com.conf
tags: updateconfig
notify: reload config file
- name: 检查nginx 配置文件的语法
shell: /usr/sbin/nginx -t
register: nginxsyntax
tags: updateconfig
- name: test nginx is running
shell: "ps -ef|grep 'nginx: maste[r]'|awk '{print $2}'"
register: nginxrunning
tags: updateconfig
- name: start nginx server
when:
- nginxsyntax.rc == 0
- nginxrunning.stdout == ""
service: name=nginx state=started
tags: updateconfig
handlers:
- name: reload config file
when: nginxsyntax.rc == 0 and nginxrunning.stdout
service:
name: nginx
state: reloaded
...
在改进的PlayBook中,我们针对文件发布TASK 任务 “update nginx main config” 和 “add virtualhost config” 增加了新属性 notify, 值为 “reload nginx server”。
它的意思是说,针对这两个文件发布的TASK,设置一个通知机制,当Ansible 认为文件的内容发生了变化(文件MD5发生变化了),它就会发送一个通知信号,通知 handlers 中的某一个任务。
具体发送到handlers中的哪个任务,由notify 的值"reload nginx server"决定。
通知发出后handlers 会根据发送的通知,在handlers中相关的任务中寻找名称为"reload nginx server" 的任务。
当发现存在这样名字的TASK,就会执行它。若没有找到,则什么也不做。若我们要实现这样的机制,千万要注意notify属性设置的值,一定要确保能和handlers中的TASK 名称对应上。
修改任何一方的文件都会导致两端的文件不一致,都会触发任务的执行。
执行
首次执行,若配置文件没有发生变化,可以发现根本就没有触发handlers 中TASK任务
#ansible-playbook -i hosts site.yml -t updateconfig
人为对Nginx 配置文件稍作修改,只要MD5校验值发生变化即可。此时再执行,发现触发了handlers 中的TASK任务
#ansible-playbook -i hosts site.yml -t updateconfig
练习
当修改 ansible 主机上的 my.cnf 文件后 触发 dbservers 服务器中的 MySQL 服务重启
编写 yum 文件,部署 MySQL
[root@localhost ~]# cat mysql.yml
---
- name: 重启MySQL服务
hosts: dbservers
gather_facts: no
tasks:
- name: 下载yum仓库
yum:
name: https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
state: installed
- name: 打开5.7版本
ini_file:
path: /etc/yum.repos.d/mysql-community.repo
section: mysql57-community
option: enabled
value: 1
- name: 关闭8.0版本
ini_file:
path: /etc/yum.repos.d/mysql-community.repo
section: mysql80-community
option: enabled
value: 0
- name: yum安装MySQL
yum: name=mysql-community-server state=present
- name: 设置开机自启 设置服务自启
systemd: name=mysqld enabled=yes state=restarted
- name: copy模块修改文件,触发重启
copy:
src: /etc/my.cnf
dest: /root/my.cnf
notify: restart mysqld
handlers:
- name: restart mysqld
systemd: name=mysqld state=restarted
...