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 文件是否有语法错误 | |
-i INVENTORY, --inventory INVENTORY, --inventory-file INVENTORY |
在 ansible-playbook 命令行中指定使用那个 Inventory 文件 |
|
-l SUBSET, --limit SUBSET |
只能在 playbook 已经选定的 hosts 范围内做进一步过滤 |
主机与用户
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: localhostdelegate_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. | |
flush boolean |
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 默认值 |
|
path aliases: dest , destfile , name required |
要修改的目标文件 | |
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中文权威指南