本节摘要:模块是 Ansible 的原子能力单元:每模块一个职责、参数化输入、幂等输出、JSON 返回。本节建立模块的分类地图,示范 ansible-doc 的现查现用法,讲清 command、shell、raw 三个"逃生舱口"的正确使用姿势,以及 changed 判定背后的通用逻辑。
不管多少个模块,形态完全一致:接收参数,检查目标状态,必要时把它调整到位,返回 JSON。这个统一形态意味着三件事。第一,学会一个模块的读法等于学会所有模块的读法——参数区、返回区、示例区的文档结构到处相同。第二,模块之间可以低风险替换:今天用 apt 装包,明天迁到 Rocky 系统换成 dnf,剧本其余部分不动。第三,幂等是模块对调用者的契约,changed 与 ok 的区分就是这份契约的可见形式。
# 模块的通用心智模型(伪代码) def 模块(参数): 当前 = 读取目标系统现状 期望 = 参数描述的状态 if 当前 == 期望: return {"changed": False} # ok:什么都不做 if 检查模式: # --check 传入时 return {"changed": True, "msg": "将变更"} 执行变更 return {"changed": True, "diff": 变更前后对比}
几百个模块不必逐个记,按用途分七类建立索引就够用了。
系统与用户 user / group / hostname / sysctl / cron / reboot 文件与模板 copy / template / file / lineinfile / blockinfile / fetch / stat / find 包管理 package(通用) / apt / dnf / yum / pip / gem 服务与进程 service(通用) / systemd / supervisorctl 命令执行 command / shell / raw / script(逃生舱口) 云与容器 各云厂商集合 / docker / kubernetes.core 工具与编排 debug / assert / wait_for / uri / set_fact / fail
两个通用模块值得点名。package 是包管理的抽象层:底层自动探测发行版选择 apt 或 dnf。写跨发行版剧本时先用 package,只有需要发行版专属参数时才下沉到具体模块。service 同理,抽象了 systemd、SysV、initrc 等服务管理后端。抽象层的存在让你可以把"发行版细节"推迟到最后才处理。
真正的工作流是三步:先用 ansible-doc -l 按关键词搜(配合 grep),再用 ansible-doc 模块名看完整文档(参数表、备注、示例),写的时候只记得参数大意即可,具体参数名让文档纠正你:
# 三步查文档 ansible-doc -l | grep -i selinux # 第一步:按关键词搜 ansible-doc ansible.posix.selinux # 第二步:读参数与示例 ansible-doc -s ansible.posix.selinux # 第三步:只看参数速查(写剧本时用)
另一个常被忽视的文档来源是模块返回值区:文档不仅说参数,还说返回哪些字段。第 2.2 节的变量渲染事故里,"引用模块返回值"是高频操作,返回值区就是合法字段的权威清单。用 register 接住结果后能引用什么,查这里。
总有模块覆盖不到的时刻:私有工具的 CLI、一次性迁移命令、老旧系统上没有 Python。三个逃生舱口按"受控程度"排序。command 最受控:直接调用可执行文件、不经 shell 解析、天然没有管道与通配符展开。shell 经 /bin/sh 解析整行,管道、重定向、变量展开都能用——自由度换来的是引号转义与注入风险。raw 在远端没有 Python 时也能跑,通过 SSH 原始通道执行,是裸机的最后一根稻草。
# command:首选逃生舱,参数列表形式,无 shell 解析 - name: 生成报表(私有工具) ansible.builtin.command: cmd: /opt/stardust/bin/report-gen --date 2024-06-01 register: rpt changed_when: "'generated' in rpt.stdout" # shell:确实需要管道时才用 - name: 统计今天的错误日志行数 ansible.builtin.shell: "grep -c ERROR /var/log/app.log || true" args: executable: /bin/bash # raw:远端连 Python 都没有时 - name: 裸机预装 Python(bootstrap 场景) ansible.builtin.raw: "apt-get update && apt-get install -y python3"
逃生舱口的核心纪律是补幂等判断。它们不检查状态、每次都执行、每次都报 changed。补救有两个开关:changed_when 按输出判定"这次算不算变更",creates 参数声明"产物文件已存在就跳过"。上面的示例里 changed_when 让"重复生成同样报表"回归 ok 状态——这是让逃生舱口融入幂等世界的关键一笔,3.6 节会把这套手法展开成完整模式。
对多数资源模块,判定逻辑就是本节开头的伪代码:比对现状与期望。两个增强能力值得知道。diff 模式(--diff 参数)让模块返回变更前后对比,模板与 lineinfile 的输出尤其直观,评审时把 --diff 输出贴出来比文字描述有效得多。check 模式(--check)全剧本干跑:模块只报告"将要变更"而不真正执行,是发布前的最后一道人工确认手段。注意 check 模式下依赖执行结果的条件任务(如 register 后判断)可能拿到空数据,解读干跑输出时要留这个心眼。

遇到需求时的选择顺序建议是:先找资源模块(有没有管这类资源的专用模块),再找抽象层(package、service),实在没有才用逃生舱口并补幂等判断。反过来从 shell 开始写、等出问题再换模块的路子,返工成本高得多——一个真实代价是 shell 版本的脚本永远拿不到 diff、check 干跑和结构化返回这三种基础设施能力。
register 拿到返回值之后的用法,决定剧本的表达力上限。三个进阶招式。招式一,结果驱动分支:register 的 stdout_lines 配合 when 做条件判断,"检查命令输出再决定动作"的探测式任务全靠它——注意搭配 changed_when: false 让探测本身不计为变更。招式二,结果二次加工:返回值是结构化数据,过滤器直接可用——"所有失败主机的列表"就是各主机 register 结果按 rc 过滤后的集合,跨主机聚合配合 hostvars 即可表达。招式三,结果作为下游输入:模板渲染、循环列表、另一任务的参数,都可以来自前一任务的返回值,任务链因此能表达"先探测再按实况行动"的智能行为。三招的共同前提是读返回值文档:每个模块的返回区列出了全部字段与类型,register 之后能引用什么,以文档为准而不是以猜为准。用熟三招后,剧本会明显从"固定流程"走向"有感知的流程"——这是模块层能给你的最大表达力红利。