Ansible playbooks 使用介绍
环境信息
- ansible-core 2.16
- Docker image python:3.12.3
Ansible Playbook 语法
Playbooks 使用 YAML 语法定义(描述)。一个 playbook 由一个或多个 play 依序组成。每个 play 运行一个或多个 task,每个 task 也称为一个 module
每一个 play 中包含了一个 tasks 列表,tasks 列表中的每个 task 在其对应的 hosts 上 依次执行。即一个 task 执行完毕,下一个 task 才会执行。
在运行 playbook 的过程中,如果一个 host 执行 task 失败,这个 host 将从整个 playbook 中移除。如果发生执行失败的情况,需要修正 playbook 中的错误,重新执行。
Ansible playbook 示例:
--- |
一个 Ansible playbook 由一个或多个 plays 组成,每个 play 包含以下部分:
name: 描述性的名称hosts: 指定目标主机become: 提升权限(默认是使用sudo提升到root用户)remote_user: 用于连接到远程主机的账户。(如果 Inventory 中定义了远程连接的用户,会覆盖此处的配置)tasks: 要执行的一系列任务列表vars: 用于定义变量,便于管理和重用gather_facts: 收集Facts, 默认值为yes
tasks 是一个任务列表,每个任务执行特定的操作。任务包含以下元素:
name: 描述任务的目的。module_name: Ansible 模块名称,如apt、service等。module_options: 模块的参数,以键值对的形式提供。when: 条件语句,控制任务是否执行。loop: 循环执行任务
执行以下命令运行 playbook.yml
ansible-playbook playbook.yml -f 10 |
常用选项说明
| 选项 | 说明 | 示例 |
|---|---|---|
-f--forks |
指定并发执行的数量,默认为 5 | |
-v--verbose -vvvvvv |
打印 debug 信息,详细程度从 -v 到 -vvvvvv |
|
-C--check |
Check mode,不执行任何实际操作,而是对要执行的操作进行验证 |
|
-D--diff |
- 只使用 --diff 会执行 play 定义的实际操作,并对所有受影响的文件或者模板显示其变更前后的具体差异- 和 --check 一起使用,不会执行 play 定义的实际操作,只显示变更前后的差异,可以在实际执行前,调试/预览将要进行的变更,防止意外配置变更或文件修改主要用于文件或者模板的变更,对于其他类型的任务(如包安装、服务管理、修改主机名等),不会显示具体的差异( 配合 --check 使用时,结果会显示为 skipping,实际执行时结果为 changed )。 |
|
--list-hosts |
不执行任何实际操作,只列出符合 pattern 的目标主机 |
|
--list-tasks |
不执行任何实际操作,只列出将要执行的 task |
|
--syntax-check |
不执行任何实际操作,只检查 playbook 文件是否有语法错误 |
主机与用户
play 中一般都需要选择要操作的目标主机,也可能需要指明以哪个用户身份来执行 tasks
hosts一个或多个组或主机的 patterns,以,分割remote_user用户名。
--- |
在每一个 task 中,也可以定义自己的远程账号
- hosts: webservers |
sudo 提权操作
在需要提权操作的场景下,需要切换到更高权限的用户,可以使用 become 指令。become 可以在全局(play)级别使用或者 task 级别使用。
如果要提权到其他用户而非 root,可以使用指令 become_user
--- |
register
在 Ansible 中,register 用于捕获任务执行的输出,并存储在一个变量中,以便后续任务使用。假设你的任务如下:
tasks: |
这表示:
- 执行
find {{ remote_dir }} -type f -exec md5sum {} + | sed "s|{{ remote_dir }}||"命令 在远程主机上获取目录下的文件列表以及其 md5 值。 - 使用
register关键字 把命令的执行结果存储到remote_file_list变量中。
register 变量是一个字典(dict) ,其主要字段包括:
- stdout : 标准输出(命令执行的结果)
- stderr : 标准错误输出
- rc : 返回码(0 表示成功)
- stdout_lines : 标准输出的列表格式(按行拆分)
- stderr_lines : 标准错误输出的列表格式(按行拆分)
可以使用 debug 模块调试变量值
- debug: |
变量
在 task 中可以使用变量。假设在 vars 那里定义了变量,可以这样使用它:
- hosts: webservers |
playbook 执行之前,默认会执行 setup 收集目标主机的 facts 信息,这是 play 中默认的第一个 task
ansible-playbook test.yml |
playbook 中的 task 可以使用 facts 中的变量的值
ansible k8s-master-nodes[0] -m setup -a "filter=ansible_distribution" |
指示命令或者模块在本地运行
在很多场景中,Ansible Playbook 中的某个任务需要在本地主机运行,而不是在远处主机上执行,要达到此目的,可以参考以下方法
- local_action 。
local_action是 Ansible 任务(Task)中的一个特殊关键字,它用于 在控制节点(localhost) 执行任务,而 不是在远程主机上执行 。通常,Ansible 任务默认是在hosts指定的远程主机上执行,但local_action让任务强制在本地执行,相当于delegate_to: localhost的简写。- name: 将远程文件列表写入本地文件
local_action:
module: copy
content: "{{ remote_file_list.stdout_lines | join('\n') }}"
dest: "./remote_files.txt"local_action基本语法等效于:- name: 在本地执行任务
local_action:
module: <模块名>
参数1: 值1
参数2: 值2- name: 在本地执行任务
action: <模块名> 参数1=值1 参数2=值2
delegate_to: localhost - delegate_to 指令。
delegate_to: localhost让任务在本地控制节点(localhost)执行,而不是远程主机 。- name: 将远程文件列表写入本地文件
copy:
content: "{{ remote_file_list.stdout_lines | join('\n') }}"
dest: "./remote_files.txt"
delegate_to: localhost
module
palybook 中的每个 task 都是为了运行某个 module。在 task 中配置 module,是以 module: options 的格式
- hosts: webservers |
handlers
handlers 中定义了在发生改动时执行的操作。
Handlers 也是一些 task 的列表,通过名字来引用,它们和一般的 task 并没有什么区别。Handlers 是由通知者进行 notify, 如果没有被 notify,handlers 不会执行。不管有多少个通知者进行了 notify,等到 play 中的所有 task 执行完成之后,handlers 也只会被执行一次。
handlers 会按照声明的顺序执行
Handlers 最佳的应用场景是用来重启服务,或者触发系统重启操作
notify
(当发生改动时)notify actions 会在 playbook 的每一个 task 结束时被触发,而且即使有多个不同的 task 通知改动的发生, notify actions 只会被触发一次。
- name: template configuration file |
when 语句
在 Ansible playbook 中,when 关键字用于条件执行任务。它允许你根据特定的条件来决定是否执行某个任务。这个功能非常强大,可以帮助你在不同的主机、不同的环境或不同的配置下灵活地执行任务。
when 表达式基于 Jinja2 模板语言,其中的变量主要来自: [1]
when 关键字后面跟随一个条件表达式,当条件为真时,任务会执行;当条件为假时,任务会被跳过。
tasks: |
when 关键字支持多种表达式,包括:
简单条件:
基于变量 的条件:
when: variable == 'value'- name: Install nginx only if nginx_install is true
apt:
name: nginx
state: present
when: nginx_install在这个示例中,
nginx_install是一个布尔变量。当nginx_install为真时,任务将执行。基于事实(facts) 的条件:
when: ansible_facts['os_family'] == 'Debian'- name: Install nginx on Debian systems
apt:
name: nginx
state: present
when: ansible_facts['os_family'] == 'Debian'
逻辑操作:
- 与操作:
when: condition1 and condition2当使用多个条件时,也先当于- name: Install nginx on Debian systems
apt:
name: nginx
state: present
when: ansible_facts['os_family'] == 'Debian' or ansible_facts['os_family'] == 'Ubuntu'and操作tasks:
- name: Shut down CentOS 6 systems
ansible.builtin.command: /sbin/shutdown -t now
when:
- ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "6" - 或操作:
when: condition1 or condition2 - 非操作:
when: not condition
- 与操作:
列表和字典操作:
- 列表包含:
when: 'item' in mylist- name: Ensure package is installed if it is in the list
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- git
when: item in packages_to_install - 字典键存在:
when: 'key' in mydict这个任务仅在- name: Run only if the key 'run_task' is present in mydict and its value is true
debug:
msg: "Running task"
when: mydict.get('run_task', False)mydict中存在键run_task且其值为真时执行。when: mydict.get('run_task', False)中False为默认值,如果mydict字典中不存在run_task键,mydict.get('run_task', False)将返回False。这种用法确保了在键不存在时,条件判断不会抛出错误。
- 列表包含:
复杂条件:
- 组合多个条件:
when: (condition1 and condition2) or condition3
- 组合多个条件:
以下是一个完整的示例
--- |
引用 Facts 变量值
Ansible-playbook 运行过程中,默认会收集目标主机的 Facts 信息。可以在 Playbook 定义中引用这些值 [2]
原始的 facts 信息可以通过 setup 模块获取
ansible <hostname> -m ansible.builtin.setup |
要在 playbook 或者 template 中引用,可以参考以下方法:
{{ ansible_facts['devices']['xvda']['model'] }} |
需要注意在 Playbook 引用 Facts 变量时,变量前缀无需
ansible_,否则会找不到变量
ansible playbook 常用模块参考
iptables
iptables模块用于配置 Linux iptables 防火墙 [1]iptables模块只修改当前系统生效的iptables规则(动态),不会将规则写入iptables服务的配置文件以实现静态(持久)配置,它的行为和iptables命令相同
iptables 模块参数参考 [1]
| 参数 | 说明 | 示例 |
|---|---|---|
action |
rule 是被 append 到最后还是插入到最前面。 默认 append如果 rule 已经存在,不会做变更 |
|
chain |
要操作的 chain。 可以是自定义的 chain 或者默认存在的 chain 如 INPUT, FORWARD, OUTPUT, PREROUTING, POSTROUTING, SECMARK or CONNSECMARK |
|
table |
This option specifies the packet matching table which the command should operate on | |
chain_management |
If true and state is present, the chain will be created if needed. If true and state is absent, the chain will be deleted if the only other parameter passed are chain and optionally table. 默认为 false |
|
comment |
规则的注释说明 | |
ctstate list / elements=string |
A list of the connection states to match in the conntrack module. Possible values are INVALID, NEW, ESTABLISHED, RELATED, UNTRACKED, SNAT, DNAT.Default: [] |
|
source |
Source specification | |
destination string |
Destination specification. Address can be either a network name, a hostname, a network IP address (with /mask), or a plain IP address. Hostnames will be resolved once only, before the rule is submitted to the kernel. Please note that specifying any name to be resolved with a remote query such as DNS is a really bad idea. The mask can be either a network mask or a plain number, specifying the number of 1’s at the left side of the network mask. Thus, a mask of 24 is equivalent to 255.255.255.0. A ! argument before the address specification inverts the sense of the address. |
|
destination_port string |
Destination port or port range specification | |
destination_ports list / elements=string |
This specifies multiple destination port numbers or port ranges to match in the multiport module |
|
dst_range string |
Specifies the destination IP range to match in the iprange module. | |
source_port |
Source port or port range specification. | |
flushboolean |
Flushes the specified table and chain of all rules.If no chain is specified then the entire table is purged.默认值 false |
|
jump |
This specifies the target of the rule | |
protocol |
The protocol of the rule or of the packet to check. The specified protocol can be one of tcp, udp, udplite, icmp, ipv6-icmp or icmpv6, esp, ah, sctp or the special keyword all |
|
rule_num |
Insert the rule as the given rule number. This works only with action=insert. |
lineinfile
lineinfile 用于以下场景:
- 确定文件中存在特定的一行,替换存在的行
- 修改文件中特定的一行
常用参数:
| 参数 | 说明 | 示例 |
|---|---|---|
state |
指定的行是否应该存在。 - absent- present 默认值 |
|
pathaliases: dest, destfile, namerequired |
要修改的目标文件 | |
line aliases: value |
要插入或者替换修改的行state=present 时为必须参数 如果 backrefs=true,可以使用 regexp 正则表达式捕获的值,使用 \g<1>、\g<2> 的方式引用捕获到的内容 |
|
backup boolean |
默认值 false 。是否创建备份文件 |
修改防火墙中某个 IP 示例
- name: Modify iptables rule in /etc/sysconfig/iptables |
playbook 示例
修改主机名
以下示例展示修改主机名可使用的 playbook,主机名称修改为 Inventory 中主机的主机名(Alias)
假设 Inventory 文件内容如下:
test_target1: |
Playbook 内容如下
--- |
相关模板变量说明
| 模板变量 | 说明 | 示例 |
|---|---|---|
inventory_hostname |
引用 Inventory 中的主机名称(Alias) | {{ inventory_hostname }} |
相关模块使用参考
| 模块 | 参考链接 | 示例 |
|---|---|---|
hostname |
hostname |
|
lineinfile |
lineinfile |
|
reboot |
reboot |
|
command |
command |
备份目录并删除
假如需要删除某个指定目录,可以参考以下配置,为保险起见,删除之前进行备份,并保留指定数量的备份文件
- name: Backup and Remove Directory |
同步 Nginx 服务配置文件
以下示例是一个 Ansible Playbook,用于 备份并同步 Nginx 配置 、 检查 Nginx 配置是否正确 (nginx -t) ,如果 配置正确,则重载 Nginx (nginx -s reload) 。
Nginx 配置到其他 Nginx |
以上的 Playbook 使用 Ansible 模块 copy 将管理主机上的目录拷贝到远程主机,实际使用可能面临以下问题:
- 文件太多时,copy 过程会比较慢(耗时较久)
- 无法保证远程主机上的内容和本地管理主机内容一致 ,比如出现远程主机存在文件,管理主机不存在
基于以上问题,推荐使用 Ansible 模块 synchronize 进行同步,synchronize 模块是 rsync 的封装,可以实现基本的 rsync 功能 ,相较于 copy 模块,其同步速度非常快,同时可以使用 delete: True 选项实现 删除目标路径中(dest)存在而源路径(src)中不存在的文件,保证源(src)和目标(dest)中的内容完全一致
使用
synchronize模块需要确保管理主机和远程主机上都存在rsync程序
默认使用rsync的ssh模式连接远程主机,ssh端口信息来自 Inventory 中的配置
以下 Playbook 使用 synchronize 同步 Nginx 配置文件
--- |
参考链接
Ansible playbooks
ansible 官方文档
Ansible中文权威指南