本文最后更新于:2023年12月5日 晚上
https://docs.ansible.com/ansible/latest/playbook_guide/index.html
[TOC]
ansible playbooks
语法 / 结构
playbook 使用yaml语法。
一个 playbook 由一个或多个 ‘play‘ 组成。每个play执行总体目标的一部分,运行一个或多个task,每个task调用一个ansible模块。
执行
playbook 按照从上往下的顺序运行,在每个play中,任务也按照从上往下的顺序运行,可以给play指定运行的机器。每个paly定义了两件事:
下面的playbook中,有两个play,每个play中都有两个task,每个task都是一个对ansible模块的调用,
第一个play以web servers为目标,第二个play以database servers为目标:
运行playbook
使用 ansible-playbook 命令运行 playbook:
验证
在运行 playbook 之前,可以验证语法错误和其他问题,ansible-playbook提供了几个验证选项,包括 --check
、diff
、--list-host
、--list-tasks
和 syntax-check
。
在执行 playbook 之前,可以使用 ansible-lint 对 ansible-playbook 进行详细的、特定于Ansible的反馈。
ansible-lint 默认规则 页面描述了每个错误。对于[403],推荐的修复方法是将playbook中的 state: latest
更改为 state: present
。
使用 playbooks
模板(jinja2)
ansible 使用 jinja2 模板 来启用动态表达式以及对变量(包括facts)的访问。
过滤器
jinja2 内置过滤器列表:
ansible 在原生的jinja2模板基础上,扩展了很多过滤器和测试变量。
处理未定义变量
提供默认值
如果是在 role 中,还可以添加 defaults/main.yml
来定义 role 中的默认值。
从2.8版本开始,访问未定义值的属性,可以定义返回默认值,而不是直接抛出错误,例如:
如果foo或者bar没有定义,则返回 'DEFAULT'
。
如果变量为false或者空字符,则使用默认值,只需要将 default
函数第二个参数设置为 true
:
关于 lookup
,见下文。
忽略未定义 omit
上面介绍了,当变量未定义时,使用设置的默认值。
某些变量有系统默认值,如果当变量未定义,使用系统默认值;当变量定义,则使用定义的值,该怎么办?
答案是使用 omit
。
在 ansible中,omit
关键字的作用是把对应的变量改为 可选,就是忽略未定义的变量。例如:将arg="{{omit}}"
传递给模块,如果arg
变量未定义,则相当于根本没传;如果arg
变量定义,则使用 arg
。
下面的示例中:default(omit)
作用和正常的 default('value')
相反,omit
对应 item.mode
,/tmp/foo
和 /tmp/bar
没有定义mode
,item.mode
使用系统默认值;/tmp/baz
定义了mode
为0444
,则item.mode
使用0444
:
设置未定义变量的错误提示信息 undef
default
和 undef
都是用于处理变量的关键字。当变量未定义时,使用default
可以为变量设置默认值;而使用 undef
则可以为变量设置一个自定义的错误提示信息,当变量未定义时会抛出这个错误信息。
两者的区别在于,使用default可以为变量设置默认值,而使用undef则不会为变量设置默认值,而是抛出一个自定义的错误提示信息。
为 true/false/null 定义不同的值 ternary
ternary
是一个三元运算符。
variable
是要进行判断的变量,true_value
是当变量为真时的值,false_value
是当变量为假时的值。
第三个参数是可选,它表示当变量未定义时的默认值,如果省略第三个参数,则默认值为false
。
管理数据类型
使用 type_debug
、dict2items
和 items2dict
过滤器来管理数据类型。您还可以使用数据类型本身将值转换为特定的数据类型。
发现数据类型
如果您不确定某个变量的底层Python类型,可以使用 type_debug
过滤器来显示它。这在调试中很有用:
这是个很有用的过滤器,可以检查变量中的数据类型是否正确,但是更推荐 类型测试
将字典转换为列表
使用 dict2items
过滤器将字典转换为适合 循环 的项目列表:
示例:
可以配置key的名称:
将列表转换为字典
使用 items2dict
过滤器将列表转换为字典。
强制数据类型
可以将值转换为某些类型。
格式化数据:yaml 和 json
将模板中的数据结构从JSON或YAML格式切换为JSON或YAML格式,并提供格式化、缩进和加载数据的选项。对调试很有用:
过滤器 to_json
和 unicode 支持
默认情况下to_json和to_nice_json将接收到的数据转换为ASCII,因此:
要保留Unicode字符,请将参数ensure_ascii=False传递给过滤器:
组合和选择数据
合并多个列表中的项目:zip
和 zip_longest
zip
和 zip_longest
是python的原生函数。
使用zip
函数将两个列表中的元素一一对应地组合在一起:
使用zip_longest
函数将多个列表中的元素一一对应地组合在一起,并使用指定的填充值填充缺失的元素:
构造字典:
组合 对象和子元素
组合 哈希/字典
…
从数组或哈希表中选择值
组合列表
permutations
combinations
product
选择JSON数据:JSON查询
随机化数据
打乱列表
管理列表变量
从集合或列表中选择(集合论)
计算数字(数学)
管理网络交互
IP地址过滤器
测试字符串是否是有效的IP地址:
需要特定的IP协议版本:
IP地址过滤器还可用于从IP地址中提取特定信息。例如,要从CIDR获取IP地址本身,您可以使用:
更多关于ipaddr
过滤器的信息和完整的使用指南在 ipaddr过滤器。
网络CLI过滤器
网络XML过滤器
网络VLAN过滤器
散列和加密字符串和密码
获取字符串的sha1哈希值:
获取字符串的md5哈希值:
获取字符串校验和:
获取sha512密码哈希(随机盐):
获取带有特定盐的sha256密码哈希:
为每个系统生成唯一哈希的幂等方法是使用运行之间一致的盐:
…
有几个过滤器可以处理文本,包括URL、文件名和路径名。
向文件添加注释
URLEncode变量
分割URL
使用正则表达式搜索字符串
管理文件名和路径名
操作字符串
管理UUID
处理日期和时间
获取k8s资源名称
测试
测试 是评估模板表达式并返回True或False的一种方式。Jinja附带了许多这样的工具。请参阅官方Jinja模板留档中的 内置测试。
测试和过滤器之间的主要区别是Jinja测试用于比较,而过滤器用于数据操作,并且在jinja中有不同的应用。测试也可以用于列表处理过滤器,如map()
和select()
来选择列表中的项目。
像所有模板一样,测试总是在Ansible控制器上执行,而不是在任务的目标上执行,因为它们测试本地数据。
除了这些Jinja2测试之外,Ansible还提供了更多测试,用户可以轻松创建自己的测试。
查找 lookups
Lookup plugins 从外部来源检索数据,如文件、数据库、键/值存储、API和其他服务。与所有模板一样,查找在Ansible控制机器上执行和评估。Ansible使用标准模板系统使查找插件返回的数据可用。在Ansible 2.5之前,查找主要间接用于循环的with_<lookup>
构造。从Ansible 2.5开始,查找更明确地用作输入loop
关键字的Jinja2表达式的一部分。
在变量中使用 lookups
有关 ansible-core 中 lookups 插件的详细信息和列表,请参阅 使用插件。
您可以使用命令 ansible-doc -l -t lookup
安装在控制机器上的查找插件列表。
now
函数:获取当前时间
在 Ansible 中,您可以使用 now
函数来获取当前时间。now
函数支持两个参数:
- utc:指定为 True 以获取 UTC 中的当前时间。默认为 False。
- fmt:接受一个 strftime 字符串,返回格式化的日期时间字符串。
以下是一个示例,演示如何使用 now 函数获取当前时间:
您还可以使用 Ansible 的 to_datetime
过滤器将字符串转换为日期时间对象。以下是一个示例:
循环 loops
比较 loop
和 with_*
with_<lookup>
关键字依赖 Lookup plugins - 甚至items
是一个 lookup。
loop
关键字相当于 with_list
,是简单循环的最佳选择。
loop
关键字不接受字符串作为输入。
将with_items
更改为loop
要小心,因为with_items
会自动展开。您可能需要使用flatten(1)
。
lookup
不能在loop
中使用,例如下面的写法是不对的:
应该使用 with_*
更合适:
标准loops
迭代简单列表
可以在变量文件或者play 的vars
部分定义list,然后在任务中直接引用:
迭代哈希列表
与条件语句循环组合时,when
将为每个项目单独处理。
在字典上迭代
使用loop循环注册变量
register
关键字:注册变量,运行命令并将该命令的结果注册为变量,每个register值在整个playbook执行期间都是有效的。
10. 使用变量 - Ansible wiki (leops.cn)
将循环的输出注册为变量。例如:
echo {{ item }}
的输出会注册到 echo
变量。
使用带循环的register
时,放置在变量中的数据结构将包含一个results
属性,该属性是来自模块的所有响应的列表。这与使用不带循环的register
时返回的数据结构不同。
对已注册变量进行后续循环以检查结果可能如下所示:
复杂loop
在嵌套list上迭代
您可以使用Jinja2表达式遍历复杂的列表。例如,循环可以组合嵌套列表。
重试任务直到满足条件
使用until
关键字重试任务,直到满足特定条件。下面是一个例子:
此任务最多运行5次,每次尝试之间的延迟为10秒。如果任何尝试的结果在其标准输出中显示 “all systems go”,则任务成功。“retries”的默认值为3,“delay”为5。
在 inventory 上循环
要遍历 inventory 或仅遍历 inventory 的子集,可以使用带有 ansible_play_batch
或 groups
变量的常规 loop
。
还有一个特定的查找插件inventory_hostnames
可以这样使用:
query
和 loopup
loop
需要一个list作为输入,但是lookup
默认返回一串都好分割的值,ansible2.5引入query
,它总是返回一个list。
下面的两个例子做同样的事情:
向loops添加控件(control)
关键字loop_control
可以让您管理循环。
精简输出 label
循环处理复杂的数据结构时,任务的控制台输出可能会很大。要限制显示的输出,请使用loop_control
的label
指令。
输出将仅显示每个item
的name
字段,而不是多行{{ item }}
变量的全部内容。
循环间隔 pause
控制任务循环中每个项目执行之间的时间(以秒为单位),请使用带有loop_control
的pause
指令。
跟踪进度 index_var
该指令指定一个变量名来包含当前循环索引。
指定循环的变量名 loop_var
循环的变量名默认是 item
,如果有嵌套循环的情况,可以使用loop_var
指定循环的变量名。
注意:loop_var
定义的变量名不要于已有的变量名冲突。
使用 ansible_loop_var
变量,访问 loop_var
定义的变量名。
扩展循环变量
开启 loop_control.extended
后,会消耗更多的内存。
从 with_x 迁移到loop
在绝大数情况下,循环推荐使用loop
而不是with_x
。loop
推荐使用过滤器,而不是更复杂的 query
或 loopup
。
下面的示例展示了如何将常见的 with_
转换为 loop
+ 过滤器:
with_list
直接替换为 loop
with_items
替换为 loop
+ flatten
过滤器
with_indexed_items
替换为 loop
+ flatten
过滤器 + loop_control.index_var
with_flattened
with_together
with_dict
with_sequence
with_subelements
with_nested
/ with_cartesian
替换为 loop
+ product
过滤器
with_random_choice
替换为 random
过滤器,不需要 loop
默认情况下,ansible 收集 facts 并在与 playbook hosts line 匹配的机器上执行所有任务。如果过程中需要对另外的机器执行操作,就需要用到 任务委派 功能。
在任务上使用 delegate_to
关键字可以实现委派功能:
local_action
delegate_to: 127.0.0.1
就是将任务委派到本地执行,因为这种委派到本地的操作很常见,所以ansible提供了 local_action
关键字,等效于 delegate_to: 127.0.0.1
:
请注意,您必须配置无密码SSH密钥或ssh代理才能使其工作,否则rsync会请求密码。
要指定更多参数,请使用以下语法:
注意:如果要委派到 inventory 中没有的主机,最好使用 add_host
模块:
add_host
有两个参数:
本地运行 connection
如果 ansible_connection
设置为local
,这请务必设置ansible_python_interpreter
,否则模块将在/usr/local/python
下运行,而不是在 {{ ansible_playbook_python }}
下。
委派 facts
收集的facts
默认分配给 inventory_hostname (当前主机),而不是产生fatcs
的主机,要将收集的事实分配给委托主机而不是当前主机,请将delegate_facts
设置为true
:
此任务收集dbserver组中机器的facts,并将facts分配给这些机器,即使 play 的目标是app_servers组。
条件语句
基本条件 when
最简单的条件语句,适用于单个任务。when
子句是没有双花括号的原始jinja2表达式(参见 group_by_module)。
例如,在开启了SELinux的机器上安装mysql:
基于ansible_facts的条件语句
通常希望根据facts执行或跳过任务。facts是各个主机的属性,包括IP地址、操作系统、文件系统的状态等等。基于facts的条件:
- 只有当操作系统是特定版本时,才能安装某个包;
- 内网主机跳过配置防火墙;
- 当文件系统已满时,才能执行清理任务
常用facts:https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html#commonly-used-facts
并非所有主机都存在所有facts,例如lsb_major_release
尽在目标主机上
块
playbooks 中的错误处理
设置远程环境
使用特定语言的版本管理器
重复使用ansible artifacts
roles 角色
模块默认值
交互式输入:提示
使用变量
发现变量:facts 和 magic 变量
playbook示例:持续交付 和 滚动升级