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
。其中,有三个部分组成:
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 是可选的,只是为了便于用户阅读,不过还是建议加上去,模块是必须的,同时也要给予模块相应的参数。
使用ansible-playbook运行playbook文件,得到如下输出信息,输出内容为JSON格式。并且由不同颜色组成,便于识别。一般而言 | 绿色代表执行成功,系统保持原样 | 黄色代表系统代表系统状态发生改变 | 红色代表执行失败,显示错误输出 执行有三个步骤:1、收集facts 2、执行tasks 3、报告结果
2、核心元素
Playbook的核心元素:
Hosts
:主机组;Tasks
:任务列表;Variables
:变量,设置方式有四种;Templates
:包含了模板语法的文本文件;Handlers
:由特定条件触发的任务;
3、基本组件
Playbooks配置文件的基础组件:
Hosts
:运行指定任务的目标主机
remote_user
:在远程主机上执行任务的用户;
sudo_user
:
tasks
:任务列表
模块,模块参数
:格式: (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 #为了在进程中能看出来
运行前三部曲
检查错误
ansible-playbook nginx.yml --syntax-check
列出所有任务
ansible-playbook nginx.yml --list-task
列出在那些机器执行
ansible-playbook nginx.yml --list-hosts
运行
ansible-playbook nginx.yml
② 测试运行结果 写完了以后,我们就可以运行了:
[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_all_ipv4_addresses:仅显示ipv4的信息 ---> [u'192.168.95.143'] ansible_eth0['ipv4']['address']:仅显示ipv4的信息 ---> eth0 的ip地址 ansible_devices:仅显示磁盘设备信息 ansible_distribution:显示是什么系统,例:centos,suse等 ansible_distribution_version:仅显示系统版本 ansible_machine:显示系统类型,例:32位,还是64位 ansible_eth0:仅显示eth0的信息 ansible_hostname:仅显示主机名 ansible_kernel:仅显示内核版本 ansible_lvm:显示lvm相关信息 ansible_memtotal_mb:显示系统总内存 ansible_memfree_mb:显示可用系统内存 ansible_memory_mb:详细显示内存情况 ansible_swaptotal_mb:显示总的swap内存 ansible_swapfree_mb:显示swap内存的可用内存 ansible_mounts:显示系统磁盘挂载情况 ansible_processor:显示cpu个数(具体显示每个cpu的型号) ansible_processor_vcpus:显示cpu个数(只显示总的个数) ansible_python_version:显示python版本
例如:批量修改主机 host 文件
---
- hosts: web
vars:
IP: "{
{ ansible_eth0['ipv4']['address'] }}"
tasks:
- name: 将原有的hosts文件备份
shell: mv /etc/hosts /etc/hosts_bak
- name: 将ansible端的hosts复制到各自机器上
copy: src=/root/hosts dest=/etc/ owner=root group=root mode=0644
- name: 在新的hosts文件后面追加各自机器内网ip和hostname
lineinfile: dest=/etc/hosts line="{
{ IP }} {
{ ansible_hostname }} "
② 用户自定义变量
我们也可以直接使用用户自定义变量,想要自定义变量有以下两种方式:
通过命令行传入
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传递变量
具体的,我们下文中说到 roles 的时候再详细说明。
④ Host Inventory
我们也可以直接在主机清单中定义。 定义的方法如下:
向不同的主机传递不同的变量:
IP/HOSTNAME varaiable=value var2=value2
向组中的主机传递相同的变量:
[groupname:vars]
variable=value
Ansible Inventory 内置参数
使用内置变量把用户名密码写在Inventory中,也就是/etc/ansible/hosts文件里,缺点就是暴露了账号密码,不安全。如果有多个主机需要使用同样的变量,可以用组变量的形式,书写格式如下:
[web]
192.168.100.10
192.168.100.11
192.168.100.12
[web:vars] #给名为webservers的组定义一个变量,:vars是固定格式
ansible_ssh_port=22
ansible_ssh_user='root'
ansible_ssh_pass='1234.com'
5、模板 templates
模板是一个文本文件,嵌套有脚本(使用模板编程语言编写)。
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
需要修改的部分如图所示。
copy模块 也需要修改为 template模块
③ 运行剧本 上面的准备工作完成后,我们就可以去运行剧本了:
[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
6、条件测试
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
7、字典
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' }