Ansible-02-Playbook
注意:对于本文中出现的 { { } }和{ % % },前后 “{ {“ 以及 “{ %” 中间在实际操作中是没有空格的,由于hexo模板渲染问题,导致不得不加空格
YAML
介绍
YAML是一个可读性高的用来表达资料序列的格式。YAML参考了其他多种语言,包括: XML、C语言、Python、Perl以及电子邮件格式RFC2822等。Clark Evans在2001年首次发表了这种语言,另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者
YAML Ain’t Markup Language,即YAML不是XML。不过,在开发的这种语言时, YAML的意思其实是:”Yet Another Markup Language”(仍是一种标记语言)
特性
- YAML的可读性好
- YAML和脚本语言的交互性好
- YAML使用实现语言的数据类型
- YAML有一个一致的信息模型
- YAML易于实现
- YAML可以基于流来处理
- YAML表达能力强,扩展性好
更多的内容及规范参见:http://www.yaml.org
语法
在单一档案中,可用连续三个连字号(-)区分多个档案。另外,还有选择性的连续三个点号( … )用来表示档案结尾
次行开始正常写Playbook的内容,一般建议写明该Playbook的功能
使用#号注释代码
缩进必须是统一的,不能空格和tab混用
缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的
YAML文件内容是区别大小写的,k/v的值均需大小写敏感
多个k/v可同行写也可换行写,同行使用,分隔
v可是个字符串,也可是另一个列表
一个完整的代码块功能需最少元素需包括 name 和 task
一个name只能包括一个task
YAML文件扩展名通常为yml或yaml
List:列表,其所有元素均使用“-”打头
示例:
1
2
3
4
5# A list of tasty fruits
-Apple
-Orange
-Strawberry
-MangoDictionary:字典,通常由多个key与value构成
示例:
1
2
3
4
5
# An employee record
name: Example Developer
job: Developer
skill: Elite也可以将key:value放置于{}中进行表示,用,分隔多个key:value
示例:
1
2
3
# An employee record
{name: Example Developer, job: Developer, skill: Elite}YAML的语法和其他高阶语言类似,并且可以简单表达清单、散列表、标量等数据结构。其结构(Structure)通过空格来展示,序列(Sequence)里的项用”-“来代表,Map里的键值对用”:”分隔
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14name: John Smith
age: 41
gender: Male
spouse:
name: Jane Smith
age: 37
gender: Female
children:
- name: Jimmy Smith
age: 17
gender: Male
- name: Jenny Smith
age: 13
gender: Female
三种常见的数据交换格式

Playbook
核心元素
Host 执行的远程主机列表
Task 任务集
Variables 内置变量或自定义变量在playbook中调用
Templates 模板,可替换模板文件中的变量并实现一些简单逻辑的文件
Handlers 和 notify 结合使用,由特定条件触发的操作,满足条件方才执行, 否则不执行
tags 标签指定某条任务执行,用于选择运行playbook中的部分代码。ansible 具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。此时,如果确信其没有变化,就可以通过tags跳过此些代码片断
ansible-playbook -t tagsname useradd.yml
基础组件
Hosts:
playbook中的每一个play的目的都是为了让特定主机以某个指定的用户身份执行任务。hosts用于指定要执行指定任务的主机,须事先定义在主机清单中
可以是如下形式:
1
2
3
4- - one.example.com
one.example.com:two.example.com
192.168.1.50
192.168.1.*Websrvs:dbsrvs 或者,两个组的并集
Websrvs:&dbsrvs 与,两个组的交集
webservers:!phoenix 在websrvs组,但不在dbsrvs组
示例: - host: websrvs:dbsrvs
remote_user: 可用于Host和task中。也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某任务;此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户
1
2
3
4
5
6
7
8- host: websrvs
remote_user:root
task:
- name: test conection
ping:
remote_user: xxx
sudo: yes 默认sudo为root
sudo_user:kinmfer sudo为kinmfertask列表和action
- play的主体部分是task list,task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后,再开始第二个任务
- task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致
- 每个task都应该有其name,用于playbook的执行结果输出,建议其内容能清晰地描述任务执行步骤。如果未提供name,则action的结果将用于输出
tasks:任务列表
两种格式:
- action: module arguments
- module: arguments 建议使用
- 注意:shell和command模块后面跟命令,而非key=value
某任务的状态在运行后为changed时,可通过“notify”通知给相应的handlers
任务可以通过”tags“打标签,可在ansible-playbook命令上使用-t指定进行调用示例:
1
2
3tasks:
- name: disable selinux
command: /sbin/setenforce 0如果命令或脚本的退出码不为零,可以使用如下方式替代
1
2
3tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true或者使用ignore_errors来忽略错误信息
1
2
3
4tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True运行playbook
运行playbok的方式
ansible-playbook <filename.yml> ... [options]常见选项
- –syntax-check 检查语法格式
- –check -C 只检测可能会发生的改变,但不真正执行操作
- –list-hosts 列出运行任务的主机
- –list-tags 列出tag
- –list-task 列出task
- –limit 主机列表 只针对主机列表中的主机执行
- -v -vv -vvv 显示过程
- -e 在Playbook中引入外部参数变量
- -t –tags 指定执行该tags的任务
- –skip-tags 跳过指定的tags任务
示例
ansible-playbook file.yml --check只检测ansible-playbook file.ymlansible-playbook file.yml --limit websrvs
示例
sysuser.yml
1
2
3
4
5
6
7
8- hosts: all
remote_user: root
gather_facts: no # 不收集信息
tasks:
- name: create mysql user
user: name=mysql system=yes uid=36
- name: create a group
group: name=httpd system=yeshttpd.yml
1
2
3
4
5
6
7
8
9
10- hosts: websrvs
remote_user: root
gather_facts: no # 不收集信息
tasks:
- name: Install httpd
yum: name=httpd state=present
- name: Install configure file
copy: src=files/httpd.conf dest=/etc/httpd/conf/
- name: start service
service: name=httpd state=started enabled=yesnginx.yml
1
2
3
4
5
6
7
8
9
10
11- hosts: websrvs
remote_user: root
tasks:
- name: add group nginx
group: name=nginx state=present
- name: add user nginx
user: name=nginx state=present group=nginx
- name: Install Nginx
yum: name=nginx state=present
- name: Start Nginx
service: name=nginx state=started enabled=yeshandlers和notify
Handlers
是task列表,这些task与前述的task并没有本质上的不同,用于当关注的资源发生变化时,才会采取一定的操作
Notify
此action可用于在每个play的最后被触发,这样可避免多次有改变发生时每次都执行指定的操作,仅在所有的变化发生完成后一次性地执行指定操作。 在notify中列出的操作称为handler,也即notify中调用handler中定义的操作
示例
1 | - hosts: websrvs |
1 | - hosts: webnodes |
1 | - hosts: websrvs |
tags
1 | - hosts: websrvs |
ansible-playbook –t conf httpd.yml
1 | - hosts: testsrv |
ansible-playbook –t rshttpd httpd2.yml
ansible-playbook –-skip-tags rshttpd httpd2.yml
变量
变量来源:
ansible setup facts 远程主机的所有变量都可直接调用
在/etc/ansible/hosts中定义
普通变量:主机组中主机单独定义,优先级高于公共变量
公共(组)变量:针对主机组中所有主机定义统一变量
通过命令行指定变量,优先级最高
ansible-playbook –e varname=value在playbook中定义
1
2
3vars:
-var1: value1
-var2: value2在独立的变量YAML文件中定义
在role中定义
变量命名
变量名仅能由字母、数字和下划线组成,且只能以字母开头
变量定义:key=value
示例:http_port=80
变量调用方式:
通过{ { variable_name } } 调用变量,且变量名前后必须有空格,有时用 “{ { variable_name } }”才生效
ansible-playbook –e 选项指定
ansible-playbook test.yml -e "hosts=www user=xxx"
主机变量
可以在inventory中定义主机时为其添加主机变量以便于在playbook中使用
示例:
1
2[websrvs]
www1.kinmfer.com http_port=80 maxRequestsPerChild=808 www2.kinmfer.com http_port=8080 maxRequestsPerChild=909组变量
组变量是指赋予给指定组内所有主机上的在playbook中可用的变量
示例 :
1
2
3
4
5[websrvs]
www1.xxx.com www2.xxx.com
[websrvs:vars]
ntp_server=ntp.xxx.com
nfs_server=nfs.xxx.com命令行指定变量:
ansible websvrs –e http_port=8000 –m hostname –a ‘name={ { hname } }{ { mark } }{ { http_port } }’使用变量文件
cat vars.yml1
2var1: httpd
var2: nginx
cat var.yml1
2
3
4
5
6
7
8
9- hosts: web
remote_user: root
vars_files:
- vars.yml
tasks:
- name: create httpd log
file: name=/app/{ { var1 } }.log state=touch
- name: create nginx log
file: name=/app/{ { var2 } }.log state=touchsetup变量
1
2
3
4
5- host: websrvs
remote_user:root
task:
- name: create log file
file: name=/var/log/ {{ ansible_fqdn }} stae=touch获取内置变量方法:
ansible oldboy -m setup -a "filter=ansible_hostname"常见主机信息:
变量 含义 ansible_all_ipv4_addresses: 仅显示ipv4的信息。 ansible_devices: 仅显示磁盘设备信息。 ansible_distribution: 显示是什么系统,例:centos,suse等。 ansible_distribution_major_version: 显示是系统主版本。 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个数(只显示总的个数)。
示例
var.yml
1 | - hosts: websrvs |
ansible-playbook –e pkname=httpd var.yml
var.yml
1 | - hosts: websrvs |
ansible-playbook var.ymlansible-playbook -e "username=user2 groupname=group2” var2.yml
when
条件测试:如果需要根据变量、facts或此前任务的执行结果来做为某task执行与否的前提时要用到条件测试,通过when语句实现,在task中使用,jinja2的语法 格式
when语句
在task后添加when子句即可使用条件测试;when语句支持Jinja2表达式语法
示例:
1
2
3
4tasks:
- name: "shutdown RedHat flavored systems"
command: /sbin/shutdown -h now
when: ansible_os_family == "RedHat"when语句中还可以使用Jinja2的大多“filter”,例如要忽略此前某语句的错误并基于其结果(failed或者success)运行后面指定的语句,可使用类似如下形式:
1
2
3
4
5
6
7
8
9
10tasks:
- command: /bin/false
register: result
ignore_errors: True
- command: /bin/something
when: result|failed
- command: /bin/something_else
when: result|success
- command: /bin/still/something_else
when: result|skipped此外,when语句中还可以使用facts或playbook中定义的变量
示例
1 | - hosts: websrvs |
1 | tasks: |
with_items
迭代:当有需要重复性执行的任务时,可以使用迭代机制
- 对迭代项的引用,固定变量名为”item“
- 要在task中使用with_items给定要迭代的元素列表
- 列表格式:
- 字符串
- 字典
ansible的循环机制还有更多的高级功能,具体请参见官方文档
示例
1 | name: add several users |
上面语句的功能等同于下面的语句:
1 | name: add user testuser1 |
with_items中可以使用元素还可为hashes
1 | name: add several users |
将多个文件进行copy到被控端
1 | - hosts: testsrv |
迭代嵌套自变量
1 | - hosts:websrvs |
字典 with_items
1 | - name: 使用ufw模块来管理哪些端口需要开启 |
基础元素
Facts:是由正在通信的远程目标主机发回的信息,这些信息被保存在ansible变量中。要获取指定的远程主机所支持的所有facts,可使用如下命令进行
ansible websrvs -m setup通过命令行传递变量
在运行playbook的时候也可以传递一些变量供playbook使用
示例:
ansible-playbook test.yml -e "hosts=www user=kinmfer"register
把任务的输出定义为变量,然后用于其他任务
示例 :
1
2
3
4
5
6
7tasks:
- name: check server port
shell: netstat -lntup
register: get_server_port
ignore_errors: True
- name: display port info
debug: msg={ { get_server_port.stdout_lines } }剧本整合
方式一:include_tasks: f1.yml
1 | hosts: all |
方式二:include: f1.yml
1 | - include:f1.yml |
方式三:import_playbook:
1 | [root@m01 ansible-playbook]# cat main.yml |
常见错误
- 剧本语法规范是否符合(空格 冒号 短横线)
- 剧本中模块使用是否正确
- 剧本中一个name标识下面只能写一个模块任务信息
- 剧本中尽量不要大量使用shell模块
模板template
注意:
在模板语法中,对于 { { } }和{ % % },前后的 “{ {“ 以及 “{ %” 中间是没有空格的,由于hexo渲染的问题,本篇文章的某些部分不得已在中间加空格,但切记是没有的!!!
文本文件,嵌套有脚本(使用模板编程语言编写)
template功能:根据模块文件动态生成对应的配置文件
template文件必须存放于templates目录下,且命名为 .j2 结尾
yaml/yml 文件需和templates目录平级,目录结构如下:
1
2
3
4./
├─ temnginx.yml
└─ templates
└─ nginx.conf.j2
Jinja2语言,使用字面量,有下面形式
- 字符串:使用单引号或双引号
- 数字:整数,浮点数
- 列表:[item1, item2, …]
- 元组:(item1, item2, …)
- 字典:{ key1:value1, key2:value2, … }
- 布尔型:true/false
- 算术运算:+, -, *, /, //, %, **
- 比较操作:==, !=, >, >=, <, <=
- 逻辑运算:and,or,no
- 流表达式:For,If,When
Jinja2
字面量
- 表达式最简单的形式就是字面量。字面量表示诸如字符串和数值的 Python 对象。如“Hello World”
- 双引号或单引号中间的一切都是字符串。无论何时你需要在模板中使用一个 字符串(比如函数调用、过滤器或只是包含或继承一个模板的参数),如42 42.23
- 数值可以为整数和浮点数。如果有小数点,则为浮点数,否则为整数。在 Python 里, 42 和 42.0 是不一样的
算术运算
Jinja 允许你用计算值。这在模板中很少用到,但为了完整性允许其存在
支持下面的运算符
+:把两个对象加到一起。通常对象是素质,但是如果两者是字符串或列表,你可以用这 种方 式来衔接它们。无论如何这不是首选的连接字符串的方式!连接字符串见 ~ 运算符。 { { 1 + 1 } } 等于 2
-:用第一个数减去第二个数。 { { 3 - 2 } } 等于 1
/:对两个数做除法。返回值会是一个浮点数。 { { 1 / 2 } } 等于 { { 0.5 } }
//:对两个数做除法,返回整数商。 { { 20 // 7 } } 等于 2
%:计算整数除法的余数。 { { 11 % 7 } } 等于 4
*:用右边的数乘左边的操作数。 { { 2 * 2 } } 会返回 4 。也可以用于重复一个字符串多次。{ { ‘=’ * 80 } } 会打印 80 个等号的横条
**:取左操作数的右操作数次幂。 { { 2**3 } } 会返回 8
比较操作符
- == 比较两个对象是否相等
- != 比较两个对象是否不等
- > 如果左边大于右边,返回 true
- >= 如果左边大于等于右边,返回 true
- < 如果左边小于右边,返回 true
- <= 如果左边小于等于右边,返回 true
逻辑运算符
对于 if 语句,在 for 过滤或 if 表达式中,它可以用于联合多个表达式
and
如果左操作数和右操作数同为真,返回 true
or
如果左操作数和右操作数有一个为真,返回 true
not
对一个表达式取反(见下)
(expr)
表达式组
[‘list’, ‘of’, ‘objects’]:
一对中括号括起来的东西是一个列表。列表用于存储和迭代序列化的数据。例如 你可以容易地在 for 循环中用列表和元组创建一个链接的列表
1
2
3
4
5
6<ul>
{% for href, caption in [('index.html', 'Index'), ('about.html', 'About'), ('downloads.html', 'Downloads')] %}
<li>{ { caption } }</li>
{% endfor %}
</ul>
(‘tuple’, ‘of’, ‘values’):元组与列表类似,只是你不能修改元组。如果元组中只有一个项,你需要以逗号 结尾它。元组通常用于表 示两个或更多元素的项。更多细节见上面的例子
{‘dict’: ‘of’, ‘key’: ‘and’, ‘value’: ‘pairs’}:
Python 中的字典是一种关联键和值的结构。键必须是唯一的,并且键必须只有一个值。字典在模板中很少使用,罕用于诸如 xmlattr() 过滤器之类
true / false:
true 永远是 true ,而 false 始终是 false
示例
利用template 同步nginx配置文件
准备templates/nginx.conf.j2文件
vim temnginx.yml
1 | - hosts: websrvs |
ansible-playbook temnginx.yml
变更替换
修改文件nginx.conf.j2 下面行为
1
worker_processes {{ ansible_processor_vcpus }};
cat temnginx2.yml1
2
3
4
5- hosts: websrvs
remote_user: root
tasks:
- name: template config to remote hosts
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.confansible-playbook temnginx2.yml
算数运算
vim nginx.conf.j2
1 | worker_processes { { ansible_processor_vcpus**2 } }; |
for循环
1 | #temnginx1.yml |
1 | #temnginx4. yml |
if判断
1 | - hosts: websrvs |



