模块与插件的分工
Ansible 的扩展分两大类:模块(module)会被送到被管机上执行,用来改变目标状态;插件(plugin)在控制节点上运行,负责查找、过滤、回调、连接、清单解析等。判断该写哪一种,只要问“这段逻辑要访问谁的环境”:需要读写目标机文件与服务就写模块,只处理控制节点上的数据与输出就写插件。
基本信息
| 运行时位置 | 模块在被管机执行;插件在控制节点执行 |
|---|---|
| 模块语言 | 支持多种语言,实践中以 Python 为主,可直接使用参数校验框架 |
| 放置位置 | 项目内 library/ 目录、角色的 library/,或集合中的 plugins/modules |
| 共享代码 | 放在 module_utils 中供多个模块复用,避免复制粘贴 |
| 自描述 | 模块内以文档字符串声明参数与返回值,ansible-doc 与参数校验都依赖它 |
| 分发方式 | 单文件适合内部临时使用,长期维护建议按集合打包版本化 |
典型使用场景
- 内部系统对接:封装公司自有 API,让任务写法与其它模块一致
- 现有模块覆盖不到的场景:某个私有命令需要结构化参数与返回值
- 重复的脚本逻辑:把散落在 shell 任务里的判断搬到模块里,统一错误处理
- 需要结构化返回:命令类任务的输出是文本,模块可以返回字段供后续判断
- 权限与审计:把高风险操作集中到模块中,便于审查与复用
- 逐步演进:先用命令类任务验证思路,稳定后固化成模块
模块骨架要点
模块骨架要点
# 结构(按集合打包时的典型位置)
# plugins/modules/app_config.py 模块本体
# plugins/module_utils/app_client.py 可复用的共享逻辑
#
# 模块内部要处理的三件事:
# 1) 参数声明与校验:交给参数校验框架,缺参、类型错在入口就被拦下
# 2) 幂等判断:做成期望状态,先将现状与目标比较,一致则直接返回无变更
# 3) 结构化返回:返回字段固定且文档化,供 register 与后续条件使用
#
# 模块文档字符串需包含参数说明、示例与返回值说明,
# 否则 ansible-doc 查不到,参数校验也无法工作。
#
# 本地调试:写好单个模块后按文档方式单独执行一次,
# 确认返回是合法结果而不是报错文本。扩展类型选择
| 需求 | 应写什么 | 说明 |
|---|---|---|
| 改目标机上的配置或服务 | 模块 | 送到被管机执行,需考虑目标机解释器 |
| 自定义变量取值方式 | lookup 插件 | 运行在控制节点,用于读取外部数据源 |
| 在模板里加工数据 | filter 或 test 插件 | 扩展 Jinja2 表达式能力 |
| 改变执行输出样式 | callback 插件 | 影响日志与展示,不改变执行结果 |
| 对接特殊传输方式 | connection 插件 | 例如自定义隧道或专有协议 |
| 从特殊来源生成主机清单 | inventory 插件 | 动态获取主机与分组信息 |
写模块前先确认现有模块是否够用:许多看似需要自研的场景,实际上用声明式模块加少量判断就能覆盖。自研模块的长期成本在维护与文档,它与 Ansible 版本、目标机解释器都要保持兼容。
模块在执行时接收的是外部输入,参数必须做校验并按最小权限使用,尤其涉及路径与命令拼接时,未校验的参数可能变成任意命令执行。同时注意敏感参数会被记录到执行输出里,确有必要时用 no_log 抑制。
演进路径
合理的路线是先写一个能跑通的最小模块,只处理一个动作并保证幂等与结构化返回;稳定之后再抽出共享逻辑到 module_utils,最后按集合打包、加版本号与文档,供多个仓库引用。跳过前两步直接写大而全的模块,往往会因为调试困难而半途而废。