【Ansible】Ansible Playbook 的任务控制

news2025/1/11 5:48:14

Ansible Playbook 的任务控制


文章目录

  • Ansible Playbook 的任务控制
  • 一、Ansible 任务控制基本介绍
  • 二、条件判断
    • 1.解决第一个问题
    • 2.nginx 语法校验
  • 三、循环控制
  • 四、Tags 属性
  • 五、Handlers 属性


在这里插入图片描述


一、Ansible 任务控制基本介绍

任务控制类似于编程语言中的 if …、for …等逻辑控制语句。下面让我们通过一个实际应用场景案列去说明在 playbook 中,任务控制如何应用。

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

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

1、nginx 启动逻辑欠缺考虑,若nginx的配置文件语法错误则会导致启动 nginx 失败,以至于 playbook 执行失败。
2、批量创建用户,通过指令的罗列过于死板。如果在创建若干个用户,将难以收场。

---
- name: task control playbook example
  hosts: web_servers
  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 web_servers
      yum: name=nginx state=present
    
    - name: update nginx main config
      copy: src=nginx.conf dest=/etc/nginx
    
    - name: add virtualhost config
      copy: src=www.guan.com.cnf  dest=/etc/nginx/conf.d/
    
    - name: start nginx server
      service: name=nginx state=started

...

二、条件判断

1.解决第一个问题

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

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

2.nginx 语法校验

- name: check ngixn syntax
  shell: /usr/sbin/nginx -t

那么如何将 nginx 语法检查的 task 与 nginx 启动的 task 关联起来呢?

如果我们能够获得语法检查的 task 的结果,根据这个结果去判断"启动 nginx 的 task 是否执行,这将是一个很好的方案。"

如何和获取到语法检查 task 的结果呢?此时,就可以使用之前学过的 ansible 中的注册变量。

[root@master1 ~]# vim www.guan.com.conf
[root@master1 ~]# cat www.guan.com.conf
server {
    listen 80;
    server_name www.guan.com.conf;
    root /usr/share/nginx/html;
    access_log /var/log/nginx/www.guan.com-access_log main;
    error_log /var/log/nginx/www.guan.com-error_log;

    add_header Access-Control-Allow-Origin *;
    
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
            expires     1d;
    }

    location ~ .\.(js|css)$? {
            expires     1d;
    }
}
[root@master1 ~]# cat site.yml
---
- name: task control playbook example
  gather_facts : no
  hosts: web_servers
  tasks:
    - name: yum nginx web_servers
      yum: name=nginx state=present
    
    - name: update nginx main config
      copy: src=nginx.conf dest=/etc/nginx
    
    - name: add virtualhost config
      copy: src=www.guan.com.cnf  dest=/etc/nginx/conf.d/
    
    - name: check nginx syntax
      shell: /usr/sbin/nginx -t
      register: nginx_syntax

    - name: print nginx syntax
      debug: var=nginx_syntax

    - name: start nginx server
      service: name=nginx state=started
      when: nginx_syntax.rc == 0
   


...

[root@master1 ~]# ansible-playbook  -i hosts site.yml

以上的逻辑,只要语法检查通过都会去执行 " start nginx service " 这个 task。在这个问题里,我们学习了 when 条件判断和注册变量的结合使用。在学了 when 条件判断中是可以支持复杂逻辑的,比如现在用到的逻辑运算符 and

另外 when 支持如下运算符:

==
!=
>>=
<<=
is defined
is not defined
true
false
支持逻辑运算符:and or

三、循环控制

  • 批量创建用户,通过指令的罗列过于死板,如果在创建若干个用户,将难以收场。

  • 如果在创建用户时,抛开 playbook 的实现不说,单纯的使用shell去批量的创建一些用户,该怎么写才方便呢?

#!/bin/bash
create_user="tomcat mysql www"
for i in `create_user`
do
	useradd $i
done

那么如果 playbook 中也存在这样的循环控制,我们也可以像写 shell 一样简单的去完成多用户创建工作。

在 playbook 中使用 with_items 去实现循环控制,且循环时的时间变量(上面shell 循环中 $i 变量)只能是 关键字 item,而不能是随意自定义的。

基于上面的基础,进一步优化了 playbook

在这里使用定义了剧本变量 create_user(一个列表),然后通过 with_items 循环遍历这个变量来达到创建用户的目的。

---
- name: task control playbook example
  hosts: web_servers
  gather_facts: no
  vars:
    create_user:
      - tomcat
      - www
      - mysql
  tasks:
    - name: create  user
      user: name={{ item }} state=present
      with_items: {{ create_user}}
      
    
    
...

执行

[root@master1 ~]# ansible-playbook  -i hosts site01.yml

PLAY [task control playbook example] ******************************************************************

TASK [create  user] ***********************************************************************************
changed: [192.168.200.183] => (item=tomcat)
changed: [192.168.200.183] => (item=www)
ok: [192.168.200.183] => (item=mysql)

PLAY RECAP ********************************************************************************************
192.168.200.183            : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@master1 ~]# 

进一步优化后

[root@master1 ~]# vim loop.yml
[root@master1 ~]# ansible-playbook  -i hosts loop.yml

PLAY [loop item] **************************************************************************************

TASK [show item] **************************************************************************************
ok: [192.168.200.182] => (item=a) => {
    "a": "VARIABLE IS NOT DEFINED!", 
    "ansible_loop_var": "item", 
    "item": "a"
}
ok: [192.168.200.182] => (item=b) => {
    "ansible_loop_var": "item", 
    "b": "VARIABLE IS NOT DEFINED!", 
    "item": "b"
}
ok: [192.168.200.182] => (item=c) => {
    "ansible_loop_var": "item", 
    "c": "VARIABLE IS NOT DEFINED!", 
    "item": "c"
}
ok: [192.168.200.183] => (item=a) => {
    "a": "VARIABLE IS NOT DEFINED!", 
    "ansible_loop_var": "item", 
    "item": "a"
}
ok: [192.168.200.183] => (item=b) => {
    "ansible_loop_var": "item", 
    "b": "VARIABLE IS NOT DEFINED!", 
    "item": "b"
}
ok: [192.168.200.183] => (item=c) => {
    "ansible_loop_var": "item", 
    "c": "VARIABLE IS NOT DEFINED!", 
    "item": "c"
}

TASK [show item when item > 3] ************************************************************************
skipping: [192.168.200.183] => (item=1) 
skipping: [192.168.200.183] => (item=2) 
skipping: [192.168.200.182] => (item=1) 
skipping: [192.168.200.182] => (item=2) 
skipping: [192.168.200.182] => (item=3) 
ok: [192.168.200.182] => (item=5) => {
    "5": "VARIABLE IS NOT DEFINED!", 
    "ansible_loop_var": "item", 
    "item": 5
}
ok: [192.168.200.182] => (item=6) => {
    "6": "VARIABLE IS NOT DEFINED!", 
    "ansible_loop_var": "item", 
    "item": 6
}
skipping: [192.168.200.183] => (item=3) 
ok: [192.168.200.183] => (item=5) => {
    "5": "VARIABLE IS NOT DEFINED!", 
    "ansible_loop_var": "item", 
    "item": 5
}
ok: [192.168.200.183] => (item=6) => {
    "6": "VARIABLE IS NOT DEFINED!", 
    "ansible_loop_var": "item", 
    "item": 6
}

PLAY RECAP ********************************************************************************************
192.168.200.182            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.200.183            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@master1 ~]# cat loop.yml
- name: loop item
  hosts: all
  gather_facts: no
  vars:
    some_list:
      - "a"
      - "b"
      - "c"
    num_list:
      - 1
      - 2
      - 3
      - 5
      - 6

  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

[root@master1 ~]# 

考虑到这样一个情况:

若更新了 nginx 的配置文件后,我们需要通过 playbook 将新的配置发布到生产服务器上,然后重新加载我们的nginx服务器,但以现在的playbook来说,每次更改nginx配置文件后虽然可以通过它发布到生产,但整个 playbook 都要执行一次,这样无形中扩大了变更范围和变更风险。

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

四、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 服务。

判断一个文件是否存在使用 start 模块

- name: check nginx running
  stat: path=/var/run/nginx.pid
  register: nginx_running

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

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

优化之后的 playbook

- name: task control playbook example
  hosts: web_servers
  gather_facts: no
  vars:
    create_user:
      - tomcat
      - www
      - mysql
  tasks:
    - name: create  user
      user: name={{ item }} state=present
      with_items: "{{ create_user }}"
  
    - name: yum nginx web_servers
      yum: name=nginx state=present
    
    - name: update nginx main config
      copy: src=nginx.conf dest=/etc/nginx/
      tags: update_config
    
    - name: add virtualhost config
      copy: src=www.guan.com.cnf  dest=/etc/nginx/conf.d/
      tags: update_config
    
    - name: check nginx syntax
      shell: /usr/sbin/nginx -t
      register: nginx_syntax
      tags: update_config
    
    - name: check nginx running
      stat: path=/var/run/nginx.pid
      register: nginx_running
      tags: update_config

    - name: print nginx syntax
      debug: var=nginx_syntax
      
    - name: print nginx syntax
      debug: var=nginx_running

    - name: reload nginx server
      service: name=nginx state=started
      when: nginx_syntax.rc == 0 and nginx_running.stat.exists == true
      tags: update_config
      
    - name: start nginx server
      service: name=nginx state=started
      when: 
        - nginx_syntax.rc == 0 
        - nginx_running.stat.exists == false
      tags: update_config
      

指定 tags 去执行 playbook
执行时一定要指定 tags,这样在执行过程中只会执行 task 任务上打上 tag 标记为 update_config 的任务

[root@master1 ~]# ansible-playbook -i  hosts site02.yml -t update_config

五、Handlers 属性

观察当前的 playbook ,不难发现,当我们的配置文件没有发生变化是,每次依然都会去触发 task “reload nginx server”,这样的处理才是最完美的实现。此时,可以使用 handlers 属性。

优化后的 playbook

- name: task control playbook example
  hosts: web_servers
  gather_facts: no
  vars:
    create_user:
      - tomcat
      - www
      - mysql
  tasks:
    - name: create  user
      user: name={{ item }} state=present
      with_items: "{{ create_user }}"
  
    - name: yum nginx web_servers
      yum: name=nginx state=present
    
    - name: update nginx main config
      copy: src=nginx.conf dest=/etc/nginx/
      tags: update_config
      notify: reload nginx server
    
    - name: add virtualhost config
      copy: src=www.guan.com.cnf  dest=/etc/nginx/conf.d/
      tags: update_config
      notify: reload nginx server
    
    - name: check nginx syntax
      shell: /usr/sbin/nginx -t
      register: nginx_syntax
      tags: update_config
    
    - name: check nginx running
      stat: path=/var/run/nginx.pid
      register: nginx_running
      tags: update_config
      
    - name: start nginx server
      service: name=nginx state=started
      when: 
        - nginx_syntax.rc == 0 
        - nginx_running.stat.exists == false
      tags: update_config
  handlers:
    - name: reload nginx server
      service: name=nginx state=reload
      when:
        - nginx_syntax.rc == 0
        - nginx_running.stat.exists == true

在改进的 playbook 中,我们针对文件发布task任务 “update 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 任务

[root@master1 ~]# ansible-playbook -i  hosts site02.yml -t update_config

人为对 nginx 配置文件稍作修改,只要 MD5 校验值发生变化即可。此时,在执行发现出发了 handlers 中的 task 任务。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/160383.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MSF社会工程学

● Metasploit发现两个远程代码执行漏洞 ○ 问题都出在WEB组件方面 ○ MSF不受影响 ● 安全面前任何软件都是平等的 ○ 没有绝对安全的软件 为什么要说社会工程学 ● Metasploit可以很好的配合到社会工程学攻击的各个阶段 ● Setoolkit工具包大量依赖Metasploit ● 基于浏览器…

[SUCTF 2019]EasySQL

目录 预先知识 信息收集 思路 源码分析 非预期解 预期解 补充 预先知识 环境 use mysql; create table if not exists my_table( id int PRIMARY key auto_increment, name VARCHAR(20), age int); insert into my_table values(NULL,xiao,19); insert into my_table v…

BUFF80双模蓝牙5.2热插拔PCB

键盘使用说明索引&#xff08;均为出厂默认值&#xff09;软件支持一些常见问题解答&#xff08;FAQ&#xff09;首次使用测试步骤蓝牙配对规则&#xff08;重要&#xff09;蓝牙和USB切换键盘默认层默认触发层0的FN键配置的功能默认功能层1配置的功能默认的快捷键蓝牙参数蓝牙…

Jetpack Compose中的Canvas

Jetpack Compose中的Canvas API 使用起来感觉比传统View中的要简单一些&#xff0c;因为它不需要画笔Paint和画布分开来&#xff0c;大多数直接就是一个函数搞定&#xff0c;当然也有一些限制。 Compose 直接提供了一个叫 Canvas 的 Composable 组件&#xff0c;可以在任何 Co…

containerd环境下build镜像

containerd环境下build镜像安装nerdctl使用nerdctl打包docker镜像下载安装 buildkit编写systemd unit文件&#xff1a;启用buildkit.service并设置开机自动运行修改Dockerfile构建镜像containerd配置代理containerd配置代理ansible剧本安装nerdctl https://blog.csdn.net/omai…

Python采集最热影评 + 制作词云图

人生苦短&#xff0c;我用Python 电影评论&#xff0c;简称影评 是对一部电影的导演、演员、镜头、摄影、剧情、线索、环境、色彩、光线、视听语言、道具作用、转场、剪辑等进行分析和评论。 电影评论的目的在于分析、鉴定和评价蕴含在银幕中的审美价值、认识价值、社会意义、…

Springboot中,异步线程的执行顺序的控制

1、顺序的定义异步任务存在如下几种顺序&#xff1a;顺序的开启子任务&#xff08;运行顺序和结束顺序不确定&#xff09;。顺序的完成&#xff08;就是A先启动&#xff0c;先执行完&#xff0c;再执行B任务&#xff0c;往往A、B之间存在某种依赖关系&#xff09;。还有就是优先…

思科设备-配置静态路由

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; (*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &a…

【云原生进阶之容器】第四章Operator原理4.2节--CRD

1 CRD概述 1.1 CRD简介 CRD全称是CustomResourceDefinition,在Kubernetes 中一切都可视为资源,Kubernetes 1.7 之后增加了对 CRD 自定义资源二次开发能力来扩展 Kubernetes API,通过 CRD 我们可以向 Kubernetes API 中增加新资源类型,而不需要修改 Kubernetes 源码来创建自…

Django搭建个人博客Blog-Day03

对user模块进行开发设计数据表Django默认就提供了和用户相关的功能&#xff0c;但是这个Django默认提供的功能有个不好的点: 不太适合我们的项目&#xff0c;例如里面的字段不够等&#xff0c;所以我们要对它进行改造一下&#xff0c;方便项目开发。拓展用户模型进入虚拟环境安…

java1算法

排序–comparable接口 java提供了一个接口Comparable用来定义类的排序规则 eg: 1、定义一个学生类Student&#xff0c;具有年龄age和姓名username连个属性&#xff0c;并通过Comparable接口提供比较规则&#xff1b; 2、定义测试类Test,在测试类中定义测试方法Comparable getM…

还不会用YakitBp?来,我教你

前言 &#x1f340;作者简介&#xff1a;被吉师散养、喜欢前端、学过后端、练过CTF、玩过DOS、不喜欢java的不知名学生。 &#x1f341;个人主页&#xff1a;被吉师散养的职业混子 &#x1f342;相应专栏&#xff1a;CTF专栏 Yakit介绍 好兄弟&#xff0c;你听说过yakit吗&…

Redis五大基本数据类型

Redis五大基本数据类型 1.Redis通用命令 常用命令 #Redis的键key操作 keys * 表示查看当前库所有key 【注意是&#xff1a;可以使用通配符】 exists <key> 表示判断某个key是否存在&#xff0c;存在返回1&#xff0c;否则返回0 type <key> 表示判断key类型 del …

fiddler的下载和安装,及常用操作

目录 一、fiddler介绍 二、fiddler的下载和安装 三、fiddler的简单配置 四、fiddler的抓包 五、fiddler的自动响应器 六、fiddler的断点 七、fiddler的构造器 一、fiddler介绍 Fiddler是比较好用的Web代理调试工具之一&#xff0c;它能记录并检查所有客户端与服务端的HT…

【SpringCloud09】Ribbon负载均衡服务调用

1.概述 1.1是什么 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。 简单的说&#xff0c;Ribbon是Netflix发布的开源项目&#xff0c;主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时&…

Java 如何设计一款小游戏详细讲解(一)

目录 介绍 讲解 步骤一 步骤二 阶段(一)详细代码 介绍 注&#xff1a;这里是参考黑马程序员课程&#xff0c;其中图片也是直接拿的黑马程序员提供的图片 其中游戏的代码主要参考黑马程序员的课程&#xff0c;登录和注册主要由自己完成。 黑马素材放到这里自取 链接: https…

【Esp32】Spi接口驱动SX1268模块

【Esp32】Spi接口驱动SX1268模块1.硬件接线2. SX1268 模块的 SPI 时序分析3. 基于 ESP32 实现函数封装3.1 读寄存器3.2 写寄存器4. 实验结果日期&#xff1a; 2023-01-13 项目需要用乐鑫的 ESP32开发板驱动 Lora 模块&#xff0c;用的是 深圳SX1268ZTR4-GC的lora模块&#xff…

通行密钥,向无休止的修改密码说再见~

在上一期里《对称加密面临的那些挑战》中&#xff0c;我们提过密码&#xff0c;和多数人理解的不同&#xff0c;日常生活中用于保护账号安全的所谓“密码”&#xff0c;更贴切的称呼应该是“口令”密码。密码学中的密码&#xff0c;和我们日常生活中登录各类信息化系统所使用的…

Vue--》基于Vue的UI组件库,大家应该熟悉并会使用

目录 PC端UI组件库 Element UI IView UI 移动端UI组件库 Vant Cube UI Mint UI Nut UI UI组件库&#xff1a;它是设计系统的一部分&#xff0c;是在我们常规界面设计过程中可以直接用来制作交互图例和搭建页面的组件集合&#xff0c;它可以作为单个组件独立存在&#x…

Linux下的FrameBuffer驱动框架

一、RGB LCD经典显示器件介绍&#xff1a;1、LCD屏幕的重要属性参数&#xff1a;① 分辨率&#xff1a;也就是屏幕上的像素点的个数&#xff1b;② 像素格式&#xff1a;即单个像素点RGB三种颜色的表达方式&#xff0c;包括RGB888、ARGB8888和RGB565等。③ LCD屏幕硬件接口&…