Playbook - QLGQ/learning-python GitHub Wiki

Introduction

playbook是一个不同于使用ansible命令执行方式的模式,其功能更强大灵活。简单来说,playbook是一个非常简单的配置管理和多主机部署系统,不同于任何已经存在的模式,可作为一个适合部署复杂应用程序的基础。playbook可以定制配置,可以按指定的操作步骤有序执行,支持同步及异步方式。playbook是通过YAML格式来进行描述定义的,可以实现多台主机应用的部署,定义在webservers及dbservers组上执行特定指令步骤。下面为读者介绍一个基本的playbook示例:

【/home/test/ansible/playbooks/nginx.yml】

---
- hosts: webservers
  vars:
    worker_processes: 4
    num_cpus: 4
    max_open_file: 65506
    root: /data
  remote_user: root
  tasks:
  - name: ensure nginx is at the latest version
    yum: pkg=nginx state=latest
  - name: write the nginx config file
    template: src=/home/test/ansible/nginx/nginx2.conf dest=/etc/nginx/nginx.conf
    notify:
    - restart nginx
  - name: ensure nginx is running
    service: name=nginx state=started
  handlers:
    - name: restart nginx
      service: name=nginx state=restarted

以上playbook定制了一个简单的Nginx软件包管理,内容包括安装、配置模板、状态管理等。

定义主机与用户

在playbook执行时,可以为主机或组定义变量,比如指定远程登录用户。以下为webservers组定义的相关变量,变量的作用域只限于webservers组下的主机。

- hosts: webservers
  vars:
    worker_processes: 4
    num_cpus: 4
    max_open_file: 65506
    root: /data
  remote_user: root

hosts参数的作用为定义操作的对象,可以是主机或组,同时通过vars参数定义了4个变量(配置模板用到),其中remote_user为指定远程操作的用户名,默认为root账号,支持sudo方式运行,通过添加sudo:yes即可。注意,remote_user参数在ansible1.4或更高版本才引入。

任务列表

所有定义的任务列表(tasks list),playbook将按定义的配置文件自上而下的顺序执行,定义的主机都将得到相同的任务,但执行的返回结果不一定保持一致,取决于主机的环境及程序包状态。建议每个任务事件都要定义一个name标签,好处是增强可读性,也便于观察结果输出时了解运行的位置,默认使用action(具体的执行动作)来替换name作为输出。下面是一个简单的任务定义示例:

tasks:
  - name: make sure nginx is running
    service: name=nginx state=running

功能是检测Nginx服务是否为运行状态,如没有则启动。其中name标签对下面的action(动作)进行描述;action(动作)部分可以是ansible的任意模块,本例为service模块,参数使用key=value的格式,如“name=httpd”,在定义任务时也可以引用变量,格式如下:

tasks:
  - name: create a virtual host file for {{ vhost }}
    template: src=somefile.j2 dest=/etc/httpd/conf.d/{{ vhost }}

在playbook可以通过template模块对本地配置模板文件进行渲染并同步到目标主机。以nginx配置文件为例,定义如下:

- name: write the nginx config file
  template: src=/home/test/ansible/nginx/nginx2.conf dest=/etc/nginx/nginx.conf
  notify:
  - restart nginx

其中,“src=/home/test/ansible/nginx/nginx2.conf”为管理端模板文件存放位置,“/etc/nginx/nginx.conf”为目标主机nginx配置文件位置。通过下面nginx模板文件可以让大家对模板的定义有个基本的概念。

【/home/test/ansible/nginx/nginx2.conf】

user                 nginx;
worker_processes    {{ worker_processes }};
{% if num_cpus == 2 %}
worker_cpu_affinity 01 10;
{% elif num_cpus == 4 %}
worker_cpu_affinity 1000 0100 0010 0001;
{% elif num_cpus >= 8 %}
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
{% else %}
worker_cpu_affinity 1000 0100 0010 0001;
{% endif %}
worker_rlimit_nofile {{ max_open_file }};
... ...

ansible会根据定义好的模板渲染成真实的配置文件,模板使用YAML语法,最终生成的nginx.conf配置如下:

user nginx;
worker_processes 4;
worker_cpu_affinity 1000 0100 0010 0001;
worker_rlimit_nofile 65506;
... ...

当目标主机配置文件发生变化后,通知处理程序(Handlers)来触发后续的动作,比如重启nginx服务。Handlers中定义的处理程序在没有通知触发时是不会执行的,触发后也只会运行一次。触发是通过Handlers定义的name标签来识别的,比如下面notify中的“restart nginx”与handlers中的“name: restart nginx”保持一致。

notify:
    - restart nginx
handlers:
    - name: restart nginx
      service: name=nginx state=restarted

执行playbook

执行playbook,可以通过ansible-playbook命令实现,格式:ansible-playbook playbookfile(.yml)[参数],如启用10个并行进程数执行playbook:ansible-playbook /home/test/ansible/playbooks/nginx.yml -f 10。

其他常用参数说明:

  • -u REMOTE_USER:手工指定远程执行playbook的系统用户;
  • --syntax-check:检查playbook的语法;
  • --list-hosts playbooks:匹配到的主机列表;
  • -T TIMEOUT:定义playbook执行超时时间;
  • --step:以单任务分步骤运行,方便做每一步的确认工作。

更多参数说明运行ansible-playbook -help来获得。

playbook角色与包含声明

当我们写一个非常大的playbook时,想要复用些功能显得有些吃力,还好ansible支持写playbook时拆分成多个文件,通过包含(include)的形式进行引用,我们可以根据多种维度进行“封装”,比如定义变量、任务、处理程序等。角色建立在包含文件之上,抽象后更加清晰、可复用。

包含文件,鼓励复用

当多个playbook涉及复用的任务列表时,可以将复用的内容剥离出,写到独立的文件当中,最后在需要的地方include进来即可,示例如下:

[tasks/foo.yml]
---
# possibly saved as tasks/foo.yml
- name: placeholder foo
  command: /bin/foo
- name: placeholder bar
  command: /bin/bar

然后就可以在使用的playbook中include进来,如:

tasks:
  - include: tasks/foo.yml

当然,也可以将变量传递到包含文件当中,这称为“参数包含”。
如在部署多个WordPress的情况下,可以根据不同用户单独部署WordPress的任务,且引用单个wordpress.yml文件,可以这样写:

tasks:
  - include: wordpress.yml user=timmy
  - include: wordpress.yml user=alice
  - include: wordpress.yml user=bob

注意,1.4或更高版本可支持以Python的字典、列表的传递参数形式,如:

tasks:
  - { include: wordpress.yml, user: timmy, ssh_keys: [ 'keys/one.txt', 'keys/two.txt' ] }

使用这两种方法都进行变量传递,然后在包含文件中通过使用{{ user }}进行变量引用。

角色

角色是ansible定制好的一种标准规范,以不同级别目录层次及文件对角色、变量、任务、处理程序等进行拆分,为后续功能扩展、可维护性打下基础。一个典型的示例如下:

site.yml
webservers.yml
fooservers.yml
roles/
    common/
        files/
        templates/
        tasks/
        handlers/
        vars/
        meta/
    webservers/
        files/
        templates/
        tasks/
        handlers/
        vars/
        meta/

在playbook是这样引用的:

[site.yml]
---
- hosts: webservers
  roles: 
    - common
    - webservers

角色定制以下规范,其中x为角色名。

  • 如roles/x/tasks/main.yml文件存在,其中列出的任务将被添加到执行队列;
  • 如roles/x/handlers/main.yml文件存在,其中所列的处理程序将被添加到执行队列;
  • 如roles/x/vars/main.yml文件存在,其中所列的变量将被添加到执行队列;
  • 如roles/x/meta/main.yml文件存在,所列任何作用的依赖关系将被添加到角色的列表(1.3及更高版本);
  • 任何副本任务可以引用roles/x/files/无需写路径,默认相对路径或绝对路径;
  • 任何脚本任务可以引用roles/x/files/无需写路径,默认相对路径或绝对路径;
  • 任何模板任务可以引用文件中的roles/x/templates/无需写路径,默认相对路径或绝对路径。

角色示例

playbook目录结构

playbook目录包括变量定义目录group_vars、主机组定义文件hosts、全局配置文件site.yml、角色功能目录。

【/home/test/ansible/playbooks/nginx】

定义主机组

以下定义了一个业务组webservers,成员为两台主机。

【nginx/hosts】

[webservers]
192.168.1.21
192.168.1.22

非必选配置,默认将引用/etc/ansible/hosts的参数,角色中自定义组与主机文件将通过“-i file”命令行参数调用,如ansible-playbook -i hosts来调用。

定义主机或组变量

group_vars为定义组变量目录,目录当中的文件名要与组名保持一致,组变量文件定义的变量作用域只受限于改组,all代表所有主机。

【nginx/group_vars/all】

---
# Variables listed here are applicable to all host groups
ntpserver: ntp.sjtu.edu.cn

【nginx/group_vars/webservers】

---
worker_processes: 4
num_cpus: 4
max_open_file: 65536
root: /data

全局配置文件site.yml

下面的全局配置文件引用了两个角色块,角色的应用范围及实现功能都不一样:

【nginx/site.yml】
---
- name: apply common configuration to all nodes
  hosts: all
  roles:
    - common

- name: configure and deploy the webservers and application code
  hosts: webservers
  roles:
    - web

全局配置文件site.yml引用了两个角色,一个为公共类的common,另一个为web类,分别对应nginx/common、nginx/web目录。以此类推,可以引用更多的角色,如db、nosql、hadoop等,前提是我们先要进行定义,通常情况下一个角色对应着一个特定功能服务。通过hosts参数来绑定角色对应的主机或组。

角色common的定义

角色common定义了handlers、tasks、templates、vars4个功能类,分别存放处理程序、任务列表、模板、变量的配置文件main.yml,需要注意的是,vars/main.yml中定义的变量优先级高于/nginx/group_vars/all,可以从ansible-playbook的执行结果中得到验证。各功能块配置文件定义如下:

【handlers/main.yml】

- name: restart ntp
  service: name=ntpd state=restarted

【tasks/main.yml】

- name: Install ntp
  yum: name=ntp state=present

- name: Configure ntp file
  template: src=ntp.conf.j2 dest=/etc/ntp.conf
  notify: restart ntp

- name: Start the ntp service
  service: name=ntpd state=started enabled=true

- name: test to see if selinux is running
  command: getenforce
  register: sestatus
  changed_when: false

其中template: src=ntp.conf.j2引用模板时无需写路径,默认在上级的tenplates目录中查找。

【templates/ntp.conf.j2】
driftfile /var/lib/ntp/drift
restrict 127.0.0.1
restrict -6 ::1

server {{ ntpserver }}

includefile /etc/ntp/crypto/pw
keys /etc/ntp/keys
此处{{ ntpserver }}将引用vars/main.yml定义的ntpserver变量。

【vars/main.yml】

---
# Variables listed here are applicable to all host groups
ntpserver: 210.72.145.44

运行角色

运行命令:cd /home/test/ansible/playbooks/nginx ansible-playbook -i hosts site.yml -f 10