美文网首页ansible
Ansible-6 ansible任务控制

Ansible-6 ansible任务控制

作者: Habit_1027 | 来源:发表于2020-01-15 19:40 被阅读0次

    这里主要来介绍PlayBook中的任务控制,任务控制类似于编程语言中的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 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
    
    image.png

    查看 nginx.conf

    
    # 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;
    }
    

    查看 www.qfedu.com.conf

    # cat www.qfedu.com.conf
    server {
        listen 80;
        server_name www.qfedu.com;
        root /usr/share/nginx/html;
        access_log /var/log/nginx/www.qfedu.com-access_log main;
        error_log  /var/log/nginx/www.qfedu.com-error_log;
    
        add_header Access-Control-Allow-Origin *;
    
        location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
                expires      1d;
        }
    
        location ~ .*\.(js|css)?$ {
                expires      1d;
        }
    }
    

    我们下面将以解决一个个问题的形式去优化上例中的PlayBook。通过问题的解决,来达到我们学习任务控制的目的

    条件判断

    解决第一个问题

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

    如果我们能够在启动之前去对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 时语法校验正确。

    "nginxsyntax": {
            "changed": true,
            "cmd": "/usr/sbin/nginx -t",
            "delta": "0:00:00.012045",
            "end": "2017-08-12 20:19:04.650718",
            "rc": 0,
            "start": "2017-08-12 20:19:04.638673",
            "stderr": "nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\nnginx: configuration file /etc/nginx/nginx.conf test is successful",
            "stderr_lines": [
                "nginx: the configuration file /etc/nginx/nginx.conf syntax is ok",
                "nginx: configuration file /etc/nginx/nginx.conf test is successful"
            ],
            "stdout": "",
            "stdout_lines": []
        }
    

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

        - 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

    ---
    - 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 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。当反复执行这个PlayBook 的,除了第一次外,其他"start nginx server" 的TASK是没有必要的执行的。能否避免这样的问题呢?
    我们在判断"start nginx server" 这个TASK任务是否启动的条件基础上再加一个条件。只有当语法校验正确、且Nginx 服务没有启动的时候再去启动服务。

    ---
    - 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 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: check nginx running
          stat: path=/var/run/nginx.pid
          register: nginxrunning
          
        - name: print nginx syntax
          debug: var=nginxsyntax
        # 只有当Nginx 的语法校验正确、且Nginx 没有启动的情况下才会去启动Nginx 。
        # 再次执行PlayBook 时,会发现"start nginx server" 的TASK被 skipping 了。
        - name: start nginx server
          service: name=nginx state=started
          when: nginxsyntax.rc == 0 and nginxrunning.stat.exists == false
    
    在这个问题的解决里,我们学习了when条件判断和注册变量的结合使用。学习了when条件判断中是可以支持复杂逻辑的。比如现在用到的逻辑运算符 and。 
    另外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 循环遍历变量这个变量来达到创建用户的目的。
    
    
    - 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 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: check nginx running
          stat: path=/var/lock/subsys/nginx
          register: nginxrunning
          
        - name: print nginx syntax
          debug: var=nginxsyntax
    
        - name: start nginx server
          service: name=nginx state=started
          when: nginxsyntax.rc == 0 and nginxrunning.stat.exists == false
    
    解决了以上问题,整个PlayBook已经有了很大的改进。但我们在生产环境中验证时发现还是不够灵活。
    若更新了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 服务。这样对配置文件更新的操作将很完美。
    
    

    改进PlayBook

    - name: tags playbook example
      hosts: web-servers
      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: reload nginx server
          service: name=nginx state=reloaded
          when: nginxsyntax.rc == 0 and nginxrunning.stat.exists == true
          tags: updateconfig
    
        - name: start nginx server
          service: name=nginx state=started
          when: nginxsyntax.rc == 0 and nginxrunning.stat.exists == false
    

    指定tags 去执行PlayBook

    // 执行时一定要指定tags,这样再执行的过程中只会执行task 任务上打上tag 标记为 updateconfig 的任务
    # ansible-playbook site.yml -t updateconfig
    
    但反复执行此PlayBook依然存在问题。当我的配置文件没有发生变化时,每次依然都会去触发TASK "reload nginx server"。如何能做到只有配置文件发生变化的时候才去触发TASK "reload nginx server",这样的处理才是最完美的实现。此时可以使用handlers 属性。
    
    
    Handlers属性
    在改进的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 名称对应上。
    
    

    改进PlayBook

    - name: handlers playbook example
      hosts: web-servers
      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/lock/subsys/nginx
          register: nginxrunning
          tags: updateconfig
    
        - name: print nginx syntax
          debug: var=nginxsyntax
    
        - name: start nginx server
          service: name=nginx state=started
          when: nginxsyntax.rc == 0 and nginxrunning.stat.exists == false
      handlers:
        - name: reload nginx server
          service: name=nginx state=reloaded
          when: nginxsyntax.rc == 0 and nginxrunning.stat.exists == true
    

    执行

    // 首次执行,若配置文件没有发生变化。发现根本就没有触发handlers 中TASK任务
    # ansible-playbook site.yml -t updateconfig
    // 人为对Nginx 配置文件稍作修改,只要MD5校验值发生变化即可。此时再执行,发现触发了handlers 中的TASK任务
    # ansible-playbook site.yml -t updateconfig
    

    相关文章

      网友评论

        本文标题:Ansible-6 ansible任务控制

        本文链接:https://www.haomeiwen.com/subject/pcxxzctx.html