一、Ansible任务控制基本介绍
任务控制类似于编程语言中的if ... 、for ... 等逻辑控制语句。
这里我们给出一个实际场景应用案例说明playbook中,任务控制如何用。
在下面的playbook中,我们创建了Tomcat、www、和mysql三个用户
安装了nginx软件包、并同时更新了nginx主配置文件和虚拟主机配置文件,最后让nginx服务处于启动状态。
---
- name: test control playbook example
hosts: webservers
tasks:
- name: create tomcat user
user: name=tomcat state=present
- name: create www user
user: name=www state=present
- name: create mysql user
user: name=mysql state=present
- name: yum nginx webservers
yum: name=nginx stste=present
- name: update nginx main config
copy: src=nginx.conf dest=/etc/nginx/
- name: add virtualhost config
copy: src=www.xiaoma.com.conf dest=/etc/nginx/conf.d/
- name: nginx server start
service: name=nginx state=started
...
整个playbook从语法上没有任何问题,但从逻辑和写法上仍然有一些地方需要我们注意和优化。
-
Nginx启动逻辑欠缺考虑。若nginx的配置文件语法错误则会导致启动nginx失败,以至于playbook执行失败。
-
批量创建用户,通过指令的罗列过于死板,如果在创建若干个用户,将难以执行。
二、条件判断
通过解决上面的问题,来达到我们学习任务控制的目的
解决第一个问题
nginx启动逻辑欠缺考虑。
若nginx的配置文件语法错误则会导致nginx失败,以至于playbook执行失败。
如果我们能够在启动之前去对nginx的配置文件语法做正确的校验,当校验通过的时候我们才去启动或者重启nginx,否则跳过启动nginx的过程,这样就避免nginx配置语法问题导致的无法启动nginx的风险。
Nginx 语法校验
- name: check nginx syntax
shell: /usr/sbin/nginx -t
如何将nginx语法检查的TASK同nginx启动的TASK关联起来?
如果我们能够获得语法检查的TASK结果,根据这个结果去判断“启动nginx的TASK”是否执行,这将是一个很好的方案。
如何获取到语法检查TASK的结果呢?此时就可以使用之前学到的Ansible的注册变量。
获取Task任务结果
- name: check nginx syntax
shell: /usr/sbin/nginx -t
register: nginxsyntax
此时有可能还有疑问,我们获取到任务结果,但是结果里面的内容是什么样子,我们如何根据内容在后续的playbook中继续使用呢?
通过debug模块去确认返回结果的数据结构
- name: print nginx syntax result
debug: var=nginxsyntax
通过debug模块,打印出来的返回结果。当nginxsyntax.rc为0时语法校验正确。
通过条件判断(when)指令使用语法校验的结果
[root@web-02 ~]# cat siter.yml
---
- name: check nginx syntax
shell: /usr/sbin/nginx -t
register: nginxsyntax
- name: print nginx syntax
debug: var=nginxsyntax
- name: start nginx server
service: name=nginx state=started
when: nginxsyntax.rc == 0
改进后的playbook
[root@web-02 ~]# cat playbppk01.yml
---
- name: task control playbook example
hosts: webservers
tasks:
- name: create tomcat user
user: name=tomcat state=present
- name: create www user
user: name=www state=present
- name: create mysql user
user: name=mysql state=present
- name: yum nginx webservers
yum: name=nginx state=present
- name: update nginx main config
copy: src=nginx.conf dest=/etc/nginx/
- name: add virtualhost config
copy: src=www.xiaoma.com.conf dest=/etc/nginx/conf.d/
- name: check nginx synstx
shell: /usr/sbin/nginx t
register: nginxsyntax
- name: print nginx syntax
debug: var=nginxsyntax
- name: start nginx server
service: name=nginx state=started
when: nginxsyntax.rc == 0
以上的逻辑,只要语法检查通过都会去执行“start nginx server”这个TASK。
when支持的运算符
==
!=
> <=
is defined
is not defined
true
false
支持逻辑运算符:and or
三、循环控制
批量创建用户,通过指令的罗列过于死板,如果在创建若干个用户,将难以收场。
如果在创建用户时,抛开playbook的实现不说,单纯的使用shell去批量的创建一些用户,通常怎么写呢?
#! /bin/bash
createuser="tomcat mysql www"
for i in `echo $createuser`
do
useradd $i
done
如果playbook中也存在这样的循环控制,我们也可以像写shell一样简单的去完成多用户创建工作。
在playbook中使用with_items
去实现循环控制,且循环时的中间变量(上面shell循环中的$i
变量)只能是关键字item
,而不能随意自定义。
在上面的基础上,改进的playbook
在这里使用定义了剧本变量createuser(一个列表),然后通过with_items循环遍历这个变量来达到创建用户的目的。
[root@ansible-01 ~]# cat playbook01.yml
---
- name: variable playbook example
hosts: webservers
vars:
createuser:
- tomcat
- www
- mysql
tasks:
- name: create user
user: name={{ item }} state=present
with_items: "{{ createuser }}"
- name: yum nginx webservers
yum: name=nginx state=present
- name: update nginx main config
copy: src=nginx.conf dest=/etc/nginx/
- name: add virtualhost config
copy: src=www.xiaoma.com.conf dest=/etc/nginx/conf.d/
- name: check nginx synstx
shell: /usr/sbin/nginx t
register: nginxsyntax
- name: print nginx syntax
debug: var=nginxsyntax
- name: start nginx server
service: name=nginx state=started
when: nginxsyntax.rc == 0
解决了以上问题,playbook已经有了很大的改进
四、Tags属性
我们可以通过play中的tags属性,去解决目前playbook变更而导致的扩大变更范围和变更风险的问题。
在改进的playbook中,针对文件发布TASK任务
"update nginx main config" 和 "add virtualhost config"
新增了属性tags,属性值为updateconfig
另外我们新增"reload nginx server" TASK 任务。当配置文件更新后,去reload nginx 服务。那重新加载需要依赖nginx服务是已经启动状态。所以,还需要进一步通过判断nginx的pid文件存在,才证明nginx服务本身是启动中,启动中才可以reload nginx服务。
判断一个文件是否存在使用stat
模块
- name: check nginx running
stat: path=/var/run/nginx.pid
register: nginxrunning
观察结果会发现:nginxrunning.stat.exists 的值是 true 就表示启动 false 就表示关闭状态。
改进playbook
---
- name: variable playbook example
hosts: webservers
vars:
createuser:
- tomcat
- www
- mysql
tasks:
- name: create user
user: name={{ item }} state=present
with_items: "{{ createuser }}"
- name: yum nginx webservers
yum: name=nginx state=present
- name: update nginx main config
copy: src=nginx.conf dest=/etc/nginx/
tags: updateconfig
- name: add virtualhost config
copy: src=www.xiaoma.com.conf dest=/etc/nginx/conf.d/
tags: updateconfig
- name: check nginx synstx
shell: /usr/sbin/nginx t
register: nginxsyntax
tags: updateconfig
- name: check nginx running
stat: path=/var/run/nginx.pid
register: nginxsyntax
tags: updateconfig
- name: print nginx syntax
debug: var=nginxsyntax
- name: start nginx server
service: name=nginx state=started
when: nginxsyntax.rc == 0
五、Handlers: 在发生改变时执行的操作
我们曾提到过module
具有"幂等"性,所以当远端系统被人改动时,可以重放 playbooks 达到恢复的目的. playbooks 本身可以识别这种改动,并且有一个基本的 event system(事件系统),可以响应这种改动.
当发生改动时notify actions 会在 playbook 的每一个 task 结束时被触发,而且即使有多个不同的 task 通知改动的发生, notify actions 只会被触发一次.
举例来说,比如多个 resources 指出因为一个配置文件被改动,所以 apache 需要重新启动,但是重新启动的操作只会被执行一次.
这里有一个例子,当一个文件的内容被改动时,重启两个 services:
- name: template configuration file
template: src=template.j2 dest=/etc/foo.conf
notify:
- restart memcached
- restart apache
notify
下列出的即是 handlers
Handlers
也是一些 task 的列表,通过名字来引用,它们和一般的 task
并没有什么区别.Handlers
是由通知者进行 notify
, 如果没有被 notify
,handlers
不会执行.不管有多少个通知者进行了 notify
等到 play 中的所有 task
执行完成之后,handlers
也只会被执行一次.
示例
handlers:
- name: restart memcached
service: name=memcached state=restarted
- name: restart apache
service: name=apache state=restarted
Handlers
最佳的应用场景是用来重启服务,或者触发系统重启操作.除此以外很少用到了
值得指出的是,handlers 会在 ‘pre_tasks’, ‘roles’, ‘tasks’, 和 ‘post_tasks’ 之间自动执行. 如果你想立即执行所有的 handler 命令,在1.2及以后的版本,你可以这样做:
tasks:
- shell: some tasks go here
- meta: flush_handlers
- shell: some other tasks
在以上的例子中,任何在排队等候的 handlers 会在执行到 ‘meta’ 部分时,优先执行.这个技巧在有些时候也能派上用场