五、Ansible之任务控制

一、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从语法上没有任何问题,但从逻辑和写法上仍然有一些地方需要我们注意和优化。

  1. Nginx启动逻辑欠缺考虑。若nginx的配置文件语法错误则会导致启动nginx失败,以至于playbook执行失败。

  2. 批量创建用户,通过指令的罗列过于死板,如果在创建若干个用户,将难以执行。

二、条件判断

通过解决上面的问题,来达到我们学习任务控制的目的

解决第一个问题
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’ 部分时,优先执行.这个技巧在有些时候也能派上用场

posted @ 2021-11-03 22:23  锦书致南辞  阅读(109)  评论(0编辑  收藏  举报