Absible#Ansible-Playbook的任务控制

Ansible-Playbook的任务控制

一、任务控制基本介绍

这里主要来介绍PlayBook中的任务控制。
  任务控制类似于编程语言中的if … 、for … 等逻辑控制语句
1、Playbook示例

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

---
- 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

下面针对上面的playbook的问题,进行优化

二、条件判断

问题一、Nginx启动逻辑欠缺考虑
  启动Nginx之前需要进行配置文件语法检测
  检验通过之后再进行启动Nginx
  Nginx 语法校验
  - name: check nginx syntax
  shell: nginx -t
  通过注册变量将Nginx检查语法的Tasks结果与Nginx的启动Tasks关联起来,根据这个结果去判断“启动NGINX的TASK”是否执行
  获取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 时语法校验正确

用到的nginx配置文件
主配置文件

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

nginx子配置文件

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

通过条件判断(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 palybook examle
  hosts: 10.11.67.32
  gather_facts: no
  remote_user: root
  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 config
      copy: src=nginx.conf dest=/etc/nginx/conf.d/

    - name: check nginx config
      shell: ls
      register: nginxsyncheck

    - name: print nginxsyncheck
      debug: var=nginxsyncheck


    - name: start nginx server
      systemd: name=nginx state=started
      when: nginxsyncheck.rc == 0

以上的逻辑,只要语法检查通过都会去执行 "start nginx server"这个TASK。在这个问题的解决里,我们学习了when 条件判断和注册变量的结合使用。学习了when条件判断中是可以支持复杂逻辑的。比如现在用到的逻辑运算符 and
  ==
  !=
  > >=
  < <=
  is defined
  is not defined
  true
  false
  支持逻辑运算符: and or

三、循环控制

三、循环控制
  playbook也存在循环控制,我们也可以像写shell一样简单的去完成多用户创建工作,在PlayBook中使用 with_items 去实现循环控制,且循环时的中间变量循环变量只能是关键字 item,而不能随意自定义
  with_items:实现循环控制
  item:定义循环变量
  使用定义了剧本变量 createuser(一个列表) ,然后通过 with_items 循环遍历变量这个变量来达到创建用户的目的。

- 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 }}"
      # createuser 是被循环的对象
      # item 每次循环被赋值的变量
- 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 }}"
      # createuser 是被循环的对象
      # item 每次循环被赋值的变量

    - 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

新版本的loop循环

- 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
      # 循环中还可以加判断,这里是当循环的数据是大于 3 的,
      # 才赋值个 var ,从而打印出来
[kakaops@ka ansible]$ sudo ansible-playbook loop.yaml 
[sudo] kakaops 的密码:
 __________________
< PLAY [loop item] >
 ------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

 _______________________
< TASK [show some_list] >
 -----------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

Saturday 07 November 2020  09:14:32 +0800 (0:00:00.111)       0:00:00.112 ***** 
ok: [10.11.67.32] => (item=a) => {
    "ansible_loop_var": "item", 
    "item": "a"
}
ok: [10.11.67.32] => (item=b) => {
    "ansible_loop_var": "item", 
    "item": "b"
}
ok: [10.11.67.32] => (item=c) => {
    "ansible_loop_var": "item", 
    "item": "c"
}

 ____________________________________
< TASK [show num_list when item > 3] >
 ------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

Saturday 07 November 2020  09:14:32 +0800 (0:00:00.159)       0:00:00.271 ***** 
skipping: [10.11.67.32] => (item=1) 
skipping: [10.11.67.32] => (item=2) 
skipping: [10.11.67.32] => (item=3) 
skipping: [10.11.67.32] => (item=4) 
ok: [10.11.67.32] => (item=5) => {
    "ansible_loop_var": "item", 
    "item": 5
}
ok: [10.11.67.32] => (item=6) => {
    "ansible_loop_var": "item", 
    "item": 6
}
 ____________
< PLAY RECAP >
 ------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

10.11.67.32                : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Saturday 07 November 2020  09:14:32 +0800 (0:00:00.168)       0:00:00.439 ***** 
=============================================================================== 
debug ------------------------------------------------------------------- 0.33s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
total ------------------------------------------------------------------- 0.33s
Playbook run took 0 days, 0 hours, 0 minutes, 0 seconds

四、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 服务。那重新加载需要依赖于 Nginx 服务是已经启动状态。所以,还需要进一步通过判断 Nngix 的 pid 文件存在,才证明 Nginx 服务本身是启动中,启动中才可以 reload Nginx 服务。 判断一个文件是否存在使用 stat 模块
判断一个文件是否存在使用 stat 模块

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

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

- 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=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
        - nginxrunning.stat.exists == false
      tags: updateconfig

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

# ansible-playbook -i hosts site.yml -t updateconfig

结合Ansible的错误处理逻辑,上面的play漏洞百出
下面是我还改进的playbook

- name: tags playbook example
  hosts: 10.11.67.32
  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: nginx -t
      register: nginxsyntax
      tags: kakaops

    - name: check nginx running
      shell: systemctl is-active nginx
      register: nginxrunning
      tags: kakaops
      ignore_errors: yes

    - name: print nginx syntax
      debug: var=nginxsyntax
      tags: kakaops
   
    - name: print nginx syntax
      debug: var=nginxrunning
      tags: kakaops

    - name: reload nginx config
      systemd: name=nginx state=reloaded
      when: nginxsyntax.rc == 0
      tags: kakaops

    - name: systemctl start nginx
      systemd: name=nginx state=started
      when:
        - nginxsyntax.rc == 0
        - nginxrunning.rc != 0
      tags: kakaops



大体逻辑是这样的,检查配置文件的错误不能忽略,一旦配置文件出错,立即终止play,只有检查配置文件正确的,才继续往下执行,重新加载配置文件。检查nginx是否开启的错误输出可以用ignore_erroes=yes忽略,通过条件判断,如果没开启,就会开启nginx。判断nginx是否开启,我没有用stat模块,检查/var/run/nginx.pid是否存在,因为这样的检查方法存在一个bug。当nginx停止的时候,上面的play执行了一个nginx -t 的命令,产生/var/run/nginx.pid文件。这时候,nginx没有启动,但我们的启动逻辑会失效

五、Handlers属性

观察当前的 Playbook,细心的你会发现,当我的配置文件没有发生变化时,每次依然都会去触发TASK “reload nginx server”。如何能做到只有配置文件发生变化的时候才去触发TASK “reload nginx server”,这样的处理才是最完美的实现。此时可以使用 handlers 属性
  handlers 英 [ˈhændləz] 美 [ˈhændlərz] n.处理程序,经办人
  notify 英 [ˈnəʊtɪfaɪ] 美 [ˈnoʊtɪfaɪ] v.(正式)通报,通知
  在改进的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 名称对应上。修改任何一方的文件都会导致两端的文件不一致,都会触发任务的执行。

- name: test handlers
  hosts: all
  gather_facts: no
  tasks:
    - name: copy a file to remote server
      copy:
        src: a.txt
        dest: /tmp/a.txt
      notify:
        - test ping
        - ping a remote server

  handlers:
    - name: ping a remote server
      ping:

    - name: test ping
      ping:
...

改进PlayBook

- 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

执行**

首次执行,若配置文件没有发生变化,可以发现根本就没有触发handlers 中TASK任务

# ansible-playbook  -i hosts site.yml -t updateconfig

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

# ansible-playbook -i hosts site.yml -t updateconfig

六、Ansible中处理失败的任务

1、Ansible管理play中的任务错误的正常逻辑

Ansible评估任务的返回代码,从而确定任务是成功还是失败
  通常而言,当任务失败时,Ansible将立即在该主机上中止play的其余部分并且跳过所有后续任务,但有些时候,可能希望即使在任务失败时也继续执行play
  如果判断任务因为判断失败,被Ansible终止后续play的话,那么判断任务还有什么意义?

2、忽略任务失败 ignore_errors: yes

默认情况下,任务失败时play会中止。不过,可以通过忽略失败的任务来覆盖此行为。可以在任务中使用ignore_errors关键字来实现此目的
tasks:
- name: install httpd
yum:
name: packages //没有这个包
state: present
ignore_errors: yes //可选{yes、no}

//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
  gather_facts: no
  tasks:
    - name: install httpd
      yum:
        name: packages      //没有这个包
        state: present
      ignore_errors: yes     //可选{yes、no}
 
    - name: shoe some massage
      debug:
        msg: "hello word"
 
 //执行play
[root@localhost project]# ansible-playbook playbook.yaml
 
PLAY [all] ****************************************************************************************************************************************************************
 
TASK [install httpd] ******************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"}, "changed": false, "failures": ["No package packages available."], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
...ignoring         //已经忽略这个任务出错
 
TASK [shoe some massage] **************************************************************************************************************************************************
ok: [client.example.com] => {
    "msg": "hello word"
}
 
PLAY RECAP ****************************************************************************************************************************************************************

3、任务失败也强制执行处理程序(handlers)

在play中设置force_handlers: yes关键字,则即使play因为后续任务失败而中止也会调用被通知的处理程序(force:促使,推动)
//查看playbook
[root@localhost project]# cat playbook.yaml


  • hosts: all
    force_handlers: yes //可选{yes、no}
    tasks:

    • name: install httpd
      shell: ls //这条命令一定会执行成功,从而保证handlers处理程序一定会被触发
      notify:

      • massage
    • name: install httpd
      yum:
      name: packages //没有这个包,肯定会出错
      state: present

    handlers:

    • name: massage
      debug:
      msg: “hello word”
//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
  force_handlers: yes     //可选{yes、no}
  tasks:
    - name: install httpd
      shell: ls        //这条命令一定会执行成功,从而保证handlers处理程序一定会被触发
      notify:
        - massage
 
    - name: install  httpd
      yum:
        name: packages       //没有这个包,肯定会出错
        state: present
 
  handlers:
    - name: massage
      debug:
        msg: "hello word"
 
 //执行play
[root@localhost project]# ansible-playbook playbook.yaml
 
PLAY [all] ****************************************************************************************************************************************************************
 
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
 
TASK [install httpd] ******************************************************************************************************************************************************
changed: [client.example.com]
 
TASK [install  httpd] *****************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"changed": false, "failures": ["No package packages available."], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
 
RUNNING HANDLER [massage] *************************************************************************************************************************************************
ok: [client.example.com] => {
    "msg": "hello word"
}
 
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com         : ok=3    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

处理程序会在任务报告changed结果时获得通知,而在任务报告ok或failed结果时不会获得通知

4、指定任务失败的条件

在任务中使用failed_when关键字来指定表示任务已失败的条件;通常与命令模块搭配使用,这些模块可能成功执行了某一命令,但命令的输出可能指示了失败

演示实例一:使用failed_when关键字
在playbook中执行脚本会以最后一个命令作为错误判断标准,中间错误命令不会影响整体的出错,同样也不会因为中间出错而报错

//查看使用的脚本
[root@localhost project]# cat files/test.sh
#!/bin/bash
cat /root       //这句肯定会出错
echo "hello word"
 //注意:在playbook中执行脚本会以最后一个命令作为错误判断标准,中间错误命令不会影响整体的出错,同样也不会因为中间出错而报错
 
 //查看playbook,执行一次看是否成功
[root@localhost project]# cat playbook.yaml
---
- hosts: all
  tasks:
    - name: test
      script:
        files/test.sh
[root@localhost project]# ansible-playbook playbook.yaml
 
PLAY [all] ****************************************************************************************************************************************************************
 
TASK [Gathering Facts] ****************************************************************************************************************************************************
    ok: [client.example.com]
 
TASK [test] ***************************************************************************************************************************************************************
changed: [client.example.com]
 
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com         : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
 //这样无法判断是否都执行成功
 
 //添加任务失败判断语句
[root@localhost project]# cat playbook.yaml
---
- hosts: all
  tasks:
    - name: test
      script:
        files/test.sh
      register: result
      failed_when: "'Is a directory' in result['stdout']"
[root@localhost project]# ansible-playbook playbook.yaml
 
PLAY [all] ****************************************************************************************************************************************************************
 
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
 
TASK [test] ***************************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"changed": true, "failed_when_result": true, "rc": 0, "stderr": "Shared connection to client.example.com closed.\r\n", "stderr_lines": ["Shared connection to client.example.com closed."], "stdout": "cat: /root: Is a directory\r\nhello word\r\n", "stdout_lines": ["cat: /root: Is a directory", "hello word"]}
 
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com         : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

:fail模块也可用于强制任务失败(主要是将杂乱的提示信息通过自己设置提示方式,达到简单、明了的目的)

演示实例二

//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
  tasks:
    - name: test
      script:
        files/test.sh
      register: result
 
    - fail:
        msg: "There have a failed"
      when: "'Is a directory' in result['stdout']"
 
 //执行play
[root@localhost project]# ansible-playbook playbook.yaml
 
PLAY [all] ****************************************************************************************************************************************************************
 
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
 
TASK [test] ***************************************************************************************************************************************************************
changed: [client.example.com]
 
TASK [fail] ***************************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"changed": false, "msg": "There have a failed"}
 
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com         : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

5、制定何时任务报告"changed"结果

当任务对托管主机进行了更改时,会报告 changed 状态并通知处理程序;如果任务不需要进行更改,则会报告ok并且不通知处理程序
使用changed_when关键字可用于控制任务在何时报告它已进行了更改

演示实例一

//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
  tasks:
    - name: test
      shell: echo "hello word"
 
 //执行后发现,每次都是changed
[root@localhost project]# ansible-playbook playbook.yaml
 
PLAY [all] ****************************************************************************************************************************************************************
 
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
 
TASK [test] ***************************************************************************************************************************************************************
changed: [client.example.com]
 
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com         : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
 
 //添加changed_when关键字,以便报告OK
[root@localhost project]# cat playbook.yaml
---
- hosts: all
  tasks:
    - name: test
      shell: echo "hello word"
      changed_when: false      //可选{true、false}
[root@localhost project]# ansible-playbook playbook.yaml
 
PLAY [all] ****************************************************************************************************************************************************************
 
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
 
TASK [test] ***************************************************************************************************************************************************************
ok: [client.example.com]
 
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com         : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0    

根据通过已注册变量收集的模块的输出来报告changed

//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
  tasks:
    - name: test
      command: echo "hello word"
      register: result
      changed_when: "'hello word' in result['stdout']"
 
  //执行play
[root@localhost project]# ansible-playbook playbook.yaml
 
PLAY [all] ****************************************************************************************************************************************************************
 
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [client.example.com]
 
TASK [test] ***************************************************************************************************************************************************************
changed: [client.example.com]
 
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com         : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
  //因为在result['stdout']中有hello word ,所以被认定为是true,所以就显示changed

6、Ansible块和错误处理

1️⃣:在playbook中,块是对任务进行逻辑分组的子句,可用于控制任务的执行方式

2️⃣:通过块,也可结合rescue和always语句来处理错误。如果块中的任何任务失败,则执行其rescue块中的任务来进行恢复

3️⃣:在block子句中的任务以及rescue子句中的任务(如果出现故障)运行之后,always子句中的任务运行

4️⃣:总结:

block:定义要运行的主要任务
rescue:定义要在block子句中定义的任务失败时运行的任务
always:定义始终都独立运行的任务,不论block和rescue子句中定义的任务是成功还是失败
5️⃣:演示:

演示实例一:当只有block和rescue,且block语句执行成功时,只执行block语句而不执行rescue语句(rescue:营救、救援)

//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
  gather_facts: no
  tasks:
    - name: test
      block:
        - name: block
          shell: echo "hello word"
 
      rescue:
        - name: rescue
          shell: ls /root
 
 //执行play
[root@localhost project]# ansible-playbook --syntax-check playbook.yaml
 
playbook: playbook.yaml
[root@localhost project]# an
anacron             ansible-config      ansible-console     ansible-galaxy      ansible-playbook    ansible-test       
ansible             ansible-connection  ansible-doc         ansible-inventory   ansible-pull        ansible-vault      
[root@localhost project]# ansible-playbook playbook.yaml
 
PLAY [all] ****************************************************************************************************************************************************************
 
TASK [block] **************************************************************************************************************************************************************
changed: [client.example.com]
 
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com         : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
 //可以发现,只执行了block语句,并没有执行rescue语句

演示实例二:当只有block和rescue,且block语句执行失败时,不执行block语句而执行rescue语句

//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
  gather_facts: no
  tasks:
    - name: test
      block:
        - name: block
          command: cat /        //这句肯定会失败
 
      rescue:
        - name: rescue
          shell: ls /root
 
 //执行play
[root@localhost project]# ansible-playbook playbook.yaml
 
PLAY [all] ****************************************************************************************************************************************************************
 
TASK [block] **************************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"}, "changed": true, "cmd": ["cat", "/"], "delta": "0:00:00.005350", "end": "2020-09-08 10:59:18.381699", "msg": "non-zero return code", "rc": 1, "start": "2020-09-08 10:59:18.376349", "stderr": "cat: /: Is a directory", "stderr_lines": ["cat: /: Is a directory"], "stdout": "", "stdout_lines": []}
 
TASK [rescue] *************************************************************************************************************************************************************
changed: [client.example.com]
 
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com         : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=1    ignored=0  

演示实例三:当block语句、rescue语句和always语句都有时,无论block语句是否失败,always语句总是执行

//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
  gather_facts: no
  tasks:
    - name: test
      block:
        - name: block
          command: cat /
 
      rescue:
        - name: rescue
          shell: ls /root
           
      always:
        - name: always
          debug:
            msg: "This is my test"
 
 //执行play
[root@localhost project]# ansible-playbook playbook.yaml
 
PLAY [all] ****************************************************************************************************************************************************************
 
TASK [block] **************************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"}, "changed": true, "cmd": ["cat", "/"], "delta": "0:00:00.008993", "end": "2020-09-08 11:05:47.816489", "msg": "non-zero return code", "rc": 1, "start": "2020-09-08 11:05:47.807496", "stderr": "cat: /: Is a directory", "stderr_lines": ["cat: /: Is a directory"], "stdout": "", "stdout_lines": []}
 
TASK [rescue] *************************************************************************************************************************************************************
changed: [client.example.com]
 
TASK [always] *************************************************************************************************************************************************************
ok: [client.example.com] => {
    "msg": "This is my test"
}
 
PLAY RECAP ****************************************************************************************************************************************************************
client.example.com         : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=1    ignored=0

6️⃣:block中的when条件也会应用到其rescue和always子句(若存在)
演示实例一:

//查看playbook
[root@localhost project]# cat playbook.yaml
---
- hosts: all
  gather_facts: no
  tasks:
    - name: test
      block:
        - name: block
          command: echo "hello word"         //该语句没有错误
          when: ansible_facts['distribution'] == "CentOS"      //条件判断出错会导致block语句不会执行
 
      rescue:
        - name: rescue
          shell: ls /root
           
      always:
        - name: always
          debug:
            msg: "This is my test"
 
 //执行play
[root@localhost project]# ansible-playbook playbook.yaml
 
PLAY [all] ****************************************************************************************************************************************************************
 
TASK [block] **************************************************************************************************************************************************************
fatal: [client.example.com]: FAILED! => {"msg": "The conditional check 'ansible_facts['distribution'] == \"CentOS\"' failed. The error was: error while evaluating conditional (ansible_facts['distribution'] == \"CentOS\"): 'dict object' has no attribute 'distribution'\n\nThe error appears to be in '/root/project/playbook.yaml': line 7, column 11, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n      block:\n        - name: block\n          ^ here\n"}
 
TASK [rescue] *************************************************************************************************************************************************************
changed: [client.example.com]
 
TASK [always] *************************************************************************************************************************************************************
ok: [client.example.com] => {
    "msg": "This is my test"
}
 
PLAY RECAP ****************************************************************************************************************************************************************
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值