Ansible-playbook高级用法

Ansible Playbook是基于Yaml语法的ansible命令文件,简单来说,可以理解为linux的普通命令和shell脚本的区别,通过playbook,可以实现更复杂的操作
Ansible-playbook高级用法
Photo by Alexandr Podvalny / Unsplash

On this page

Ansible Playbook是基于Yaml语法的ansible命令文件,简单来说,可以理解为linux的普通命令和shell脚本的区别,通过playbook,可以实现更复杂的操作。

下面是一个部署nfs远程挂载的ansible playbook

--- 
- name: setup nfs server
  hosts: nfs
  tasks:
    - name: 01 - install nfs-utils
      yum:
        name: nfs-utils
        state: installed
    - name: 02 - install rpcbind
      yum:
        name: rpcbind
        state: installed
    - name: 03 - start and enable nfs-utils
      systemd:
        name: nfs-utils
        state: started
        enabled: true
    - name: 04 - start and enable rpcbind
      systemd:
        name: rpcbind
        state: started
        enabled: true
    - name: 05 - setup nfs configuration
      shell:
        cmd: "echo '/data 172.16.1.0/24(rw,sync,all_squash)' >> /etc/exports"
    - name: 06 - change wordpress file user and group
      file:
        path: /data
        state: directory
        owner: nfsnobody
        group: nfsnobody
        recurse: true
    - name: 09 - start and enable nfs
      systemd:
        name: nfs
        state: started
        enabled: true

- name: mount wordpress files to web server
  hosts: web
  tasks: 
    - name: 01 - install rpcbind
      yum:
        name: rpcbind
        state: installed
    - name: 02 - install nfs-utils
      yum:
        name: nfs-utils
        state: installed
    - name: 03 - start and enable nfs-utils
      systemd:
        name: nfs-utils
        state: started
        enabled: true
    - name: 04 - create mount directory
      file:
        path: /www
        state: directory
    - name: 05 - restart rpcbind
      systemd:
        name: rpcbind
        state: restarted
        enabled: true
    - name: 06 - mount
      mount:
        src: 172.16.1.31:/wordpress
        path: /www
        fstype: nfs
        state: mounted

从这个playbook可以看出,部分内容是重复的,但是通过playbook的高级用法可以更简单的实现相同的功能。

Loop循环

  • 使用loop关键字定义循环变量
  • 使用item关键字提取loop每次循环出来的值

以nfs服务器中安装nfs-utils和rpcbind为例,使用loop可以让我们在一个模块中安装两个应用。

--- 
- name: install nfs-utils and rpcbind
  hosts: nfs
  tasks:
    - name: installation
      yum:
        name: "{{ item }}"
        state: installed
      loop:
        - nfs-utils
        - rpcbind

如此一来,我们就实现了通过循环安装多个软件。

但是,可以发现我们后面一样要通过systemd模块来运行并为这两个应用设置开机自启;目前loop循环是置于name: installation下面,没办法在其他的模块中使用,因此,引入下一个概念:vars

使用vars定义循环列表

通过vars关键字我们可以为循环配置一个变量名,让这个循环可以被同级的任何一个模块使用。

--- 
- name: setup nfs applications
  hosts: nfs
  vars:
    app:
      - nfs-utils
      - rpcbind
  tasks:
    - name: installation
      yum:
        name: "{{ item }}"
        state: installed
      loop: "{{ app }}"
    - name: start and set enabled
      systemd:
        name: "{{ item }}"
        state: started
        enabled: true
      loop: "{{ app }}"

循环处理字典数据

假如我要创建三个文件,他们的属主各不相同,应该如果实现呢?

--- 
- name: create files for three different users
  hosts: web
  tasks:
  - name: create files
    file:
      path: "{{ item.path }}"
      state: touch
      owner: "{{ item.owner }}"
    loop:
      - {path: '/opt/mike.txt', owner: mike}
      - {path: '/opt/sarah.txt', owner: sarah}
      - {path: '/opt/john.txt', owner: john}

当然,字典循环也可以被声明成一个变量,和之前的差不多,就不加赘述了。

Register将模块的输出结果注册为变量

Register可以将模块的执行结果注册为一个变量,并通过debug模块中的.stdout_lines输出到ansible管理终端。

例如,我想要知道一个文本文件中有多少行:

--- 
- name: how many lines for the text file
  hosts: 172.16.1.105
  tasks:
  - name: count lines
    shell:
      cmd: "cat /opt/testfile.txt | wc -l"
    register: lines

  - name: output the value
    debug:
      msg: "{{ lines.stdout_lines }}"

注册多个变量并和loop结合使用

在之前的基础上再查看一下目标主机名

--- 
- name: register and loop
  hosts: 172.16.1.105
  tasks:
  - name: count lines
    shell:
      cmd: "cat /opt/testfile.txt | wc -l"
    register: lines
  - name: show hostname
    shell:
      cmd: "cat /etc/hostname"
    register: hostname

  - name: output the value
    debug:
      msg: "{{ item }}"
    loop:
      - lines.stdout_lines
      - hostname.stdout_lines

When条件判断

when通常和register组合使用,以实现通过判断执行结果来指定后续操作。

以查看文件为例:

--- 
- name: how many lines for the text file
  hosts: 172.16.1.105
  tasks:
  - name: count lines
    shell:
      cmd: "cat /opt/testfile.txt"
    ignore_errors: true
    register: result

  - name: output the value
    debug:
      msg: "FATAL: File does not exists"
    when: result is failed

ignore_errors: 忽略报错继续向后执行

与ansible的内置变量相结合,还可以实现更多的有趣的功能;比如我想为web主机组下的所有主机创建一个文件,但其中一台主机不包括在内,这就可以用内置变量加条件判断的方法来实现:

--- 
- name: how many lines for the text file
  hosts: web
  tasks:
  - name: count lines
    file:
      path: /opt/file.txt
      state: touch
    when: inventory_hostname != "172.16.1.7"

inventory_hostname: 目标主机名 - 取决于你在hosts文件中是如何配置的(ip或者别名)

Notify和Handler

当调用某个任务确实执行了,且状态changed为true,notify就会执行指定的handler事件

还是以创建文件为例:

---
- hosts: web
    tasks:
    - name: create file
        file:
        path: /opt/testfile.txt
        state: touch
        mode: 600
        notify:
        - output message
    
    handlers:
    - name: output message
        debug:
        msg: "File created"

使用Tags为不同的task打标签,实现剧本的部分执行

- name: deploy nfs
    hosts: nfs
    tasks:
    - name: 01 - install nfs-utils
    yum: name=nfs-utils state=installed
    tags: 01_install_nfs_service

    - name: 02 - install rpcbind
    yum: name=rpcbind state=installed
    tags: 02_install_rpcbind_service

    - name: 03 - create group
    group: name=www gid=666
    tags: 03_add_group

    - name: 04 - create user
    user: name=www uid=666 group=www create_home=no shell=/sbin/nologin
    tags: 04_add_user

显示剧本的所有tag标签:

ansible-playbook --list-tags playbook.yml

使用-t参数根据tag执行部分剧本:

ansible-playbook -t tag名 playbook.yml

对一开始的部署nfs挂载剧本进行改造

--- 
- name: setup nfs server
  hosts: nfs
  vars:
    app:
      - nfs-utils
      - rpcbind
  tasks:
    - name: 01 - install apps
      yum:
        name: "{{ item }}"
        state: installed
      loop: "{{ app }}"
    - name: 02 - start and enable apps
      systemd:
        name: "{{ item }}"
        state: started
        enabled: true
      loop: "{{ app }}"
    - name: 03 - setup nfs configuration
      shell:
        cmd: "echo '/data 172.16.1.0/24(rw,sync,all_squash)' >> /etc/exports"
      notify:
        - start NFS

      handlers:
        - name: start NFS
          systemd:
            name: nfs
            state: started
            enabled: true
    - name: 04 - change wordpress file user and group
      file:
        path: /data
        state: directory
        owner: nfsnobody
        group: nfsnobody
        recurse: true

- name: mount directory to web server
  hosts: web
  vars:
    app:
      - nfs-utils
      - rpcbind  
  tasks: 
    - name: 01 - install apps
      yum:
        name: "{{ item }}"
        state: installed
      loop: "{{ app }}"
    - name: 02 - start and enable apps
      systemd:
        name: "{{ item }}"
        state: started
        enabled: true
      loop: "{{ app }}"
    - name: 03 - create mount directory
      file:
        path: /www
        state: directory  
    - name: 04 - mount
      mount:
        src: 172.16.1.31:/data
        path: /www
        fstype: nfs
        state: mounted