playbook 是 ansible 用于配置,部署,和管理被控节点的剧本。
经过 playbook 的详细描述,执行其中的一系列 tasks ,可让远端主机达到预期的状态。playbook 就像 Ansible 控制器给被控节点列出的的一系列 to-do-list ,而被控节点必需要完成。
也能够这么理解,playbook 字面意思,即剧本,现实中由演员按照剧本表演,在Ansible中,此次由计算机进行表演,由计算机安装,部署应用,提供对外服务,以及组织计算机处理各类各样的事情。php
执行一些简单的任务,使用ad-hoc命令能够方便的解决问题,可是有时一个设施过于复杂,须要大量的操做时候,执行的ad-hoc命令是不适合的,这时最好使用playbook。
就像执行shell命令与写shell脚本同样,也能够理解为批处理任务,不过playbook有本身的语法格式。
使用playbook你能够方便的重用这些代码,能够移植到不一样的机器上面,像函数同样,最大化的利用代码。在你使用Ansible的过程当中,你也会发现,你所处理的大部分操做都是编写playbook。能够把常见的应用都编写成playbook,以后管理服务器会变得十分简单。node
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格式:
一、文件的第一行应该以 "---" (三个连字符)开始,代表YMAL文件的开始。
二、在同一行中,#以后的内容表示注释,相似于shell,python和ruby。
三、YMAL中的列表元素以”-”开头而后紧跟着一个空格,后面为元素内容。
四、同一个列表中的元素应该保持相同的缩进。不然会被当作错误处理。
五、play中hosts,variables,roles,tasks等对象的表示方法都是键值中间以":"分隔表示,":"后面还要增长一个空格。
下面是一个举例:python
--- #安装与运行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
。其中,有三个部分组成:mysql
host部分
:使用 hosts 指示使用哪一个主机或主机组来运行下面的 tasks ,每一个 playbook 都必须指定 hosts ,hosts也可使用通配符格式。主机或主机组在 inventory 清单中指定,可使用系统默认的/etc/ansible/hosts
,也能够本身编辑,在运行的时候加上-i
选项,指定清单的位置便可。在运行清单文件的时候,–list-hosts
选项会显示那些主机将会参与执行 task 的过程当中。
remote_user
:指定远端主机中的哪一个用户来登陆远端系统,在远端系统执行 task 的用户,能够任意指定,也可使用 sudo,可是用户必需要有执行相应 task 的权限。
tasks
:指定远端主机将要执行的一系列动做。tasks 的核心为 ansible 的模块,前面已经提到模块的用法。tasks 包含name
和要执行的模块
,name 是可选的,只是为了便于用户阅读,不过仍是建议加上去,模块是必须的,同时也要给予模块相应的参数。nginx
使用ansible-playbook运行playbook文件,获得以下输出信息,输出内容为JSON格式。而且由不一样颜色组成,便于识别。通常而言
| 绿色表明执行成功,系统保持原样
| 黄色表明系统表明系统状态发生改变
| 红色表明执行失败,显示错误输出
执行有三个步骤:一、收集facts 二、执行tasks 三、报告结果
web
Playbook的核心元素:sql
Hosts
:主机组;
Tasks
:任务列表;
Variables
:变量,设置方式有四种;
Templates
:包含了模板语法的文本文件;
Handlers
:由特定条件触发的任务;shell
Playbooks配置文件的基础组件:编程
Hosts
:运行指定任务的目标主机
remoute_user
:在远程主机上执行任务的用户;
sudo_user
:
tasks
:任务列表vim格式:
tasks:
– name: TASK_NAME
module: arguments
notify: HANDLER_NAME
handlers:
– name: HANDLER_NAME
module: arguments
模块,模块参数
:格式:
(1) action: module arguments
(2) module: arguments
注意:shell和command模块后面直接跟命令,而非key=value类的参数列表;
handlers
:任务,在特定条件下触发;接收到其它任务的通知时被触发;
(1) 某任务的状态在运行后为changed时,可经过“notify”通知给相应的handlers;
(2) 任务能够经过“tags“打标签,然后可在ansible-playbook命令上使用-t指定进行调用;
① 定义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。
上文中,咱们说到了variables
是变量,有四种定义方法,如今咱们就来讲说这四种定义方法:
上一篇中,咱们有说到setup
这个模块,这个模块就是经过调用facts组件来实现的。咱们这里的variables
也能够直接调用facts
组件。
具体的facters
咱们可使用setup
模块来获取,而后直接放入咱们的剧本中调用便可。
咱们也能够直接使用用户自定义变量,想要自定义变量有如下两种方式:
经过命令行传入
ansible-playbook
命令的命令行中的-e VARS, --extra-vars=VARS
,这样就能够直接把自定义的变量传入。
在playbook中定义变量
咱们也能够直接在playbook中定义咱们的变量:
vars: - var1: value1 - - var2: value2
① 定义剧本
咱们就使用全局替换把咱们刚刚编辑的文件修改一下:
[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
发现这样也是能够的~
具体的,咱们下文中说到 roles 的时候再详细说明。这里是传送带
咱们也能够直接在主机清单中定义。
定义的方法以下:
向不一样的主机传递不一样的变量:
IP/HOSTNAME varaiable=value var2=value2
向组中的主机传递相同的变量:
[groupname:vars] variable=value
模板是一个文本文件,嵌套有脚本(使用模板编程语言编写)。
Jinja2
:Jinja2是python的一种模板语言,以Django的模板语言为本来。
模板支持:
字符串:使用单引号或双引号;
数字:整数,浮点数;
列表:[item1, item2, ...]
元组:(item1, item2, ...)
字典:{key1:value1, key2:value2, ...}
布尔型:true/false
算术运算:
+, -, *, /, //, %, **
比较操做:
==, !=, >, >=, <, <=
逻辑运算:
and, or, not
一般来讲,模板都是经过引用变量来运用的。
① 定义模板
咱们直接把以前定义的/tmp/nginx.conf
改个名,而后编辑一下,就能够定义成咱们的模板文件了:
[root@server ansible]# cd /tmp [root@server tmp]# mv nginx.conf nginx.conf.j2 [root@server tmp]# vim nginx.conf.j2 worker_processes {{ ansible_processor_vcpus }}; listen {{ nginxport }};
② 修改剧本
咱们如今须要去修改剧原本定义变量:
[root@server ansible]# vim nginx.yml
须要修改的部分如图所示。
③ 运行剧本
上面的准备工做完成后,咱们就能够去运行剧本了:
[root@server ansible]# ansible-playbook nginx.yml -t reloadnginx PLAY [web] ********************************************************************* TASK [setup] ******************************************************************* ok: [192.168.37.122] ok: [192.168.37.133] TASK [copy nginx.conf] ********************************************************* ok: [192.168.37.122] ok: [192.168.37.133] PLAY RECAP ********************************************************************* 192.168.37.122 : ok=2 changed=0 unreachable=0 failed=0 192.168.37.133 : ok=2 changed=0 unreachable=0 failed=0
when语句:在task中使用,jinja2的语法格式。
举例以下:
tasks: - name: install conf file to centos7 template: src=files/nginx.conf.c7.j2 when: ansible_distribution_major_version == "7" - name: install conf file to centos6 template: src=files/nginx.conf.c6.j2 when: ansible_distribution_major_version == "6"
循环:迭代,须要重复执行的任务;
对迭代项的引用,固定变量名为"item",然后,要在task中使用with_items给定要迭代的元素列表;
举例以下:
tasks: - name: unstall web packages yum: name={{ item }} state=absent with_items: - httpd - php - php-mysql
ansible playbook 还支持字典功能。举例以下:
- name: install some packages yum: name={{ 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' }
对于以上全部的方式有个弊端就是没法实现复用假设在同时部署Web、db、ha 时或不一样服务器组合不一样的应用就须要写多个yml文件。很难实现灵活的调用。
roles 用于层次性、结构化地组织playbook。roles 可以根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只须要在playbook中使用include指令便可。简单来说,roles就是经过分别将变量(vars)、文件(file)、任务(tasks)、模块(modules)及处理器(handlers)放置于单独的目录中,并能够便捷地include它们的一种机制。角色通常用于基于主机构建服务的场景中,但也能够是用于构建守护进程等场景中。
角色集合:roles/
mysql/
httpd/
nginx/
files/:存储由copy或script等模块调用的文件;
tasks/:此目录中至少应该有一个名为main.yml的文件,用于定义各task;其它的文件须要由main.yml进行“包含”调用;
handlers/:此目录中至少应该有一个名为main.yml的文件,用于定义各handler;其它的文件须要由main.yml进行“包含”调用;
vars/:此目录中至少应该有一个名为main.yml的文件,用于定义各variable;其它的文件须要由main.yml进行“包含”调用;
templates/:存储由template模块调用的模板文本;
meta/:此目录中至少应该有一个名为main.yml的文件,定义当前角色的特殊设定及其依赖关系;其它的文件须要由main.yml进行“包含”调用;
default/:此目录中至少应该有一个名为main.yml的文件,用于设定默认变量;
1. 在roles目录下生成对应的目录结构
[root@server ansible]# cd roles/ [root@server roles]# ls [root@server roles]# mkdir -pv ./{nginx,mysql,httpd}/{files,templates,vars,tasks,handlers,meta,default} [root@server roles]# tree . ├── httpd │ ├── default │ ├── files │ ├── handlers │ ├── meta │ ├── tasks │ ├── templates │ └── vars ├── mysql │ ├── default │ ├── files │ ├── handlers │ ├── meta │ ├── tasks │ ├── templates │ └── vars └── nginx ├── default ├── files ├── handlers ├── meta ├── tasks ├── templates └── vars 24 directories, 0 files
2. 定义配置文件
咱们须要修改的配置文件为/tasks/main.yml
,下面,咱们就来修改一下:
[root@server roles]# vim nginx/tasks/main.yml - name: cp copy: src=nginx-1.10.2-1.el7.ngx.x86_64.rpm dest=/tmp/nginx-1.10.2-1.el7.ngx.x86_64.rpm - name: install yum: name=/tmp/nginx-1.10.2-1.el7.ngx.x86_64.rpm state=latest - name: conf template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf tags: nginxconf notify: new conf to reload - name: start service service: name=nginx state=started enabled=true
3. 放置咱们所须要的文件到指定目录
由于咱们定义的角色已经有了新的组成方式,因此咱们须要把文件都放到指定的位置,这样,才能让配置文件找到这些并进行加载。
rpm包放在files
目录下,模板放在templates
目录下:
[root@server nginx]# cp /tmp/nginx-1.10.2-1.el7.ngx.x86_64.rpm ./files/ [root@server nginx]# cp /tmp/nginx.conf.j2 ./templates/ [root@server nginx]# tree . ├── default ├── files │ └── nginx-1.10.2-1.el7.ngx.x86_64.rpm ├── handlers ├── meta ├── tasks │ └── main.yml ├── templates │ └── nginx.conf.j2 └── vars 7 directories, 3 files
4. 修改变量文件
咱们在模板中定义的变量,也要去配置文件中加上:
[root@server nginx]# vim vars/main.yml nginxprot: 9999
5. 定义handlers文件
咱们在配置文件中定义了notify
,因此我么也须要定义handlers
,咱们来修改配置文件:
[root@server nginx]# vim handlers/main.yml - name: new conf to reload service: name=nginx state=restarted
6. 定义剧本文件
接下来,咱们就来定义剧本文件,因为大部分设置咱们都单独配置在了roles里面,因此,接下来剧本就只须要写一点点内容便可:
[root@server ansible]# vim roles.yml - hosts: web remote_user: root roles: - nginx
7. 启动服务
剧本定义完成之后,咱们就能够来启动服务了:
[root@server ansible]# ansible-playbook roles.yml PLAY [web] ********************************************************************* TASK [setup] ******************************************************************* ok: [192.168.37.122] ok: [192.168.37.133] TASK [nginx : cp] ************************************************************** ok: [192.168.37.122] ok: [192.168.37.133] TASK [nginx : install] ********************************************************* changed: [192.168.37.122] changed: [192.168.37.133] TASK [nginx : conf] ************************************************************ changed: [192.168.37.122] changed: [192.168.37.133] TASK [nginx : start service] *************************************************** changed: [192.168.37.122] changed: [192.168.37.133] RUNNING HANDLER [nginx : new conf to reload] *********************************** changed: [192.168.37.122] changed: [192.168.37.133] PLAY RECAP ********************************************************************* 192.168.37.122 : ok=6 changed=4 unreachable=0 failed=0 192.168.37.133 : ok=6 changed=4 unreachable=0 failed=0
启动事后照例查看端口号:
[root@server ansible]# ansible web -m shell -a "ss -ntulp |grep 9999" 192.168.37.122 | SUCCESS | rc=0 >> 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))
能够看出咱们的剧本已经执行成功。