1. 为什么会出现 ansible.builtin 和 ansible.posix 两个命名空间
1.1 Ansible 2.10 之后的模块拆分
如果你和我一样是从 Ansible 2.9 一路用过来的,第一次看到ansible.builtin和ansible.posix的时候大概率会愣一下:这俩看起来都像“Ansible 官方出的东西”,凭什么要分两个名字?尤其当你看到文档里的示例一会儿写ansible.builtin.copy,一会儿写ansible.posix.sysctl,很容易觉得自己是不是漏学了什么。
其实答案并不复杂。Ansible 2.10 之后,原来的“一大包模块”不再全部堆在 ansible-base 里面,而是按照场景拆分成了很多集合(Collection)。这个拆分的本质,是把原本混在一起的代码重新组织成可独立发布、独立版本、独立维护的包。你可以把它理解成一套工具箱被重装了:以前所有工具都挂在一块木板上,想要啥直接拿;现在分成了好几个抽屉,每个抽屉负责一类任务。
ansible.builtin就是那个始终放在手边的“基础抽屉”,里面是 Ansible 核心引擎直接依赖、并且随 ansible-core 一起发布的模块。而ansible.posix是新拆出来的“POSIX 系统配置抽屉”,专门放那些和 Linux/Unix 系统底层配置强相关的模块。
1.2 ansible.builtin 到底是什么
ansible.builtin不是某个可以在 Galaxy 上单独下载的包,它是 ansible-core 的一部分。换句话说,只要你安装了 ansible-core,ansible.builtin里的模块就已经可用了,不需要额外安装,也装不了、卸不掉。
它包含的东西非常杂,但都是“通用自动化操作”的底子,比如:
- 执行命令:
ansible.builtin.command、ansible.builtin.shell、ansible.builtin.script - 文件操作:
ansible.builtin.file、ansible.builtin.copy、ansible.builtin.template、ansible.builtin.lineinfile - 软件包管理:
ansible.builtin.apt、ansible.builtin.dnf、ansible.builtin.yum、ansible.builtin.package - 服务管理:
ansible.builtin.service、ansible.builtin.systemd、ansible.builtin.sysvinit - 流程控制:
ansible.builtin.debug、ansible.builtin.assert、ansible.builtin.set_fact、ansible.builtin.include_tasks
所以,ansible.builtin更像是“Ansible 这个语言本身的标准库”。哪怕你后面的 playbook 一个第三方集合都不用,靠这些模块已经能完成绝大多数日常任务。
1.3 ansible.posix 到底是什么
ansible.posix是 Red Hat 维护的一个独立集合,随 Ansible 社区发行版默认安装,但如果你用的是最小化安装的ansible-core,它不一定在环境里。
这个集合的定位非常明确:处理 POSIX 系统层面的专项配置。看名字里有 POSIX,但别被名字骗了,它不是要覆盖所有类 Unix 系统,而是收编了那些“跟系统底层工具强相关”的模块,比如管理挂载点、调整内核参数、处理 SSH 授权密钥、控制 SELinux 状态、配置防火墙、设置 ACL 等等。
常见模块包括:
ansible.posix.mount:管理挂载点和 fstabansible.posix.sysctl:管理内核参数ansible.posix.authorized_key:管理 SSH 公钥授权ansible.posix.firewalld:管理 firewalld 防火墙规则ansible.posix.acl:管理 POSIX ACLansible.posix.synchronize:包装 rsync 做文件同步ansible.posix.selinux、ansible.posix.seboolean、ansible.posix.sefcontext:SELinux 相关配置
正是因为这些模块看起来都很“系统”,如果你之前只熟悉老版本 Ansible,会误以为它们本来就该属于ansible.builtin。实际上在 2.10 拆分时,它们被明确归到了ansible.posix这个集合里。
2. 一张表看懂 ansible.builtin 和 ansible.posix 的核心差异
2.1 核心对比表
下面这张表基本可以覆盖日常判断时需要用到的信息:
| 维度 | ansible.builtin | ansible.posix |
|---|---|---|
| 定位 | Ansible 核心引擎自带的标准库 | POSIX 系统专项配置集合 |
| 安装方式 | 随 ansible-core 自动提供,不可单独安装 | 随 Ansible 发行版默认安装,也可用 galaxy 单独安装 |
| 版本管理 | 跟随 ansible-core 版本 | 独立版本号,可单独升级 |
| 维护方 | Ansible Core Team | Red Hat / Ansible Collections 团队 |
| 典型模块 | copy、file、command、shell、apt、service | mount、sysctl、authorized_key、firewalld、acl、synchronize |
| 依赖 | 大多数基于 Python 标准库和常见系统命令 | 更依赖外部系统工具,如 mount、sysctl、rsync、patch、firewall-cmd |
| 适用范围 | 通用任务、流程控制、基础文件/包/服务操作 | Linux/Unix 系统配置专项操作 |
| 升级风险 | 必须随 ansible-core 一起评估 | 可以在 requirements.yml 中单独锁定版本 |
这张表说明了一个关键点:这不是“新写法”和“旧写法”的对比,而是“基础库”和“系统工具集合”的对比。ansible.builtin永远都在,ansible.posix则需要考虑是否随环境安装。
2.2 ansible.posix 里具体有哪些模块
我接触过的项目里,出现频率最高的ansible.posix模块大概是下面这些。记住这个清单,你遇到“系统配置类任务”时就知道该往哪里找了:
| 模块 | 作用 | 依赖的外部工具 |
|---|---|---|
ansible.posix.sysctl | 管理/etc/sysctl.conf或/etc/sysctl.d/下的内核参数 | sysctl |
ansible.posix.mount | 管理 fstab 挂载条目和当前挂载状态 | mount、umount、findmnt 等 |
ansible.posix.authorized_key | 管理用户的~/.ssh/authorized_keys文件 | ssh-keygen 等 |
ansible.posix.firewalld | 管理 firewalld 的端口、服务、富规则 | firewall-cmd |
ansible.posix.acl | 给文件/目录设置 POSIX ACL | setfacl、getfacl |
ansible.posix.synchronize | 封装 rsync 做增量同步 | rsync |
ansible.posix.patch | 使用 patch 命令应用补丁文件 | patch |
ansible.posix.at | 通过 atd 调度一次性任务 | at |
ansible.posix.selinux | 修改 SELinux 状态和策略 | SELinux 相关 Python 绑定 |
ansible.posix.seboolean | 切换 SELinux boolean | setsebool 等 |
ansible.posix.sefcontext | 管理 SELinux 文件上下文映射 | semanage 等 |
实际项目中不一定要全用上,但知道边界很重要:如果某个任务的官方文档写的是ansible.posix.xxx,但你安装的只是ansible-core,那你必须先确认这个集合是否在当前执行环境里。
2.3 ansible.builtin 里最常用的模块
ansible.builtin的模块数量很多,背下来不现实,但可以按照职能分类记:
- 文件与内容:
copy、template、file、lineinfile、blockinfile、stat、find - 命令与执行:
command、shell、script、raw、expect - 包管理:
package、apt、dnf、yum、pip - 服务与进程:
service、systemd、sysvinit、service_facts - 流程控制与变量:
debug、assert、fail、set_fact、include_vars、include_tasks、import_tasks、block、rescue - 远程信息:
gather_facts、slurp、fetch、wait_for - 网络与下载:
get_url、uri
这些模块解决的问题更偏“Ansible 任务本身”:文件要拷过去、包要装好、服务要起来、命令要在远端执行。它们不一定非要是某个操作系统特有的能力,而是各种场景下都会用到的基础操作。
3. 同一个任务,到底选哪一个:边界和选型逻辑
3.1 先用“操作对象”划边界
我的一个简单判断方法是:先看这个任务是在操作“Ansible 能控制的东西”,还是在操作“操作系统本身的配置”。
如果是前者,比如我要复制配置文件、安装软件包、启动服务,那就优先用ansible.builtin。它的模块足够泛化,跨 Linux 发行版时会尽量帮你抹平差异。
如果是后者,比如我要改内核参数、改 fstab、配 firewalld、设置 SSH authorized_keys、应用 AC L规则,那就去看ansible.posix。因为这类操作往往直接和系统里的某个工具对应,写进ansible.posix反而更合理。
举个例子,复制一个 nginx 配置到目标机器,用ansible.builtin.copy就够了。但如果想让某个目录对某个用户开放可读权限,而且这个权限不是简单的755能表达的,那我就会考虑ansible.posix.acl。
3.2 三组容易被误用的场景
我以前踩过几个坑,也见过同事在这几个地方犹豫,这里单独拿出来说一下。
第一组是ansible.builtin.file和ansible.posix.acl的选择。file模块能设置 owner、group、mode,但它处理的是传统权限模型。如果你需要让“某个特定用户”对某个目录拥有读权限,同时不影响其他用户权限,就不可能只靠mode实现。这种时候用ansible.posix.acl就非常合适。
第二组是手动管理 SSH 公钥和ansible.posix.authorized_key的选择。有些人会用copy或者template把整个authorized_keys文件覆盖上去。这种做法在“一次性初始化”时没问题,但如果你在多台机器上持续管理,很容易出现两个问题:一是把别人机器上已有的管理员公钥覆盖掉,二是重复执行任务时文件内容可能不一致。authorized_key模块会自己维护文件结构,保证幂等,而且可以精确地只添加或删除某一个公钥。所以我后来只要涉及 SSH 公钥管理,一律用ansible.posix.authorized_key。
第三组是用command还是mount来写挂载。早期我看到过有人在 playbook 里用shell直接执行echo "..." >> /etc/fstab,然后又用mount -a。这种写法风险非常高:重复执行会写重复条目,fstab 写错会导致机器重启起不来,而且完全没有任何校验。ansible.posix.mount这个模块虽然也要调系统命令,但它会管理 fstab 的条目格式,能避免重复,还能在检查模式下预览变更,这是 shell 加 echo 完全没有的保障。
3.3 三条选型规则
如果你不想每条任务都翻文档,可以直接按这三条规则来判断:
- 只要是“Ansible 引擎层面的动作”,比如执行命令、拷贝文件、设置变量、控制流程、安装包、启停服务,默认找
ansible.builtin。 - 只要是“系统专项配置”,比如内核参数、挂载、防火墙、SSH 公钥、ACL、SELinux,优先去
ansible.posix里查一下有没有对应模块。 - 如果
ansible.posix里没有,再考虑其他专业集合,比如 Windows 相关任务找ansible.windows,网络设备任务找对应的厂商集合。
这套规则能解决 90% 的纠结。剩下 10% 的边界情况,以官方文档的模块归属和版本说明为准。
4. 从短模块名到 FQCN:两种写法和它们的兼容层
4.1 短模块名为什么还能用:ansible.legacy 路由
很多人升级到新版 Ansible 后,发现自己还在写authorized_key:、mount:这种短模块名,而且居然能跑。这不是魔法,是 Ansible 保留了一个名为ansible.legacy的兼容路由层。
当你没有在 playbook 里声明任何集合,直接写mount:时,Ansible 会按兼容规则找到ansible.legacy.mount,然后通过路由表映射到实际模块,也就是ansible.posix.mount。这样做的好处是旧 playbook 不会被立刻打破。坏处是,如果你用短模块名但没有安装ansible.posix,很可能报错得很突然,而且报错信息不一定立刻告诉你要装哪个集合。
所以我的建议是:旧项目可以暂时保留短模块名,但新项目尽量用 FQCN。所谓 FQCN,就是完全限定模块名,例如ansible.posix.mount、ansible.builtin.copy。它最大的价值是一眼就能看出模块属于哪个集合,别人接手你的 playbook 时也不会猜“这个 mount 是哪来的”。
4.2 推荐写法一:直接写 FQCN
如果某个 playbook 里只用了一两个ansible.posix模块,我会直接写 FQCN,不用额外声明collections。比如:
- hosts: all become: true tasks: - name: 开启 IP 转发 ansible.posix.sysctl: name: net.ipv4.ip_forward value: '1' sysctl_set: true reload: true - name: 部署 SSH 公钥 ansible.posix.authorized_key: user: deploy key: "{{ lookup('file', 'files/deploy.pub') }}" state: present这种写法的好处是显式、可靠。哪怕是完全陌生的环境,看到ansible.posix.sysctl,也能立刻知道这个任务需要安装ansible.posix集合。
4.3 推荐写法二:playbook 级 collections 声明
如果整个 playbook 里有十几个ansible.posix模块,每个任务都写ansible.posix.前缀会非常啰嗦。这时候可以在 playbook 顶部声明collections,然后继续用短模块名:
- hosts: all become: true collections: - ansible.posix tasks: - name: 设置内核参数 sysctl: name: net.ipv4.ip_forward value: '1' - name: 挂载 NFS mount: path: /mnt/data src: 192.168.1.10:/srv/data fstype: nfs opts: rw,sync state: mounted需要注意的是,collections声明的作用范围是当前 playbook。如果某个角色内部也用到了sysctl,最好在角色的 meta 配置里也声明集合,或者直接写 FQCN,否则会出现“模块找不到”的尴尬。
4.4 放到版本库里:requirements.yml 与执行环境
如果是团队项目,我强烈建议把集合依赖写进collections/requirements.yml,而不是只在某台机器上手动装。比如:
collections: - name: ansible.posix version: ">=1.5.0"然后执行:
ansible-galaxy collection install -r collections/requirements.yml如果你们用的是 AWX / Automation Controller 这类带执行环境的产品,也要把ansible.posix加进执行环境的构建文件里。因为执行环境是一个相对干净、可控的镜像,不会自动包含你本地手动安装的集合。把依赖写进 requirements.yml,再配合构建执行环境,才不会出现“本机能跑,构建环境里跑不了”的情况。
5. 升级后最常见的坑和我怎么排查
5.1 “couldn't resolve module/action 'ansible.posix.xxx'”的处理
这是我在 ansible-core 裸安装环境里最常遇到的报错。你写好了ansible.posix.sysctl,执行时却提示无法解析某个模块。原因很简单:当前环境没有安装ansible.posix。
排查步骤通常是这样的:
先确认集合是否真的没有安装:
ansible-galaxy collection list | grep posix如果没有输出,直接安装:
ansible-galaxy collection install ansible.posix安装完再看一眼能不能在 ansible-doc 里找到:
ansible-doc -l ansible.posix | grep sysctl如果是在 ansible-core 环境里跑,并且你不确定 Ansible 发行版里是否默认包含这个集合,那就以上面的命令输出为准。不要想当然认为“Ansible 装了就应该有”。
5.2 用短模块名但没装 posix 集合
还有一类坑是:playbook 里写的是sysctl:这种短模块名,但执行环境里没有ansible.posix。由于短模块名依赖ansible.legacy兼容路由,一旦对应集合缺失,报错信息可能比较绕,不一定直接告诉你“去装 ansible.posix”。
我的排查习惯是三步走。
第一步,看报错里有没有“couldn't resolve module”这类关键词。第二步,去ansible-galaxy collection list看关键集合是否齐全。第三步,如果缺失,安装后再跑。如果你希望在报错一开始就明确一点,那就老老实实用 FQCN,至少报错时能直接告诉你缺的是哪个集合。
5.3 两个容易写错的参数习惯
排错之外,参数也容易踩坑。我讲两个最典型的。
第一个是ansible.posix.sysctl的value参数。看起来应该是整数,但写代码时最好用字符串。比如:
ansible.posix.sysctl: name: net.ipv4.ip_forward value: '1'为什么?因为 YAML 本身在某些场景下会把数字当成不同进制解析,尤其是一些带前导零的权限位或者掩码值。用字符串能避免这种隐藏的类型转换问题。
第二个是ansible.posix.firewalld的permanent和immediate参数。如果只写permanent: true,规则会持久化到配置,但当前会话不一定立刻生效;如果只写immediate: true,当前会生效,重启后可能又没了。大多数时候你需要两个都写:
ansible.posix.firewalld: port: 8080/tcp state: enabled permanent: true immediate: true这个细节在排障时经常被忽略,但实际影响非常大:你以为规则加上了,结果服务重启后防火墙又回到原来的状态。
6. 我的使用习惯和一些补充说明
6.1 项目里的最小配套
现在我自己写 playbook 时,默认会做几个固定动作。第一,新写的内容一律用 FQCN,不靠短模块名的兼容层。第二,涉及ansible.posix模块时,先把集合依赖写进collections/requirements.yml,避免别人拉代码后少装集合。第三,在ansible.cfg里设置好collections_path,如果是项目级安装,通常会指向项目内部的collections/目录:
ansible-galaxy collection install ansible.posix -p ./collections[defaults] collections_path = ./collections这样做之后,即使我换了服务器、换了执行环境,也能很快把整套依赖恢复出来。
6.2 什么时候我仍然刻意不用 posix
不过我也不是所有系统配置都会无脑上ansible.posix。如果目标机器环境被压得很小,比如容器刚起来、连 rsync 都没有,又或者只是临时执行一次简单任务,我会优先考虑ansible.builtin里的基础模块。比如文件拷贝用copy,命令执行用command,而不是为了用某个模块额外引入一整包依赖。
但这只是一个“省事”的取舍,不是长期方案。真正需要稳定管理的系统配置,比如挂载、内核参数、SSH 公钥,最终还是要落到ansible.posix上来做,因为它能把“系统工具调用”变成“可重复、可检查、幂等”的 Ansible 任务。
如果你现在正处在 Ansible 2.9 到 2.10+ 的升级路上,或者刚开始写新 playbook,我建议你不要纠结“为什么文档里一会儿 builtin 一会儿 posix”,只需要记住:ansible.builtin是 Ansible 自带的基础能力,ansible.posix是系统专项配置集合。任务涉及系统底层配置时优先去ansible.posix里找工具,涉及通用文件、包、服务、流程时回到ansible.builtin。这个思路能帮你少走很多弯路。