ansible 三、playbook(基础)

大番茄 2020年02月13日 1,558次浏览

Playbook 可以用来管理远程主机的配置与部署, 方便对远程主机执行一些策略和步骤。

https://docs.ansible.com/ansible/latest/user_guide/playbooks.html

一、基础

Playbook 使用YAML语法,每个Playbook文件由一个或多个plays组成。每个play就是对一组远程主机的操作,多组远程主机就是多个play。

1、基本语法

- hosts: webserver
  remote_user: root
  gather_facts: False
  tasks:
  - name: install nginx
    yum:
      name: nginx
      state: present
  - name: install php
    yum: name=nginx state=present

- hosts: dbserver
  gather_facts: False
  tasks:
  - name: install mariadb
    yum:
      name: mariadb
      state: present

hosts: 指定主机或者主机组
remote_user: 连接远程主机的用户, 默认就是root, ansible 配置文件定义的。 在测试的时候,发现换用户不管用,还不如直接修改ansible_user变量来的直接。
gather_facts: 是否获取远程主机的fact信息,如果不需要使用fact变量,可以关闭,节省执行时间。默认是开启的,可以用False或no关闭。
tasks: 任务列表,下面定义任务。

  • name: 任务名称,在执行的时候会显示,所以最好是加上。
  • yum: 这个位置是模块名称,后面是模块的定义参数。有两种使用方式, 意思都一样, 推荐使用换行那种。
yum:  name=nginx  state=present
yum:
  name: mariadb
  state: present

2、提权

- hosts: dbserver
  remote_user: op
  gather_facts: False
  vars:
    ansible_user: op
  tasks:
  - name: print ansible_user
    debug: msg={{ ansible_user }}

  - name: install mariadb
    yum:
      name: mariadb-server
      state: present

上面添加ansible_user变量是因为remote_user切换不动用户,使用变量硬性修改。

[root@lvs playbook]# ansible-playbook test.yml -u op

PLAY [dbserver] *********************************************************************************************************************

TASK [print ansible_user] ***********************************************************************************************************
ok: [172.100.102.90] => {
    "msg": "op"
}

TASK [install mariadb] **************************************************************************************************************
fatal: [172.100.102.90]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "changes": {"installed": ["mariadb-server"]}, "msg": "You need to be root to perform this command.\n", "rc": 1, "results": ["Loaded plugins: fastestmirror\n"]}

PLAY RECAP **************************************************************************************************************************
172.100.102.90             : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

提示普通用户不能yum安装软件。 有的公司不会直接用root登录,都是普通用户连接,然后sudo执行管理命令。

- hosts: dbserver
  remote_user: op
  gather_facts: False
  become: yes
  become_method: sudo
  become_user: root
  vars:
    ansible_user: op
  tasks:
  - name: print ansible_user
    debug: msg={{ ansible_user }}

  - name: install mariadb
    yum:
      name: mariadb-server
      state: present

几个提权关键字跟ansible命令是一样的。
become: 是否提权
become_method: 提权方法,比如:sudo, su。 默认sudo。
become_user: 提权到哪个用户,一般都是root,默认也是root。
上面的become_methodbecome_user都可以省略。

执行的时候如果sudo需要输入密码,ansible-playbook 命令需要添加参数, --ask-become-pass-K, 交互式输入密码。

[root@lvs playbook]# ansible-playbook test.yml  -K
BECOME password:

PLAY [dbserver] *********************************************************************************************************************

TASK [print ansible_user] ***********************************************************************************************************
ok: [172.100.102.90] => {
    "msg": "op"
}

TASK [install mariadb] **************************************************************************************************************
changed: [172.100.102.90]

PLAY RECAP **************************************************************************************************************************
172.100.102.90             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

提权也可以只添加在某个任务里。

  - name: install mariadb
    become: yes
    become_method: sudo
    become_user: root
    yum:
      name: mariadb-server
      state: present

3、主机执行顺序

在2.4版以后添加的
可以控制主机的执行顺序, 默认是inventory中主机列表的顺序。
关键字: order
有以下参数:
inventory: 默认值,以inventory文件里的主机顺序。

reverse_inventory: inventory文件里的相反顺序。

sorted: 按主机名称的字母顺序排序

reverse_sorted: 按主机名称的字母顺序反向排序

shuffle: 随机排序。

例子:默认情况

- hosts: webserver
  remote_user: root
  gather_facts: False
  tasks:
  - name: print hello
    shell: echo 'hello'

相当于

- hosts: webserver
  remote_user: root
  order: inventory
  gather_facts: False
  tasks:
  - name: print hello
    shell: echo 'hello'

为了方便测试,需要修改并发。下面修改并发为1,-f参数指定。

ansible-playbook test.yml -f 1

执行顺序:

TASK [print hello] ******************************************************************************************************************
changed: [172.100.102.90]
changed: [172.100.102.92]
changed: [172.100.102.93]

改为reverse_inventory。

- hosts: webserver
  remote_user: root
  order: reverse_inventory
  gather_facts: False
  tasks:
  - name: print hello
    shell: echo 'hello'
[root@lvs playbook]# ansible-playbook test.yml -f 1

PLAY [webserver] ********************************************************************************************************************

TASK [print hello] ******************************************************************************************************************
changed: [172.100.102.93]
changed: [172.100.102.92]
changed: [172.100.102.90]

4、可以换行

行太长了,可以换行,换行以后保持缩进或更多缩进。

  tasks:
  - name: Copy ansible inventory file to client
    copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts
          owner=root group=root mode=0644

5、忽略错误

多个任务,在前面的任务发生错误以后, 后面的任务就不在执行了。
如:

  tasks:
  - name: install httpd
    yum: name=httpd state=present

  - name: print hello
    shell: echo 'hello'
TASK [install httpd] ****************************************************************************************************************
fatal: [172.100.102.90]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "changes": {"installed": ["httpd"]}, "msg": "You need to be root to perform this command.\n", "rc": 1, "results": ["Loaded plugins: fastestmirror\n"]}
fatal: [172.100.102.93]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "changes": {"installed": ["httpd"]}, "msg": "You need to be root to perform this command.\n", "rc": 1, "results": ["Loaded plugins: fastestmirror\n"]}
fatal: [172.100.102.92]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "changes": {"installed": ["httpd"]}, "msg": "You need to be root to perform this command.\n", "rc": 1, "results": ["Loaded plugins: fastestmirror\n"]}

如果在install httpd那里安装失败, 下面的print hello就不会执行了。可以添加忽略错误的关键字ignore_errors

  tasks:
  - name: install httpd
    yum: name=httpd state=present
    ignore_errors: True

  - name: print hello
    shell: echo 'hello'
TASK [install httpd] ****************************************************************************************************************
fatal: [172.100.102.93]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "changes": {"installed": ["httpd"]}, "msg": "You need to be root to perform this command.\n", "rc": 1, "results": ["Loaded plugins: fastestmirror\n"]}
...ignoring
fatal: [172.100.102.92]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "changes": {"installed": ["httpd"]}, "msg": "You need to be root to perform this command.\n", "rc": 1, "results": ["Loaded plugins: fastestmirror\n"]}
...ignoring
fatal: [172.100.102.90]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "changes": {"installed": ["httpd"]}, "msg": "You need to be root to perform this command.\n", "rc": 1, "results": ["Loaded plugins: fastestmirror\n"]}
...ignoring

TASK [print hello] ******************************************************************************************************************
changed: [172.100.102.93]
changed: [172.100.102.92]
changed: [172.100.102.90]

注意, 这个忽略错误,只是忽略返回码不为0的错误,也就是命令执行错误。 而连接或登录远程主机失败的错误无法忽略。

6、任务控制

--tags, --skip-tags
只执行某个或某几个任务,而不是全部执行。

- hosts: webserver
  gather_facts: False
  tasks:
  - name: install httpd
    yum: name=httpd state=present
    tags: install_nginx

  - name: push nginx conf
    copy: src=/etc/nginx/nginx.conf
          dest=/etc/nginx/nginx.conf
    tags: config

  - name: restart nginx
    service: name=nginx state=restarted
    tags: restart_nginx

比如只执行install_nginx与config。

ansible-playbook --tags "install_nginx, config" test.yml

跳过restart_nginx。

ansible-playbook --skip_tags "restart_nginx" test.yml

--start-at-task
指定从某个任务开始,而不是从头开始执行。这个跟tags没关系。
比如从push nginx conf开始:

ansible-playbook --start-at-task "push nginx conf" test.yml

6、语法检查

--syntax-check

ansible-playbook --syntax-check  test.yml

二、handlers

添加只有在某些任务执行发生变化的时候才会触发执行的任务。

比如: 第一次发布配置文件,需要触发重载配置的操作。然后后来又再一次发布,但是配置都没有做过修改,ansible对比文件发现都一样,实际也不会推送配置, 这样也就没有必要重载配置。

1、基本格式

- hosts: webserver
  gather_facts: False
  tasks:
  - name: push nginx conf
    copy: src=/etc/nginx/nginx.conf
          dest=/etc/nginx/nginx.conf
    notify: reload nginx

  handlers:
  - name: reload nginx
    service: name=nginx state=reloaded

handlers 也是配置任务列表, 与 tasks 没有什么不同。
notify 后面的就是 handlers 里的任务名称。
执行结果:

[root@lvs playbook]# ansible-playbook test1.yml

PLAY [webserver] ********************************************************************************************************************

TASK [push nginx conf] **************************************************************************************************************
changed: [172.100.102.93]
changed: [172.100.102.92]

RUNNING HANDLER [restart nginx] *****************************************************************************************************
changed: [172.100.102.93]
changed: [172.100.102.92]

PLAY RECAP **************************************************************************************************************************
172.100.102.92             : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
172.100.102.93             : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

可以看到触发了HANDLER。
再执行一下:

[root@lvs playbook]# ansible-playbook test1.yml

PLAY [webserver] ********************************************************************************************************************

TASK [push nginx conf] **************************************************************************************************************
ok: [172.100.102.92]
ok: [172.100.102.93]

PLAY RECAP **************************************************************************************************************************
172.100.102.92             : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
172.100.102.93             : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

2、notify 触发多个

notify 后面可以写成列表,用来触发多个handler的任务。
如:

- hosts: webserver
  gather_facts: False
  tasks:
  - name: push nginx conf
    copy: src=/etc/nginx/nginx.conf
          dest=/etc/nginx/nginx.conf
    notify:
      - reload nginx
      - echo hello

  handlers:
  - name: reload nginx
    service: name=nginx state=reloaded
  - name: echo hello
    debug: msg='hello'

这里的nginx.conf配置文件又修改了一下,不然也不会触发。

TASK [push nginx conf] **************************************************************************************************************
changed: [172.100.102.93]
changed: [172.100.102.92]

RUNNING HANDLER [reload nginx] ******************************************************************************************************
changed: [172.100.102.93]
changed: [172.100.102.92]

RUNNING HANDLER [echo hello] ********************************************************************************************************
ok: [172.100.102.93] => {
    "msg": "hello"
}
ok: [172.100.102.92] => {
    "msg": "hello"
}

3、同一个play中相同handler任务多次触发也只会执行一次

tasks中的任务多次触发同一个handler任务,对应的handler任务也只会执行一次。
handler的执行会在tasks执行完以后再执行。

4、listen 侦听任务通知

虽然所notify可以通知多个handler。 但是量很大的情况下,可能会遇到多个任务都需要触发一大堆handler。
这种情况就可以使用listen,应该更加的灵活。
handlers的任务添加listen, 然后notify后面是listen的字符串,不再是handler任务的名称。

- hosts: webserver
  gather_facts: False
  tasks:
  - name: push nginx conf
    copy: src=/etc/nginx/nginx.conf
          dest=/etc/nginx/nginx.conf
    notify: restart web server

  handlers:
  - name: restart nginx
    service: name=nginx state=restarted
    listen: restart web server

  - name: restart php-fpm
    service: name=php-fpm state=restarted
    listen: restart web server

  - name: test
    shell: echo 'hello world'
    listen: restart web server
TASK [push nginx conf] **************************************************************************************************************
changed: [172.100.102.93]
changed: [172.100.102.92]

RUNNING HANDLER [restart nginx] *****************************************************************************************************
changed: [172.100.102.93]
changed: [172.100.102.92]

RUNNING HANDLER [restart php-fpm] ***************************************************************************************************
changed: [172.100.102.93]
changed: [172.100.102.92]

RUNNING HANDLER [test] **************************************************************************************************************
changed: [172.100.102.93]
changed: [172.100.102.92]