第五章·自动化运维工具-Ansible流程控制

playbook条件语句

不管是shell还是各大编程语言中,流程控制,条件判断这些都是必不可少的,在我们使用Ansible的过程中,条件判断的使用频率极其高。

例如:

  1. 我们使用不同的系统的时候,可以通过判断系统来对软件包进行安装。
  2. 在nfs和rsync安装过程中,客户端服务器不需要推送配置文件,之前我们都是写多个play,会影响效率。
  3. 我们在源码安装nginx的时候,执行第二遍就无法执行了,此时我们就可以进行判断是否安装过。

根据不同的操作系统安装apache

官方示例:

tasks:
  - name: "shut down Debian flavored systems"
    command: /sbin/shutdown -t now
    when: ansible_facts['os_family'] == "Debian"
    # note that all variables can be used directly in conditionals without double curly braces
- hosts: web_group
  tasks:
    - name: Install CentOS Httpd
      yum:
        name: httpd
        state: present
    #官方
      when: ansible_facts['os_family'] == "CentOS"
    #非官方
      when: ansible_distribution == "CentOS"

    - name: Install Ubuntu Httpd
      yum:
        name: apache2
        state: present
      when: ansible_facts['os_family'] == "Ubuntu"

还可以使用括号对条件进行分组

tasks:
  - name: "shut down CentOS 6 and Debian 7 systems"
    command: /sbin/shutdown -t now
    when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
          (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")

也可以指定多条件为列表

tasks:
  - name: "shut down CentOS 6 systems"
    command: /sbin/shutdown -t now
    when:
      - ansible_facts['distribution'] == "CentOS"
      - ansible_facts['distribution_major_version'] == "6"

条件运算

tasks:
  - shell: echo "only on Red Hat 6, derivatives, and later"
    when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release']|int >= 6

在nfs和rsync安装过程中,客户端服务器不需要推送配置文件,之前我们都是写多个play,会影响效率。

- hosts: rsync_nfs
  tasks:
    - name: 安装rsync和nfs服务
      yum:
        name:
        - rsync
        - nfs-utils
      state: present

    - name: 推送rsync配置文件
      template:
      src: /root/wordpress_ansible/rsync/rsyncd.conf
      dest: /etc
      when: ansible_hostname == 'backup'

## 多条件判断
- hosts: rsync_nfs
  tasks:
    - name: 安装rsync和nfs服务
      yum:
        name:
        - rsync
        - nfs-utils
      state: present
      when: ansible_hostname == 'backup' or ansible_hostname == 'nfs'

    - name: 推送rsync配置文件
      template:
      src: /root/wordpress_ansible/rsync/rsyncd.conf
      dest: /etc
      when: ansible_hostname == 'backup'

我们在源码安装nginx的时候,执行第二遍就无法执行了,此时我们就可以进行判断是否安装过。

- hosts: web_group
  tasks:
  - name: 查看nginx目录
    shell: "ls -l /etc/nginx"
    register: xxx
  - name: 判断是否安装nginx
    shell: 'cd /opt && rpm -Uvh *.rpm'
    when: xxx.rc != 0

## 模糊匹配
- hosts: all
  tasks:
  - name: 推送nginx虚拟主机配置文件
    copy:
      src: /root/wordpress_ansible/nginx_php/blog.zls.com.conf
      dest: /etc/nginx/conf.d
# when: ansible_hostname == 'web01' or ansible_hostname == 'web02'
    when: ansible_hostname is match 'web*'

  - name: 推送php配置文件
    copy:
      src: /root/wordpress_ansible/nginx_php/www.conf
      dest: /etc/php-fpm.d

playbook循环语句

- hosts: web_group
  tasks:
    - name: start service
      systemd:
        name: "{{ item }}"
        state: started
      with_items:
        - httpd
        - php-fpm
        - mariadb

定义变量循环

- name: ensure a list of packages installed
  yum:
    name: "{{ packages }}"
  vars:
    packages:
    - httpd
    - httpd-tools
- hosts: web_group
  tasks:
    - name: ensure a list of packages installed
      yum: name= "{{ item }}" state=present
      with_items:
        - httpd
        - httpd-tools

字典循环

1.创建用户

[root@m01 ~]# cat loop.yml
- hosts: web_group
  tasks:
    - name: Add Users
      user:
        name: "{{ item.name }}"
        groups: "{{ item.groups }}"
        state: present
      with_items:
        - { name: 'lw', groups: 'linux' }
        - { name: 'zls', groups: 'python' }

2.拷贝文件

- hosts: web_group
  tasks:
    - name: copy conf and code
      copy:
        src: "{{ item.src }}"
        dest: "{{ item.dest }}"
        mode: "{{ item.mode }}"
      with_items:
        - { src: "./httpd.conf", dest: "/etc/httpd/conf/", mode: "0644" }
        - { src: "./upload_file.php", dest: "/var/www/html/", mode: "0600" }

playbook handiers

什么是handlers

handler用来执行某些条件下的任务,比如当配置文件发生变化的时候,通过notify触发handler去重启服务。

在saltstack中也有类似的触发器,写法相对Ansible简单,只需要watch,配置文件即可。

案例

[root@m01 ~]# cat handler.yml 
- hosts: web_group
  vars:
    - http_port: 8080
  tasks:
    - name: Install Http Server
      yum:
        name: httpd
        state: present

    - name: config httpd server
      template:
        src: ./httpd.j2
        dest: /etc/httpd/conf
      notify: 
        - Restart Httpd Server
        - Restart PHP Server

    - name: start httpd server
      service:
        name:httpd
        state: started
        enabled: yes

  handlers:
    - name: Restart Httpd Server
      systemd:
        name: httpd
        state: restarted 

    - name: Restart PHP Server
      systemd:
        name: php-fpm
        state: restarted

注意:

  1. 无论多少个task通知了相同的handles,handles仅会在所有的tasks结束后运行一次
  2. handlers只有在其所在的任务被执行时,才会被运行,如果一个任务定义了notify调用handlers,但是由于条件判断等原因,该任务未被执行,namehandlers同样不会被执行
  3. handlers只会在每一个play的末尾运行一次,如果想在一个playbook中间运行handlers,则需要使用meta模块来实现。例如: -meta: flush_handlers。
  4. 如果一个play在运行到调用handlers的语句之前失败了,那么这个handlers将不会被执行。我们可以使用meta模块的--force-handlers选项来强制执行Handlers,即使Handlers所在的play中途运行失败也能执行
  5. 不能使用handlers替代tasks

playbook任务标签

默认情况下,Ansible在执行一个playbook时,会执行playbook中定义的所有任务,Ansible的标签(tag)功能可以给单独任务甚至整个playbook打上标签,然后利用这些标签来指定要运行playbook中的个别任务,或不执行指定的任务。

打标签的方式

  • 对一个task打一个标签
- name: 安装rsync
  yum:
    name: rsync
    state: present
  when: ansible_hostname != 'db01'
  tags: install_rsync
  • 对一个task打多个标签
- name: 推送rsync配置文件
  template:
    src: /root/wordpress_ansible/rsync/rsyncd.conf
    dest: /etc
  when: ansible_hostname == 'backup'
  notify: Rrestart rsync
  tags:
    - install_rsync
    - send_rsync_config
  • 对多个task打一个标签
- name: 安装rsync
  yum:
    name: rsync
    state: present
  when: ansible_hostname != 'db01'
  tags: install_rsync

- name: 推送rsync配置文件
  template:
    src: /root/wordpress_ansible/rsync/rsyncd.conf
    dest: /etc
  when: ansible_hostname == 'backup'
  tags: install_rsync

- name: 创建密码文件
  copy:
    content: "{{ rsync_user }}:123"
    dest: "{{ rsync_pass_path }}"
    mode: 0600
  when: ansible_hostname == 'backup'
  tags: install_rsync

- name: 创建{{ backup_dir }}目录
  file:
    path: /{{ backup_dir }}
    owner: "{{ user_group }}"
    group: "{{ user_group }}"
    state: directory
  when: ansible_hostname == 'backup'
  tags: install_rsync

- name: 启动rsync服务
  service:
    name: rsyncd
    state: started
    enabled: True
  when: ansible_hostname == 'backup'
  tags: install_rsync

打完标签如何使用

  • -t执行指定的tag标签任务
  • --skip-tags:执行-skip-tags之外的标签任务
ansible-playbook -i base/hosts lnmp_wp.yml -t 'install_rsync'

ansible-playbook -i base/hosts lnmp_wp.yml --skip-tags 'install_rsync'

playbook文件复用

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

只调用task:include_tasks

调用整个task文件:include (新版本:import_playbook)

在saltstack中,叫做top file入口文件

示例一

[root@m01 m01]# cat task.yml 
- hosts: web_group
  vars:
    - http_port: 8080

  tasks:
    - include_tasks: task_install.yml
    - include_tasks: task_configure.yml
    - include_tasks: task_start.yml

  handlers:
    - name: Restart Httpd Server
      systemd:
        name: httpd
        state: restarted

[root@m01 m01]# cat task_install.yml 
- name: Install Http Server
  yum:
    name: httpd
    state: present

[root@m01 m01]# cat task_configure.yml 
- name: configure httpd server
  template:
    src: ./httpd.j2
    dest: /etc/httpd/conf/httpd.conf
  notify: Restart Httpd Server

[root@m01 m01]# cat task_start.yml 
- name: start httpd server
  service:
    name: httpd
    state: started
    enabled: yes

示例二

- include: httpd.yml
- include: nfs.yml
- include: rsync.yml

示例三

- import_playbook: httpd.yml
- import_playbook: nfs.yml
- import_playbook: rsync.yml

playbook忽略错误

默认playbook会检测task执行的返回状态,如果遇到错误则会立即终止playbook的后续task执行,然而有些时候playbook即使执行错误了也要让其继续执行。

加入参数:ignore_errors:yes 忽略错误

[root@m01 ~]# cat ignore.yml
---
- hosts: web_group
  tasks:
    - name: Ignore False
      command: /bin/false
      ignore_errors: yes

    - name: touch new file
      file:
        path: /tmp/lw.txt
        state: touch

playbook错误处理

如上所述,当task执行失败时,playbook将不再继续执行,包括如果在task中设置了handler也不会被执行。 但是我们可以采取强制措施...

强制调用handler

[root@m01 ~]# cat handler.yml 
- hosts: web_group
  vars:
    - http_port: 8080
  force_handlers: yes
  tasks:

    - name: config httpd server
      template:
        src: ./httpd.j2
        dest: /etc/httpd/conf
      notify: 
        - Restart Httpd Server
        - Restart PHP Server

    - name: Install Http Server
      yum:
        name: htttpd
        state: present

    - name: start httpd server
      service:
        name:httpd
        state: started
        enabled: yes

  handlers:
    - name: Restart Httpd Server
      systemd:
        name: httpd
        state: restarted 

    - name: Restart PHP Server
      systemd:
        name: php-fpm
        state: restarted

山林不向四季起誓 荣枯随缘