Ansible 插件是使用 Python 编写的代码块,它们在 Ansible 执行的特定生命周期中被调用,用于扩展或修改 Ansible 的核心功能。

你可以把 Ansible 想象成一个功能强大的汽车,而 Python 插件就像是各种可以安装的“模块”或“配件”,比如更强的引擎、更智能的导航系统或更酷炫的灯光,这些插件让这辆汽车能适应更多场景,完成更多任务。
为什么需要 Ansible 插件?
Ansible 的设计哲学是“无代理”(Agentless),通过 SSH 等协议管理远程节点,但为了保持核心的轻量级和灵活性,很多高级或特定领域的功能被实现为插件,使用插件的好处包括:
- 功能扩展:Ansible 核心无法预知所有需求,插件允许你添加连接方式(如连接到网络设备)、过滤数据(如处理 JSON)、加载变量(如从数据库读取)等。
- 代码复用:将复杂的逻辑封装在插件中,可以在多个 Playbook 和角色中重复使用,避免代码冗余。
- 核心稳定性:将实验性或特定于某个环境的功能放在插件中,不影响 Ansible 核心的稳定性和发布周期。
- 社区贡献:任何人都可以编写插件并为 Ansible 生态做贡献,极大地丰富了 Ansible 的功能库。
Ansible 插件的类型
Ansible 插件种类繁多,每种类型都在 Ansible 运行的不同阶段发挥作用,以下是最常见和最重要的一些类型:
连接插件
这是最基础的插件类型,定义了 Ansible 如何与被管理节点通信。

- 作用:建立和管理与远程主机的连接。
- 默认插件:
paramiko(基于 Python 的 SSH)、ssh(调用系统原生 SSH 命令,性能更好)。 - 其他示例:
local: 在本地执行,不通过 SSH。docker: 在 Docker 容器内执行。network_cli: 用于与网络设备(如 Cisco, Juniper)建立持久性 CLI 连接。winrm: 用于连接 Windows 节点。
过滤器插件
Jinja2 模板系统的一部分,用于处理和转换变量数据。
- 作用:在模板或 Playbook 中对变量进行格式化、计算、转换等操作。
- 核心示例:
{{ my_list | unique }}: 去重。{{ 'hello world' | upper }}: 转换为大写。{{ my_dict | dict2items }}: 将字典转换为列表。
- 自定义示例:你可以编写一个自定义过滤器,
{{ my_ip | is_valid_ip }}来验证 IP 地址格式。
测试插件
同样是 Jinja2 模板系统的一部分,用于判断变量的值或状态。
- 作用:在
if语句中作为条件判断。 - 核心示例:
{% if my_string is defined %}: 检查变量是否已定义。{% if my_list is empty %}: 检查列表是否为空。{% if my_number is even %}: 检查数字是否为偶数。
- 自定义示例:编写一个测试插件
{% if my_port is port_in_use %}来检查某个端口是否被占用。
Lookup 插件
用于在 Playbook 执行期间从外部源检索数据。
- 作用:获取动态数据,并将其注入到变量中。
- 核心示例:
{{ lookup('file', '/path/to/file.txt') }}: 读取文件内容。{{ lookup('env', 'HOME') }}: 获取环境变量。{{ lookup('password', 'chars=ascii,digits,length=8') }}: 生成一个随机密码。
- 高级示例:
{{ lookup('redis', 'key') }}: 从 Redis 数据库获取值。{{ lookup('dnstxt', 'example.com') }}: 查询 DNS TXT 记录。{{ lookup('k8s', 'my-pod', namespace='default') }}: 从 Kubernetes API 获取 Pod 信息。
回调插件
这是最强大的插件之一,允许你监听和干预 Ansible 的整个执行过程。

- 作用:在任务执行前、执行后、失败时等事件点触发自定义逻辑,例如发送通知、记录日志、生成自定义报告等。
- 示例:
ansible.cfg中配置callback_plugins = /path/to/callback_plugins。- 编写一个回调插件,在任何一个任务失败时,自动向 Slack 或钉钉发送报警消息。
- 编写一个回调插件,在 Playbook 执行结束后,生成一个详细的 HTML 格式的报告。
动作插件
位于模块和核心执行引擎之间,可以拦截、修改模块的参数或结果。
- 作用:在模块调用前后进行“劫持”和处理,实现“胶水”逻辑。
- 示例:
ansible.builtin.async_status就是一个动作插件,它专门用来检查异步任务的执行状态。- 可以编写一个动作插件,在调用
yum模块前,自动添加一个-y参数,或者在调用后自动记录安装的包列表。
其他类型插件
- Cache 插件:用于缓存 Facts 和其他数据,避免重复收集,提高执行速度(如
jsonfile,redis)。 - Vars 插件:用于从特定来源动态加载变量(如从文件、数据库、云 API 加载)。
- Terminal 插件:与
network_cli连接插件配合,用于处理与网络设备的交互逻辑。
如何编写一个简单的 Python 插件?(以回调插件为例)
下面我们通过一个简单的“Hello World”回调插件,来感受一下编写过程。
目标:在 Ansible 执行完每个任务后,打印一条消息。
创建插件目录结构
你需要一个目录来存放你的插件,推荐在你的项目根目录下创建。
my_ansible_project/
├── ansible.cfg
├── inventory
└── plugins/
└── callback_plugins/
└── hello.py
配置 ansible.cfg
告诉 Ansible 你的插件在哪里,在 ansible.cfg 文件中添加:
[defaults] callback_plugins = ./plugins/callback_plugins
编写 Python 插件代码 (plugins/callback_plugins/hello.py)
Ansible 插件是标准的 Python 类,回调插件需要继承 ansible.plugins.callback.CallbackBase。
# plugins/callback_plugins/hello.py
from ansible.plugins.callback import CallbackBase
import time
# Ansible 会自动加载所有继承自 CallbackBase 的类
class CallbackModule(CallbackBase):
"""
这是一个简单的 Hello World 回调插件。
它会在每个任务执行后打印一条消息。
"""
# v2_playbook_on_task_ok 方法会在任务成功执行后被调用
def v2_playbook_on_task_ok(self, task):
# 获取任务名称
task_name = task.get_name().strip()
# 获取执行时间
execution_time = "%.2f" % task.duration
print(f"\n[Hello Callback] 任务 '{task_name}' 执行成功!耗时: {execution_time} 秒。")
# 必须调用父类的方法,否则会中断 Ansible 的正常流程
super().v2_playbook_on_task_ok(task)
# v2_playbook_on_task_failed 方法会在任务失败后被调用
def v2_playbook_on_task_failed(self, task, **kwargs):
task_name = task.get_name().strip()
print(f"\n[Hello Callback] 警告!任务 '{task_name}' 执行失败!")
# 同样,调用父类方法
super().v2_playbook_on_task_failed(task, **kwargs)
使用插件
创建一个简单的 Playbook 来测试它。
---
- name: Test Callback Plugin
hosts: localhost
connection: local
gather_facts: no
tasks:
- name: Task 1 - A successful task
ansible.builtin.debug:
msg: "This will succeed"
- name: Task 2 - A failing task
ansible.builtin.command: /bin/false # 这个命令会失败
ignore_errors: true # 我们故意让它失败,但不中断 Playbook
- name: Task 3 - Another successful task
ansible.builtin.debug:
msg: "This will also succeed"
运行并观察结果
ansible-playbook test_playbook.yml
你会看到类似下面的输出,注意观察我们自定义的回调信息是如何被插入到标准输出中的:
TASK [Task 1 - A successful task] ***************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "This will succeed"
}
[Hello Callback] 任务 'Task 1 - A successful task' 执行成功!耗时: 0.01 秒。
TASK [Task 2 - A failing task] ****************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["/bin/false"], "delta": "0:00:00.001993", "end": "2025-10-27T10:30:00.123456", "msg": "non-zero return code", "rc": 1, "start": "2025-10-27T10:30:00.121463", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
[Hello Callback] 警告!任务 'Task 2 - A failing task' 执行失败!
... (忽略 ignore_errors 的处理) ...
TASK [Task 3 - Another successful task] ********************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "This will also succeed"
}
[Hello Callback] 任务 'Task 3 - Another successful task' 执行成功!耗时: 0.00 秒。
总结与学习资源
| 特性 | 描述 |
|---|---|
| 语言 | Python,几乎所有的 Ansible 插件都是用 Python 编写的。 |
| 位置 | 通常存放在 ~/.ansible/plugins/ (全局) 或项目目录下的 plugins/ (项目级)。 |
| 加载 | Ansible 会自动扫描配置的插件目录,并动态加载符合条件的插件。 |
| 核心 | 理解 Ansible 的生命周期(如 Playbook 开始、任务执行、结束等)是编写高级插件的关键。 |
如何深入学习?
-
官方文档是最佳起点:
- Developing Plugins: 官方开发者指南,介绍了所有插件类型的编写方法。
- Callbacks: 回调插件的官方用户指南。
-
阅读源码:
- Ansible 自身包含了大量的插件实现,阅读它们的源码是最好的学习方式,源码可以在 Ansible GitHub 仓库 中找到。
-
社区贡献:
查看 Ansible Galaxy 或 GitHub 上其他人贡献的插件,学习他们的实现思路。
掌握 Ansible 插件的开发,意味着你不再仅仅是 Ansible 的使用者,而是成为了它的“赋能者”,可以根据自己的具体需求,无限地扩展其能力边界。
