任何对 Ansible 稍有了解的人都会证实,保持幂等性是实现稳定自动化的关键秘诀。没有幂等性,几乎不可能检测到漂移和/或可预测地管理状态变化。同样,任何非完全初级的 Podman 用户都会知道,定义和使用卷是基本操作。
现在来看问题所在:在 Ansible 中,template
模块是部署和持续管理远程文件内容的重要工具。但是,如果你尝试使用模板自动化 Podman 卷内容管理,你很快就会发现一个主要缺点
Ansible 完全被用户命名空间所困扰。
注意:如果你还不熟悉用户命名空间,我建议阅读 Dan 的“在 Podman 无根容器中使用文件和设备”。它要么会启发你,要么会让你更困惑。如果是后者,我建议在继续之前阅读我的“通俗易懂的无根 Podman 用户命名空间”文章。
例如,考虑下面的 playbook ./local_1.yml
。它旨在部署一个 Web 服务器容器,而不将主机用户映射到 root 用户 (userns: nomap
)。这种缺乏 user -> root
映射旨在保护主机。如果进程从容器中逃逸,它将无法访问主机用户的文件或进程。请忽略针对 localhost
的设置,这仅仅是为了演示目的。但请注意显式管理卷和配置文件所有权的任务
./local_1.yml
--- - hosts: localhost gather_facts: true gather_subset: user become: false tasks: - name: Config volume for nginx container containers.podman.podman_volume: name: nginx_confd options: - o=uid=0,gid=0 state: present register: vresult - name: Deploy nginx configuration template: src: default.conf.j2 dest: >- {{ vresult.volume.Mountpoint }}/default.conf mode: u=rw,g=r,o=r owner: 0 group: 0 - name: Run nginx container containers.podman.podman_container: name: webserver image: nginx publish: ["8080:80"] userns: nomap volume: - >- {{ vresult.volume.Name }}:/etc/nginx/conf.d:Z,U recreate: true state: started
如果你还没有安装,请务必安装 podman 集合
$ ansible-galaxy collection install containers.podman
在运行 playbook 之前,请务必获取一份内置的默认配置副本。示例可以使用它作为配置模板的存根,因此无需在其中实际添加任何 jinja2 元素
$ mkdir templates $ podman run -it --rm nginx \ cat /etc/nginx/conf.d/default.conf \ > ./templates/default.conf.j2
最后,当 playbook 以 ansible-playbook -i localhost, -l localhost -c local local_1.yml
应用时,结果应该是一个类似于以下的失败消息
FAILED! => {"changed": false, "checksum": "...", "mode": "0644", "msg": "chown failed: [Errno 1] Operation not permitted: .../nginx_confd/_data/default.conf" ...}
一个可能显而易见的解决方法是,简单地给予用户 sudo
访问权限,在任务中使用 become: true
,并根据一些硬编码的计算手动偏移 ID。但在你这样做之前请仔细考虑。对于快速/脏的 playbook 来说可能没问题,但它绝对无法扩展。
这完全是由于全局命名空间执行上下文造成的。Ansible 以普通用户身份在“远程”主机上执行任务,没有 sudo 访问权限 (become: false
)。此外,template
模块试图写入一个文件(Ansible 假定)由 UID/GID 零 拥有。而实际上,它应该由某个相对于 $UID/$GID
的 UID/GID 偏移量拥有(由 /etc/subuid
和 /etc/subgid
确定)。更糟糕的是,Ansible 将永远无法告知操作员任何 default.conf
更改,也无法触发相关的通知任务,例如重新启动容器。所以,这在几乎所有方面都是糟糕的。该死!
对自动生成的低级系统细节(如 ID)的管理完全打破了 Ansible(在大规模情况下)的“通用”性质。它要求 Ansible 操作超出其预期范围的方面。请考虑以下不算太牵强但完全有效的 /etc/subuid
内容
...cut... fred:100000:65536 fred:165536:1024 fred:166560:131072 wilma:100000:999999 ...cut...
如果还不清楚:Ansible 不应该直接处理 subuid/subgid 映射,原因与它不应该直接操作 /etc/passwd
或 /etc/group
完全相同:这些是操作系统和系统实现细节,可能会/将会意外地发生变化。考虑一下用户命名空间迁移到身份提供程序(如 FreeIPA 或 Active Directory)的情况,那时怎么办,抛弃你所有的 Playbook 吗?
幸运的是,有一种方法可以解决这个问题,尽管有点“hacky”:以一种几乎对 Ansible 透明的方式抽象出用户命名空间的机制。关键是将 podman unshare
命令插入到由 Ansible 创建的远程进程链中,从 python
开始。这可以通过一个简单的包装脚本轻松完成
./files/podman_unshare_wrapper.sh
#!/bin/sh exec /usr/bin/podman unshare $(type -p python3) "$@"
如以下 ./local_2.yml
示例 Playbook 所示,部署包装器非常简单,请确保在本地 ./files
子目录中创建它。然后可以使用 Ansible 复制模块的默认行为来创建任何缺失的远程目标目录。使用包装器同样简单。暂时覆盖“远程” python
解释器位置。然后 template
模块可以通过包装器正确处理用户命名空间转换,以设置预期的文件所有权
./local_2.yml
--- - hosts: localhost gather_facts: true gather_subset: user become: false tasks: - name: Config volume for nginx container containers.podman.podman_volume: name: nginx_confd options: - o=uid=0,gid=0 state: present register: vresult - name: Deploy podman unshare wrapper copy: src: podman_unshare_wrapper.sh dest: >- {{ ansible_user_dir }}/.local/bin/ mode: u=rxw,g=rx,o=rx - name: Deploy nginx configuration vars: ansible_python_interpreter: >- {{ ansible_user_dir }}/.local/bin/podman_unshare_wrapper.sh template: src: default.conf.j2 dest: >- {{ vresult.volume.Mountpoint }}/default.conf mode: u=rw,g=r,o=r owner: 0 group: 0 - name: Run nginx container containers.podman.podman_container: name: webserver image: nginx publish: ["8080:80"] userns: nomap volume: - >- {{ vresult.volume.Name }}:/etc/nginx/conf.d:Z,U recreate: true state: started
尝试像以前一样运行该 playbook,ansible-playbook -i localhost, -l localhost -c local local_2.yml
。您应该会发现它成功完成并启动了一个正常工作的 nginx
容器(监听 https://:8080
)。此外,您可以通过以下命令从主机验证正确的文件所有权
$ vol=$(podman volume inspect -f '{{.Mountpoint}}' nginx_confd) $ podman unshare ls -lan "$vol" total 4 drwxr-xr-x. 2 0 0 26 Feb 6 15:23 . drwx------. 3 0 0 19 Feb 6 15:23 .. -rw-r--r--. 1 0 0 1093 Feb 6 15:23 default.conf
假设一切顺利,Ansible template
模块现在拥有了新的超能力!也许最好的部分是,如果内容发生漂移或需要运行处理程序,此版本的模板任务将提供完整的更改详细信息。继续对本地 stub 模板文件进行一些修改。然后再次运行 playbook,注意 changed
状态,并使用此命令查看它干净地更新了文件
$ podman exec -it webserver cat /etc/nginx/conf.d/default.conf
诚然,这个包装脚本肯定是一种 hack,所以它可能不会永远有效,也不是对每个需要它的 Ansible 模块都有效。但至少目前,希望这些示例已经突出了所涉及的组件和关键操作。因此,维护这个 hack 应该大部分时间都是容易的。在所有情况下,有了这些新知识,我希望您能够将其运用到未来可能需要/受益于它的任何 Playbook 中。
回复 Chris Evich取消回复