playbook 是 ansible 用于配置,部署,和管理被控节点的剧本。通过 playbook 的详细描述,执行其中的一系列 tasks ,可以让远端主机达到预期的状态。playbook 就像 Ansible 控制器给被控节点列出的的一系列 to-do-list ,而被控节点必须要完成。也可以这么理解,playbook 字面意思,即剧本,现实中由演员按照剧本表演,在 Ansible 中,这次由计算机进行表演,由计算机安装,部署应用,提供对外服务,以及组织计算机处理各种各样的事情。
二、Ansible playbook 使用场景执行一些简单的任务,使用 ad-hoc 命令可以方便的解决问题,但是有时一个设施过于复杂,需要大量的操作时候,执行的 ad-hoc 命令是不适合的,这时最好使用 playbook。就像执行 shell 命令与写 shell 脚本一样,也可以理解为批处理任务,不过 playbook 有自己的语法格式。使用 playbook 你可以方便的重用这些代码,可以移植到不同的机器上面,像函数一样,最大化的利用代码。在你使用 Ansible 的过程中,你也会发现,你所处理的大部分操作都是编写 playbook。可以把常见的应用都编写成 playbook,之后管理服务器会变得十分简单。
三、Ansible playbook 格式 1、书写格式playbook 由 YMAL 语言编写。YAML( /ˈjæməl/ ) 参考了其他多种语言,包括:XML、C 语言、Python、Perl 以及电子邮件格式 RFC2822,Clark Evans 在 2001 年 5 月在首次发表了这种语言,另外 Ingy döt Net 与 OrenBen-Kiki 也是这语言的共同设计者。YMAL 格式是类似于 JSON 的文件格式,便于人理解和阅读,同时便于书写。首先学习了解一下 YMAL 的格式,对我们后面书写 playbook 很有帮助。以下为 playbook 常用到的YMAL格式:
1、文件的第一行应该以 "---" (三个连字符)开始,表明 YMAL 文件的开始。2、在同一行中,# 之后的内容表示注释,类似于 shell,python 和 ruby。3、YMAL 中的列表元素以 ”-” 开头然后紧跟着一个空格,后面为元素内容。4、同一个列表中的元素应该保持相同的缩进。否则会被当做错误处理。5、play 中 hosts,variables,roles,tasks 等对象的表示方法都是键值中间以 ":" 分隔表示,":" 后面还要增加一个空格。
下面是一个举例:
---
#安装与运行mysql服务
- hosts: node1
remote_user: root
tasks:
- name: install mysql-server package
yum: name=mysql-server state=present
- name: starting mysqld service
service: name=mysql state=started
我们的文件名称应该以 .yml 结尾,像我们上面的例子就是 mysql.yml。其中,有三个部分组成:
hosts :使用 hosts 指示使用哪个主机或主机组来运行下面的 tasks ,每个 playbook 都必须指定 hosts ,hosts 也可以使用通配符格式。主机或主机组在 inventory 清单中指定,可以使用系统默认的 /etc/ansible/hosts,也可以自己编辑,在运行的时候加上 -i 选项,指定清单的位置即可。在运行清单文件的时候,–list-hosts 选项会显示那些主机将会参与执行 task 的过程中。
remote_user:指定远端主机中的哪个用户来登录远端系统,在远端系统执行 task 的用户,可以任意指定,也可以使用 sudo,但是用户必须要有执行相应 task 的权限。
tasks:指定远端主机将要执行的一系列动作。tasks 的核心为 ansible 的模块,前面已经提到模块的用法。tasks 包含 name 和要执行的模块,name 是可选的,只是为了便于用户阅读,不过还是建议加上去,模块是必须的,同时也要给予模块相应的参数。
使用 ansible-playbook 运行 playbook 文件,得到如下输出信息,输出内容为 JSON 格式。并且由不同颜色组成,便于识别。一般而言:
| 绿色代表执行成功,系统保持原样
| 黄色代表系统代表系统状态发生改变
| 红色代表执行失败,显示错误输出
执行有三个步骤:
1、收集 facts
2、执行 tasks
3、报告结果
Playbook 的核心元素:
Hosts:主机组;Tasks:任务列表;Variables:变量,设置方式有四种;Templates:包含了模板语法的文本文件;Handlers:由特定条件触发的任务;
3、基本条件Playbooks 配置文件的基础组件:
Hosts:运行指定任务的目标主机remoute_user:在远程主机上执行任务的用户;sudo_user:tasks:任务列表
a、tasks 格式tasks:
– name: TASK_NAME
module: arguments
notify: HANDLER_NAME
handlers:
– name: HANDLER_NAME
module: arguments
b、模块,模块参数
(1) action: module arguments
(2) module: arguments
注意:shell 和 command 模块后面直接跟命令,而非 key=value 类的参数列表;
c、handlers:任务,在特定条件下触发或者接收到其它任务的通知时被触发(1) 某任务的状态在运行后为 changed 时,可通过 “notify” 通知给相应的 handlers;
(2) 任务可以通过 “tags“ 打标签,而后可在 ansible-playbook 命令上使用 -t 指定进行调用;
c、举例① 定义 playbook
[root@server ~]# cd /etc/ansible
[root@server ansible]# vim nginx.yml---- hosts: web
remote_user: root
tasks:
- name: install nginx
yum: name=nginx state=present
- name: copy nginx.conf
copy: src=/tmp/nginx.conf dest=/etc/nginx/nginx.conf backup=yes
notify: reload #当nginx.conf发生改变时,通知给相应的 handlers
tags: reloadnginx #打标签
- name: start nginx service
service: name=nginx state=started
tags: startnginx #打标签
handlers: #注意,前面没有-,是两个空格
- name: reload
service: name=nginx state=restarted #为了在进程中能看出来
② 测试运行结果
写完了以后,我们就可以运行了:
[root@server ansible]# ansible-playbook nginx.yml
现在我们可以看看两台机器的端口是否开启:
[root@server ansible]# ansible web -m shell -a 'ss -nutlp |grep nginx'192.168.37.122 | SUCCESS | rc=0 >>
tcp LISTEN 0 128 *:80 *:* users:(("nginx",pid=8304,fd=6),("nginx",pid=8303,fd=6))
192.168.37.133 | SUCCESS | rc=0 >>
tcp LISTEN 0 128 *:80 *:* users:(("nginx",pid=9671,fd=6),("nginx",pid=9670,fd=6))
③ 测试标签
我们在里面已经打上了一个标签,所以可以直接引用标签。但是我们需要先把服务关闭,再来运行剧本并引用标签:
[root@server ansible]# ansible web -m shell -a 'systemctl stop nginx'
[root@server ansible]# ansible-playbook nginx.yml -t startnginx
④ 测试 notify
我们还做了一个 notify,来测试一下。首先,它的触发条件是配置文件被改变,所以我们去把配置文件中的端口改一下:
[root@server ansible]# vim /tmp/nginx.conf
listen 8080;
然后我们重新加载一下这个剧本:
发现我们执行的就是 reload 段以及我们定义的 notify 部分。我们来看一看我们的端口号:
[root@server ansible]# ansible web -m shell -a 'ss -ntlp | grep nginx'192.168.37.122 | SUCCESS | rc=0 >>
LISTEN 0 128 *:8080 *:* users:(("nginx",pid=2097,fd=6),("nginx",pid=2096,fd=6))
192.168.37.133 | SUCCESS | rc=0 >>
LISTEN 0 128 *:8080 *:* users:(("nginx",pid=3061,fd=6),("nginx",pid=3060,fd=6))
可以看出,我们的 nginx 端口已经变成了 8080。
4、variables 部分上文中,我们说到了 variables 是变量,有四种定义方法,现在我们就来说说这四种定义方法:
① facts :可直接调用setup 这个模块就是通过调用 facts 组件来实现的。我们这里的 variables 也可以直接调用 facts 组件。具体的 facters 我们可以使用 setup 模块来获取,然后直接放入我们的剧本中调用即可。
② 用户自定义变量我们也可以直接使用用户自定义变量,想要自定义变量有以下两种方式:
通过命令行传入:ansible-playbook 命令的命令行中的 -e VARS, --extra-vars=VARS,这样就可以直接把自定义的变量传入。
在 playbook 中定义变量:我们也可以直接在 playbook 中定义我们的变量。示例如下:
vars:
- var1: value1
- - var2: value2
5、variables 举例
① 定义剧本
我们就使用全局替换把我们刚刚编辑的文件修改一下:
[root@server ansible]# vim nginx.yml
这样一来,我们的剧本就定义完成了。
② 拷贝配置文件我们想要在被监管的机器上安装什么服务的话,就直接在我们的 server 端上把该服务的配置文件拷贝到我们的/tmp/目录下。这样我们的剧本才能正常运行。我们就以 keepalived 服务为例:
[root@server ansible]# cp /etc/keepalived/keepalived.conf /tmp/keepalived.conf
③ 运行剧本,变量由命令行传入
[root@server ansible]# ansible-playbook nginx.yml -e rpmname=keepalived
同样的,我们可以直接在剧本中把变量定义好,这样就不需要在通过命令行传入了。以后想要安装不同的服务,直接在剧本里把变量修改一下即可。
[root@server ansible]# vim nginx.yml
我们刚刚已经把变量定义在剧本里面了。现在我们来运行一下试试看:
[root@server ansible]# ansible-playbook nginx.yml
发现这样也是可以的。
a、通过 roles 传递变量
b、Host Inventory
我们也可以直接在主机清单中定义。
1、向不同的主机传递不同的变量:
IP/HOSTNAME varaiable=value var2=value2
2、向组中的主机传递相同的变量:
[groupname:vars]
variable=value
6、模板 templates
模板是一个文本文件,嵌套有脚本(使用模板编程语言编写)。Jinja2:Jinja2 是 python 的一种模板语言,以 Django 的模板语言为原本。模板支持:
字符串:使用单引号或双引号; 数字:整数,浮点数; 列表:[item1, item2, ...] 元组:(item1, item2, ...) 字典:{key1:value1, key2:value2, ...} 布尔型:true/false 算术运算:+, -, *, /, //, %, ** 比较操作:==, !=, >, >=, { ansible_processor_vcpus }}; listen {{ nginxport }};{ item }} state=absent with_items: - httpd - php - php-mysql{ item }} state=present with_items: - nginx - memcached - php-fpm - name: add some groups group: name={{ item }} state=present with_items: - group11 - group12 - group13 - name: add some users user: name={{ item.name }} group={{ item.group }} state=present with_items: - { name: 'user11', group: 'group11' } - { name: 'user12', group: 'group12' } - { name: 'user13', group: 'group13' } tcp LISTEN 0 128 *:9999 *:* users:(("nginx",pid=7831,fd=6),("nginx",pid=7830,fd=6),("nginx",pid=7829,fd=6)) 192.168.37.133 | SUCCESS | rc=0 >> tcp LISTEN 0 128 *:9999 *:* users:(("nginx",pid=9654,fd=6),("nginx",pid=9653,fd=6),("nginx",pid=9652,fd=6))
可以看出我们的剧本已经执行成功。