集合:为什么名字里写着来源
一句话总结
collection 是把 role、模块、插件捆绑在一起、附上版本(version)来分发的单元,而 ansible.builtin.copy 这样的长名称,是把这个整体的来源写进了名称里。
为什么需要它
直到 2019 年,Ansible 都是把几千个模块一起打包在本体里发布的。要修一个 mysql_user,整个 Ansible 就得发新版本;反过来,升级 Ansible,也会让用不到的几千个模块一起变化。社区模块的维护者和 Ansible 核心的维护者在同一个仓库里发生冲突,发布越来越沉重。
名称冲突也是真实的事故。内部做了一个 deploy role,如果有人在 roles/ 里再放一个同名 role,光看日志就分不清执行的是哪一个。搜索路径的顺序决定结果,而这个顺序不是由人,而是由环境决定的。
Ansible 2.9 中引入的 collection 一举解决了这两个问题。把内容物按 <네임스페이스>.<이름>(占位符依次为命名空间、名称)打包,给这个包附上 SemVer 版本,在 task 中调用时使用 <네임스페이스>.<이름>.<모듈>(占位符依次为命名空间、名称、模块)这样的完整名称(FQCN)。看名称就能知道来源,即使有两个相同的短名称,FQCN 也不会重叠。
工作原理
collection 源码有固定的目录形状。ansible-galaxy collection init <네임스페이스>.<이름>(占位符依次为命名空间、名称)会生成这个骨架。
| 位置 | 内容 |
|---|---|
galaxy.yml |
命名空间、名称、版本、作者、许可证、标签。构建会读取这个文件 |
roles/ |
role。role 内的约定与原来完全一样 |
plugins/ |
模块、过滤器、lookup、callback 之类的插件。每种类型都有规定的子目录 |
playbooks/ |
collection 一并分发的 playbook |
meta/runtime.yml |
最低 ansible 版本(requires_ansible)、重命名(plugin_routing)、action group |
docs/ |
文档 |
galaxy.yml 的 version 必须是 SemVer。 如果像 1.2 这样写成两段,构建会拒绝。有了这条规则,>=1.2.0,<2.0.0 这样的范围表示才有意义。
分发分三步。
ansible-galaxy collection init acme.platform --init-path src # 뼈대
ansible-galaxy collection build --output-path dist # 묶음 만들기
ansible-galaxy collection install dist/acme-platform-1.2.0.tar.gz -p collections
build 生成的不只是普通的 tar.gz。里面同时包含 MANIFEST.json(元数据)和 FILES.json(每个文件的校验和)。安装时会用这份清单确认完整性,所以即使只把压缩包放到公司内部文件服务器上,也可以让机器判断“是不是我们做的那个版本”。
要安装的版本不写在命令行,而是写在文件里。
# requirements.yml
collections:
- name: acme.platform
version: "1.2.0" # Galaxy 나 사내 저장소에서 받을 때
- name: /srv/artifacts/acme-platform-1.2.0.tar.gz
type: file # 로컬 묶음 파일을 그대로 설치할 때
该代码块中的两处韩文注释说明:第一处用于从 Galaxy 或内部仓库获取并指定版本的情形,第二处用于直接安装本地打包文件的情形。
用 ansible-galaxy collection install -r requirements.yml -p <경로>(占位符为路径)安装。只要有固定文件,即使源码仓库已经前进到 1.3.0,安装的仍然是 1.2.0。 这一行就是引入 collection 的最大理由——仓库的最新状态与被分发的版本分离开了。
查找顺序也值得了解。ansible 会查看 ANSIBLE_COLLECTIONS_PATH(或 ansible.cfg 中的 collections_path)中写的路径,以及 playbook 旁边的 collections/ 目录。在 play 中写上 collections: 关键字,就可以用短名称来调用这些 collection,但官方文档推荐使用 FQCN。 短名称依赖的是搜索顺序,而这个顺序是由环境决定的。
在现场相遇的样子
第一,“装了却找不到”。 几乎总是路径问题。用 -p 安装的路径如果不在运行时的搜索路径中,ansible 就不认识这个 collection。养成用 ansible-galaxy collection list -p <경로>(占位符为路径)确认“那里有没有”,用 ansible-doc -t role <FQCN> 确认“这里看不看得到”的习惯,可以省时间。
第二,role 参数规范。 在 meta/argument_specs.yml 中写明参数的类型、默认值和允许值,role 就会在运行第一个 task 之前校验参数,出错当场停下。如果分发的是别人要用的 role,这既是文档也是防线。没有规范,错误的值会在第五个 task 前后以莫名其妙的错误爆出来。
第三,内部分发。 大部分代码无法上传到 Galaxy。实际工作中是由 CI 把 build 出来的压缩包放到内部文件服务器或 Artifactory 上,各团队的 requirements.yml 指向那个地址和版本。流水线在运行之前执行一次 install -r requirements.yml。在这种结构下,“昨天还行今天不行”的事会减少——因为版本写在文件里。
第四,依赖不是免费的。 在 galaxy.yml 的 dependencies: 中写其他 collection,安装时会一并拉取,但这是有可拉取的仓库时的情况。在断开互联网的网络中,连依赖 collection 的压缩包也要手工一起搬运。本实验环境也是这样的网络,所以不讲依赖的自动解析。
下一项实验要做什么
从骨架开始创建 acme.platform collection,填写 galaxy.yml,在其中放一个 role,用 meta/argument_specs.yml 强制参数,构建压缩包并确认内容物和 MANIFEST,安装到本地路径后用 ansible-doc 查询,再用 FQCN 调用并运行。最后把源码的版本升到 1.3.0,亲眼看到 requirements.yml 仍然保持 1.2.0。