Ansible之Playbook的任务控制

一)Ansible 任务控制基本介绍

这⾥主要来介绍PlayBook中的任务控制。

任务控制类似于编程语⾔中的if ... 、for ... 等逻辑控制语句。

这⾥我们给出⼀个实际场景应⽤案例去说明在PlayBook中,任务控制如何应⽤。

在下⾯的PlayBook中,我们创建了 tomcat、www 和 mysql 三个⽤户。 安装了Nginx 软件包、并同时更新了 Nginx 主配置⽂件和虚拟主机配置⽂件,最后让Nginx 服务处于启动状态。

整个PlayBook从语法上没有任何问题,但从逻辑和写法上仍然有⼀些地⽅需要我们去注意及优化:

  1. Nginx启动逻辑⽋缺考虑。若Nginx的配置⽂件语法错误则会导致启动Nginx失败,以⾄于PlayBook执⾏失败。
  2. 批量创建⽤户,通过指令的罗列过于死板。如果再创建若⼲个⽤户,将难以收场。
shell 复制代码
---
- 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 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: start nginx server
    service: name=nginx state=started

二)条件判断

解决第一个问题:Nginx启动逻辑⽋缺考虑。 若Nginx的配置⽂件语法错误则会导致启动Nginx失败,以⾄于PlayBook执⾏失败。

如果我们能够在启动之前去对Nginx的配置⽂件语法做正确性的校验,只有当校验通过的时候我们才去启动或者重启Nginx;否则则跳过启动Nginx的过程。这样就会避免Nginx 配置⽂件语法问题⽽导致的⽆法启动Nginx的⻛险。

nginx语法校验:

shell 复制代码
- name: check nginx syntax
  shell: /usr/sbin/nginx -t

那如何将Nginx语法检查的TASK同Nginx启动的TASK关联起来呢?

如果我们能够获得语法检查的TASK的结果,根据这个结果去判断"启动NGINX的TASK"是否执⾏,这将是⼀个很好的⽅案。 如何和获取到语法检查TASK的结果呢? 此时就可以使⽤之前学到的 Ansible中的注册变量:

获取Task任务结果:

shell 复制代码
- name: check nginx syntax
  shell: /usr/sbin/nginx -t
  register: nginxsyntax

此时有可能还有疑问,获取到任务结果后,但是结果⾥⾯的内容是个什么样⼦, 我该根据内容在后续的PlayBook中怎样使用

通过debug模块去确认返回结果的数据结构:

shell 复制代码
- name: print nginx syntax result
  debug: var=nginxsyntax

通过debug模块,打印出来返回结果。那么在变量nginxsyntax的rc为0时语法校验正确。

通过条件判断(when)指令去使用语法校验的结果:

shell 复制代码
- 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:

shell 复制代码
---
- name: task control playbook example
  hosts: webservers
  gather_facts: no
  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 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
    service: name=nginx state=started
    when: nginxsyntax.rc == 0

以上的逻辑,只要语法检查通过都会去执⾏ "start nginx server"这个TASK。 在这个问题的解决⾥,我们学习了when 条件判断和注册变量的结合使⽤。学习了when条件判断中是可以⽀持复杂逻辑的。⽐如现在⽤到的逻辑运算符 and。

另外 when ⽀持如下运算符:

shell 复制代码
==
!=
> >=
< <=
is defined
is not defined
true
false
#⽀持逻辑运算符: 
and or

三)循环控制

解决第二个问题:批量创建⽤户,通过指令的罗列过于死板。如果再创建若⼲个⽤户,将难以收场。

那么如果在创建⽤户时,抛开PlayBook的实现不说, 单纯的使⽤shell去批量的创建⼀些⽤户。通常会怎么写呢?

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 循环遍历变量这个变量来达到创建⽤户的⽬的。

shell 复制代码
- name: variable playbook example
  hosts: webservers
  gather_facts: no
  vars:
   createuser:
    - tomcat
    - www
    - mysql
  tasks:
   - name: create user
     user: name={{ item }} state=present
     with_items: "{{ createuser }}"
   - 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
     service: name=nginx state=started
     when: nginxsyntax.rc == 0

解决了以上问题,整个PlayBook已经有了很⼤的改进。

在这里给大家一个关于遍历的新版本的循环playbook实验:

shell 复制代码
- name: loop item
  hosts: all
  gather_facts: no
  vars:
    some_list:
     - "a"
     - "b"
     - "c"
    num_list:
     - 1
     - 2
     - 3
     - 5
 tasks:
 - name: show item
   debug:
     var: "{{ item }}"
   loop: "{{ some_list }}"
- name: show item when item > 3
  debug:
   var: "{{ item }}"
  loop: "{{ num_list }}"
  when: item > 3

四)Tags属性

考虑这样⼀个情况:

若更新了Nginx 的配置⽂件后,我们需要通过PlayBook将新的配置发布到⽣产服务器上,然后再重新加载我们的Nginx 服务。但以现在的PlayBook来说,每次更改Nginx 配置⽂件后虽然可以通过它发布到⽣产,但整个PlayBook都要执⾏⼀次,这样⽆形中扩⼤了变更范围和

变更⻛险。

Tags 属性就可以解决这个问题。

我们可以通过Play中的tags 属性,去解决⽬前PlayBook变更⽽导致的扩⼤变更范围和变更⻛险的问题。

在改进的PlayBook中,针对⽂件发布TASK 任务:

"update nginx main config" 和 "add virtualhost config"。

新增了属性 tags ,属性值为updateconfig。

新增"reload nginx server" TASK任务。当配置⽂件更新后,去reload Nginx 服务。

判断⼀个⽂件是否存在使⽤ stat 模块:

shell 复制代码
- name: check nginx running
  stat: path=/var/run/nginx.pid
 register: nginxrunning

观察结果,会发现 nginxrunning.stat.exists 的值是 true 就表示启动状态,是 false 就是关闭状态。

接下来下来就可以依据这个结果,来决定是否重新加载 Nginx 服务。

改进后的Playbook:

shell 复制代码
- name: tags playbook example
  hosts: webservers
  gather_facts: no
  vars:
   createuser:
   - tomcat
   - www
   - mysql
  tasks:
  - name: create user
    user: name={{ item }} state=present
    with_items: "{{ createuser }}"
  - name: yum nginx webserver
    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.qfedu.com.conf
          dest=/etc/nginx/conf.d/
    tags: updateconfig
  - name: check nginx syntax
    shell: /usr/sbin/nginx -t
    register: nginxsyntax
    tags: updateconfig
  - name: check nginx running
    stat: path=/var/run/nginx.pid
    register: nginxrunning
    tags: updateconfig
  - name: print nginx syntax
    debug: var=nginxsyntax
 
  - name: print nginx syntax
    debug: var=nginxrunning
  - name: reload nginx server
    service: name=nginx state=started
    when: nginxsyntax.rc == 0 and
    nginxrunning.stat.exists == true
    tags: updateconfig
  - name: start nginx server
    service: name=nginx state=started
    when:
     - nginxsyntax.rc == 0
     - nginxrunning.stat.exists == false
     tags: updateconfig

执⾏时⼀定要指定tags,这样在执⾏的过程中只会执⾏task 任务上打上tag 标记为 updateconfig 的任务

shell 复制代码
ansible-playbook -i hosts site.yml -t updateconfig

五)Handlers属性

观察当前的 Playbook,不能发现,当我的配置⽂件没有发⽣变化时,每次依然都会去触发TASK "reload nginx server"。

如何能做到只有配置⽂件发⽣变化的时候才去触发TASK "reload nginx server",这样的处理才是最完美的实现。此时可以使⽤handlers 属性。

改进的Playbook:

shell 复制代码
- name: handlers playbook example
  hosts: webservers
  gather_facts: no
  vars:
   createuser:
    - tomcat
    - www
    - mysql
  tasks:
  - name: create user
    user: name={{ item }} state=present
    with_items: "{{ createuser }}"
  - name: yum nginx webserver
    yum: name=nginx state=present
  - name: update nginx main config
    copy: src=nginx.conf dest=/etc/nginx/
    tags: updateconfig
    notify: reload nginx server
  - name: add virtualhost config
    copy: src=www.qfedu.com.conf
          dest=/etc/nginx/conf.d/
    tags: updateconfig
    notify: reload nginx server
  - name: check nginx syntax
    shell: /usr/sbin/nginx -t
    register: nginxsyntax
    tags: updateconfig
  - name: check nginx running
    stat: path=/var/run/nginx.pid
    register: nginxrunning
    tags: updateconfig
  - name: start nginx server
    service: name=nginx state=started
    when:
     - nginxsyntax.rc == 0
     - nginxrunning.stat.exists == false
 handlers:
   - name: reload nginx server
     service: name=nginx state=reloaded
     when:
      - nginxsyntax.rc == 0
      - nginxrunning.stat.exists == true

在改进的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任务

shell 复制代码
ansible-playbook -i hosts site.yml -t updateconfig

⼈为对Nginx 配置⽂件稍作修改,只要MD5校验值发⽣变化即

可。此时再执⾏,发现触发了handlers 中的TASK任务

shell 复制代码
 ansible-playbook -i hosts site.yml -t updateconfig 1
相关推荐
独行soc7 小时前
2025年渗透测试面试题总结-66(题目+回答)
java·网络·python·安全·web安全·adb·渗透测试
sun0077008 小时前
android ndk编译valgrind
android
AI视觉网奇9 小时前
android studio 断点无效
android·ide·android studio
jiaxi的天空9 小时前
android studio gradle 访问不了
android·ide·android studio
No Silver Bullet10 小时前
android组包时会把从maven私服获取的包下载到本地吗
android
catchadmin10 小时前
PHP serialize 序列化完全指南
android·开发语言·php
tangweiguo0305198712 小时前
Kable使用指南:Android BLE开发的现代化解决方案
android·kotlin
00后程序员张14 小时前
iOS App 混淆与资源保护:iOS配置文件加密、ipa文件安全、代码与多媒体资源防护全流程指南
android·安全·ios·小程序·uni-app·cocoa·iphone
柳岸风16 小时前
Android Studio Meerkat | 2024.3.1 Gradle Tasks不展示
android·ide·android studio
编程乐学16 小时前
安卓原创--基于 Android 开发的菜单管理系统
android