Ansible流程控制

1.Ansible定义变量

层级定义变量:
#定义变量
lnmp:
  framework:
    package:
      web_pkg: nginx
      db_pkg: mysql
      code_pkg: php-fpm
#添加格式:{{ lnmp.framework.package.web_pkg }}

#playbook剧本中调用变量
- hosts: web_group
  tasks:
  - name: 创建目录
    file:
     path: /opt/{{lnmp.framework.package.db_pkg }}
     state: directory
  - name: 安装lnmp
    yum:
     name:
        - {{ framework.package.web_pkg}}
        - {{ framework.package.db_pkg }}
        - {{ framework.package.code_pkg}}

#定义多重变量
framework:
  lnmp:
    web_pkg: nginx-1.19.2
    db_pkg: mariadb-server
    code_pkg: php-fpm
  lamt:
    web_pkg: httpd
    db_pkg: mysql
    code_pkg: tomcat
  lamp:
    web_pkg: httpd
    db_pkg: mysql
    code_pkg: php-fpm
    web_pkg: httpd
    db_pkg: mysql
    code_pkg: php-fpm
#playbook剧本中调用变量
- hosts: web_group
  tasks:
    - name: 安装lnmp
    yum:
     name:
        - "{{ framework.lnmp.web_pkg }}"
    - "{{ framework.lnmp.db_pkg }}"
    - "{{ framework.lnmp.code_pkg}}"

2.Ansible的facts缓存

playbook在执行之前,使用setup模块获取所有主机信息(变量:内置变量)
#查看主机信息
ansible web01 -m setup

#/etc/motd
欢迎登录 {{ ansible_hostname }}
内网IP:{{ ansible_eth1.ipv4.address }}
外网IP:{{ ansible_eth0.ipv4.address }}
总内存:{{ ansible_memtotal_mb }}
可用内存:{{ ansible_memfree_mb }}

#设置主机登录提示
- hosts: web_group
  tasks:
  - name: 推送
     copy:
    template:
      src: /root/wordpress/motd
      dest: /etc/

#创建内网、外网、总内存、用户名目录
- hosts: web_group
  tasks:
  - name: 创建"{{ ansible_eth1.ipv4.address }}"
    file:
      path: /tmp/{{ ansible_eth1.ipv4.address }}
      state: directory
  - name: 创建"{{ ansible_eth0.ipv4.address }}"
    file:
      path: /tmp/{{ ansible_eth0.ipv4.address }}
      state: directory
  - name: 创建"{{ ansible_memtotal_mb }}"
    file:
      path: /tmp/{{ ansible_memtotal_mb }}
      state: directory
  - name: 创建"{{ ansible_hostname }}"
    file:
      path: /tmp/{{ ansible_hostname }}
      state: directory

#关闭facts缓存
gather_facts: no

3.Ansible条件语句

官方示例:
- hosts: web02
  tasks:
  - name: 安装apache(centos:httpd、ubuntu:apache2)
    yum:
      name: apache2
      state: present
    when: ansible_os_family == 'Debain'
  - name: 安装apache(centos:httpd、ubuntu:apache2)
    yum:
      name: httpd
      state: present
    when: ansible_os_family == 'RedHat'

#语句解析
==:等于
!=:不等于
>:大于
>=:大于等于
<:小于
<=:小于等于
is match:匹配 使用通配符
is not match: 取反

- hosts: all
  tasks:
  - name: 安装apache(centos:httpd、ubuntu:apache2)
    yum:
      name: apache2
      state: present
    when: ansible_os_family is not match 'web*'

4.使用小括号进行分组运算

- hosts: all
  tasks:
  - name: 安装apache(centos:httpd、ubuntu:apache2)
    yum:
      name: apache2
      state: present
    when: ansible_os_family is not match 'web*'
    when: (ansible_distribution =="CentOS" and ansible_distribution_major_version == "6") or (ansible_distribution == "Debian" and ansible_distribution_major_version == "7")

      when:
        - ansible_distribution == "CentOS"
        - ansible_distribution_major_version == "6"
#and模式要同时满足when的两个条件(没满足则跳过安装)  
- hosts: all
  tasks:
  - name: 安装apache(centos:httpd、ubuntu:apache2)
    yum:
      name: httpd
      state: present
    when:
      - ansible_hostname == 'web01'
      - ansible_hostname == 'db01'

#and模式要同时满足when的两个条件(没满足则跳过安装) 
- hosts: all
  tasks:
  - name: 安装apache(centos:httpd、ubuntu:apache2)
    yum:
      name: httpd
      state: present
    when: ansible_hostname == 'web01' and ansible_hostname == 'db01'

#在web01和db01上安装httpd     
- hosts: all
  tasks:
  - name: 安装apache(centos:httpd、ubuntu:apache2)
    yum:
      name: httpd
      state: present
    when: (ansible_hostname == 'web01') or (ansible_hostname == 'db01')   

5.条件运算

#操作系统是RedHat系统并且系统大于等于6才会安装httpd
- hosts: all
  tasks:
  - name: 安装apache(centos:httpd、ubuntu:apache2)
    yum:
      name: httpd
      state: present
    when: ansible_os_family == "RedHat" and ansible_distribution_major_version |int >= 6

6.Ansible流程控制循环

with_item(注意:模块中,只有一个动作是变量时,使用列表循环)
##列表循环
#创建hg、hhg、csg这三个用户
- hosts: all
  tasks:
  - name: 创建用户
    user:
      name: "{{ item }}"
      state: present
    with_items:
      - hg
      - hhg
      - csg
    when: ansible_hostname is match 'db01'
#停止web02上的nginx php mysql服务
- hosts: web02
  tasks:
  - name: 停止nginx php mysql
    service:
      name: "{{ item }}"
      state: stopped
      enabled: true
    with_items:
      - nginx
      - php-fpm
      - mariadb 

注意:模块中,有一个以上动作是变量时,使用字典循环

##字典循环   
- hosts: all
  tasks:
  - name: 推送nginx配置文件
    copy:
      src: "{{ item.src }}"
      dest: "{{ item.dest }}"
    when: ansible_hostname == 'web01'
    with_items:
      - {'src':'/root/wordpress/blog.conf','dest':'/etc/nginx/conf.d'}
      - {'src':'/root/wordpress/nginx.conf','dest':'/etc/nginx/'}
    notify: restart nginx
  handlers:
  - name: restart nginx
    service:
      name: nginx
      state: reloaded

7.Ansible的触发器handlers

handler用来执行某些条件下的任务,比如当配置文件发生变化的时候,通过notify触发handler去重启服务。
在saltstack中也有类似的触发器,写法相对Ansible简单,只需要watch,配置文件即可。
#推送nginx配置文件
- hosts: web_group
  tasks:
  - name: 推送nginx配置文件
    copy:
      src: '{{  item.src  }}'
      dest: '{{  item.dest  }}'
    with_items:
      - { 'src': "/root/wordpress/web/nginx.conf", 'dest': "/etc/nginx/" }
      - { 'src': "/root/wordpress/web/wp.conf", 'dest': "/etc/nginx/conf.d/" }
      - { 'src': "/root/wordpress/web/www.conf", 'dest': "/etc/php-fpm.d/" }
    when: ansible_hostname is match 'web*'
    notify: restart nginx

  handlers:
  - name: restart nginx
    service:
      name: nginx
      state: reloaded
#使用handlers触发器,推送配置文件成功及重启服务生效配置文件
注意:
1.无论多少个task通知了相同的handlers,handlers仅 会在所有tasks结束后运行一次。
2.Handlers只有在其所在的任务被执行时,才会被运行;如果一个任务中定义了notify调用Handlers,但是由于条件判断等原因,该任务未被执行,那么Handlers 同样不会被执行。
3.Handlers只会在每一个play的末尾运行一次;如果想 在一个playbook中间运行Handlers,则需要使用meta 模块来实现。例如: -meta: flush_handlers。
4.如果一个play在运行到调用Handlers的语句之前失败 了,那么这个Handlers将不会被执行。我们可以使用meta模块的–force-handlers选项来强制执行 Handlers,即使Handlers所在的play中途运行失败也能执行。
5.不能使用handlers替代tasks

8.Ansible任务标签tags

默认情况下,Ansible在执行一个playbook时,会执行playbook中定义的所有任务,Ansible的标签(tag)功能可以给单独任务甚至整个playbook打上标签,然后利用这些标签来指定要运行playbook中的个别任务,或不执行指定的任务。
#一个任务打一个标签
[root@m01 m01]# cat test.yml 
- hosts: all
  tasks:
  - name: 创建"{{ user }}"组
    group:
      name: "{{ user }}"
      gid: "{{ id }}"
      state: present
    tags: create_group

[root@m01 m01]# ansible-playbook test.yml -t ‘create_group’

#一个任务打多个标签
[root@m01 m01]# cat test1.yml 
- hosts: all
  tasks:
  - name: 创建"{{ user }}"组
    group:
      name: "{{ user }}"
      gid: "{{ id }}"
      state: present
    tags:
      - create_group
      - nginx_group
      - nfs_group
[root@m01 m01]# ansible-playbook test1.yml -t ‘create_group’
[root@m01 m01]# ansible-playbook test1.yml -t ‘nginx_group’
[root@m01 m01]# ansible-playbook test1.yml -t ‘nfs_group’

#多个任务打一个标签
[root@m01 m01]# cat test2.yml 
- hosts: all
  tasks:
  - name: 创建"{{ user }}"组
    group:
      name: "{{ user }}"
      gid: "{{ id }}"
      state: present
    tags: nginx_group

  - name: 创建"{{ user }}"用户
    user:
      name: "{{ user }}"
      uid: "{{ id }}"
      group: "{{ id }}"
      shell: /sbin/nologin
      create_home: false
      state: present
    tags: nginx_group
[root@m01 m01]# ansible-playbook test.yml -t ‘create_group’

-t 执行当前标签
--

9.Ansible playbook的复用

在之前写playbook的过程中,我们发现,写多个 playbook没有办法,一键执行,这样我们还要单个 playbook挨个去执行,很鸡肋。所以在playbook中有一 个功能,叫做include用来动态调用task任务列表。

image

只调用task:include_tasks
调用整个task文件:include (新版本: import_playbook)
在saltstack中,叫做top file入口文件。
[root@m01 new]# tree nginx
nginx
├── config_nginx.yaml
├── install_nginx.yaml
└── start_nginx.yaml

[root@m01 new]# cat nginx/install_nginx.yaml
- name: 安装nginx
    yum:
      name: nginx
      state: present

[root@m01 new]# catnginx/config_nginx.yaml
- name: 配置nginx
    copy:
      src:/opt/ansible/nginx_php/nginx.conf
      dest: /etc/nginx/nginx.conf
    notify: Restart Nginx

[root@m01 new]# cat nginx/start_nginx.yaml
- name: 启动nginx
    service:
      name: nginx
      state: started
      enabled: true

[root@m01 new]# cat nginx/nginx.yaml
- hosts: all
  tasks:
    - include_tasks: nginx/config_nginx.yaml
    when: ansible_hostname == 'web01'
    - include_tasks: nginx/start_nginx.yaml
    when: ansible_hostname == 'web01'
    - include_tasks: nfs-server/install_nfs.yaml
    when: ansible_hostname == 'nfs'

10.playbook忽略错误

- hosts: all
  tasks:
    - name: 安装nginx
      shell: 'rpm -ivh /opt/nginx_php/*.rpm'
    when: ansible_hostname is match 'web*'
    ignore_errors: true
    - name: chuangjianmulu
      file:
        path: /opt/123
        state: directory

11.playbook错误处理,抑制变更

[root@m01 ansible]# vim db.yaml
- hosts: web01
  tasks:
  - name: 查看磁盘
    shell: 'df -h'
    changed_when: false