自动化运维利器Ansible-Playbook的任务控制


文章目录

  • 自动化运维利器Ansible-Playbook的任务控制
  • 条件判断
  • 示例1
  • 示例2
  • 练习
  • 循环控制
  • 模板
  • 模板 new
  • 循环加判断
  • Tags 属性
  • 测试文件是否存在
  • Handlers 属性
  • 练习



条件判断

示例1
[root@localhost ~]# cat hosts 
[webservers]
192.168.116.145  ansible_ssh_port=2222

[root@localhost ~]# cat when.yml 
---

- name: 条件判断的小示例
  hosts: webservers
  gather_facts: no 
  tasks: 
    - name: exec shell
      shell: echo $UID
      register: ret
#    - name: show ret
#      debug:
#        var: ret
- name: 利用执行结果进行判断
  when: ret.stdout == "0"
  shell: touch /root/Neko.txt
...

[root@localhost ~]# ansible-playbook -i hosts when.yml
[root@localhost ~]# ls
a.sh             hosts          Neko.txt
示例2
[root@localhost ~]# cat hosts 
[dbservers]
192.168.116.131 
192.168.116.145 ansible_ssh_port=2222 ansible_ssh_user=liu

[root@localhost ~]# cat when.yml 
---
- name: 条件判断的小示例
  hosts: dbservers
  gather_facts: no 
  tasks: 
    - name: exec shell
      shell: echo $UID
      register: ret
#    - name: show ret
#      debug:
#        var: ret
- name: 利用执行结果进行判断
  when: ret.stdout == "0"
  shell: touch /root/Neko.txt
...
[root@localhost ~]# ansible-playbook -i hosts when.yml
练习

1 为什么要判断

  • 根据业务需求判断

2 判断什么

  • 命令执行是否成功,也就是 $? 的值
  • 命令执行后的返回结果
    需要知道命令返回结果的数据结构
    debug
  • 使用 when 属性进行判断
[root@localhost ~]# cat when2.yml 
---
- name: 判断是否存在用户
  hosts: dbservers
  gather_facts: no 
  tasks:
    - name: 执行shell命令,获取用户信息
      shell: id tom && echo "ok" || echo "no"
      register: tom1
    - name: touch a file 
      when: tom1.stdout_lines[-1] == "ok"
      file: 
        path: /home/tom/love 
        state: touch
    - name: 没有则创建这个用户
      when: tom1.stdout_lines[-1] == "no"
      user: name=tom
...

循环控制

循环 https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html

[root@localhost ~]# cat with.yml 
---
- name: 循环打印
  hosts: dbservers
  gather_facts: no
  vars:
    nums:
      - 1
      - shark
      - hello
  tasks:
    - name: 输出被循环的元素
      with_items: "{{ nums }}"
      debug:
        var: item
...
模板
[root@localhost ~]# cat info.yml 
---

info:
  a: 1
  b: 2
  c: 3

infos:

  - user: shark
    age: 18
  - user: tom
    age: 19

#{"info": {"a":1, "b":2, "c":3}}
#{infos: [{"user": "shark", "age":18},{"user": "tom", "age": 19}}
...

[root@localhost ~]# cat infos.yml 
---

- name: 循环打印
  hosts: dbservers
  gather_facts: no
  vars:
    nums:
      - 1
      - shark
      - hello
  vars_files:
    - info.yml
      tasks:
    - name: 输出被循环的元素
      with_items: "{{ nums }}"
      debug:
        var: item
    - name: 循环字典
      with_items: "{{ infos }}"
      debug:
        msg: "姓名是:{{item.user}},年龄是: {{item.age}}"
...
模板 new
##### 

[root@localhost ~]# cat infox.yml
---

- name: 循环打印
  hosts: dbservers
  gather_facts: no
  vars:
    nums: [1, "1", shark, "hello"]
    info: {a: 1, b: 2, c: 3}
    infos: [{"user": shark, age: 18},{user: xiguatian, age: 19}]
  tasks:
    - name: 循环列表
      with_items: "{{ nums }}"
      debug:
         var: item
    - name: 循环字典
      with_items: "{{ info | dict2items }}"
      debug:
        msg: "姓名是:{{item.key}}, 年龄是: {{item.value}}"
    - name: 循环列表和字典
      with_items: "{{ infos }}"
      debug:
        msg: "姓名是:{{item.user}}, 年龄是: {{item.age}}"

...

循环加判断

[root@localhost ~]# cat loop.yml 
---

- name: 循环中使用判断
  hosts: dbservers
  gather_facts: no
  vars:
    nums:
      - 1
      - 2
      - 3
  tasks:
    - name: 循环元素,只打印大于1的元素
      loop: "{{ nums }}"
      when: item > 1
      debug:  
        var: item
...

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

需求如下:

1 在下面的PlayBook中,我们创建了 tomcat、www 和 mysql 三个用户。

安装了Nginx 软件包、并同时更新了 Nginx 主配置文件和虚拟主机配置文件,最后让Nginx 服务处于启动状态。

playbook 如下:

[root@localhost ~]# cat when.nginx.yml 
---

- name: task control playbook example
  hosts: webservers
  gather_facts: no
  vars:
    users: [tomcat, www, mysql]
  tasks:
    - name: create tomcat user
      loop: "{{ users }}"
      user:
        name: "{{ item }}"
        state: present

    - name: copy nginx repo file
      copy: src=nginx.repo  dest=/etc/yum.repos.d/nginx.repo

    - 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
      when: nginxsyntax.rc == 0
      service: name=nginx state=started

用到的配置文件

# 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 子配置文件

# cat www.neko.com.conf
server {
    listen 80;
    server_name www.neko.com;
    root /usr/share/nginx/html;
    access_log /var/log/nginx/www.neko.com-access_log main;
    error_log  /var/log/nginx/www.neko.com-error_log;
    
    add_header Access-Control-Allow-Origin *;

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
        expires      1d;
    }

    location ~ .*\.(js|css)?$ {
        expires      1d;
    }

}

我们下面将以解决一个个问题的形式去优化上例中的PlayBook。

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

Tags 属性

[root@localhost ~]# cat tags.yml 
---
- name: 使用 tags 执行指定的任务
  hosts: webservers
  gather_facts: no 
  vars: 
    a: 1
    b: 2
    c: 3
  tasks:
    - name: 输入 a
      debug: var=a
      tags: Neko
    - name: 输入 b
      debug: var=b
      tags: Neko

    - name: 输入 c
      debug: var=c
      tags: ohhh
    - name: 输入 a and b
      debug: msg="{{a}}{{b}}"
...

[root@localhost ~]# ansible-playbook -i hosts tags.yml
[root@localhost ~]# ansible-playbook -i hosts tags.yml  -t Neko
[root@localhost ~]# ansible-playbook -i hosts tags.yml  -t Neko,ohhh

测试文件是否存在

[root@localhost ~]# cat file.yml 
---
- name: 测试文件是否存在
  hosts: webservers
  gather_facts: no 
  tasks: 
    - name: 测试
      file:
        path: /var/run/nginx.pid
        state: file
      register: ret
    - name: debug
      debug: var=ret
...
[root@localhost ~]# ansible-playbook -i hosts  file.yml

Handlers 属性

观察当前的 Playbook,细心的你会发现,当我的配置文件没有发生变化时,每次依然都会去触发TASK “reload nginx server”。

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

- 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: task control playbook example
  hosts: webservers
  gather_facts: no
  vars:
    createuser:
     - tomcat
     - www
     - mysql

  tasks:
    - name: create tomcat user
      with_items: "{{ createuser }}"
      user:
        name: "{{ item }}"
        state: present

    - name: yum nginx webserver
      yum: name=nginx state=present

    - name: update nginx main config
      copy: src=nginx.conf dest=/etc/nginx/nginx.conf
      tags: updateconfig
      notify: reload config file

    - name: add virtualhost config
      copy: 
        src: www.qfedu.com.conf 
        dest: /etc/nginx/conf.d/www.qfedu.com.conf
      tags: updateconfig
      notify: reload config file

    - name: 检查nginx 配置文件的语法
      shell: /usr/sbin/nginx -t
      register: nginxsyntax
      tags: updateconfig

    - name: test nginx is running
      shell: "ps -ef|grep 'nginx: maste[r]'|awk '{print $2}'"
      register: nginxrunning
      tags: updateconfig

    - name: start nginx server
      when:
        - nginxsyntax.rc == 0
        - nginxrunning.stdout == ""
      service: name=nginx state=started
      tags: updateconfig
  handlers:
    - name: reload config file
      when: nginxsyntax.rc == 0 and nginxrunning.stdout
      service:
        name: nginx
        state: reloaded
...

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

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

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

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

当修改 ansible 主机上的 my.cnf 文件后 触发 dbservers 服务器中的 MySQL 服务重启
编写 yum 文件,部署 MySQL

[root@localhost ~]# cat mysql.yml 
---
- name: 重启MySQL服务
  hosts: dbservers
  gather_facts: no
  tasks:
    - name: 下载yum仓库
      yum: 
        name: https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
        state: installed
    - name: 打开5.7版本
      ini_file:
        path: /etc/yum.repos.d/mysql-community.repo 
        section: mysql57-community
        option: enabled
        value: 1
    - name: 关闭8.0版本
      ini_file:
        path: /etc/yum.repos.d/mysql-community.repo 
        section: mysql80-community
        option: enabled
        value: 0
    - name: yum安装MySQL
      yum: name=mysql-community-server state=present
    - name: 设置开机自启 设置服务自启
      systemd: name=mysqld enabled=yes state=restarted

    - name: copy模块修改文件,触发重启
      copy:
        src: /etc/my.cnf
        dest: /root/my.cnf
      notify: restart mysqld
      handlers:
    - name: restart mysqld
      systemd: name=mysqld state=restarted
...