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
。