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. | |
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中文权威指南