TripleO - 从容器安全地生成容器的模式¶
本文档描述了一种模式,可以用作 TripleO 当前方法的替代方案,以允许某些容器(Neutron 等)生成需要特殊权限(如网络命名空间)的并行进程。具体来说,它避免了暴露 Docker 套接字或使用最近进入 Stein 代码库的 Podman nsenter 技巧。
问题描述¶
在 Queens 中,TripleO 实现了一种容器化架构,旨在将所有 OpenStack 服务容器化。这种架构是成功的,但与裸机部署的等效版本相比,一些应用程序出现了回归。其中一个应用程序是 Neutron,它需要能够生成由 Neutron 代理自身直接启动的长期“并行”进程的能力。在最初的 Queens 架构中,Neutron 在代理容器内部启动这些并行进程,如果 Neutron 代理自身重新启动,则会导致服务中断。在裸机上,以前不会出现这种情况,因为这些进程将继续在代理重新启动/升级后运行。
Rocky 中的解决方法是为 Neutron 代理添加“包装器”脚本,并将 Docker 套接字暴露给每个代理容器。这些包装器脚本被绑定挂载到容器中,以便覆盖并行进程的正常位置。使用这种粗糙的机制,像 ‘dnsmasq’ 和 ‘haproxy’ 这样的二进制文件将启动一个 shell 脚本,而不是正常的二进制文件,这些自定义 shell 脚本依赖于从主机暴露的 Docker 套接字,以便能够使用提供给脚本的相同参数启动一个并行容器。
这种机制在功能上解决了容器化的问题,但暴露了一些安全问题,因为我们现在将启动任何容器的能力暴露给这些 Neutron 代理容器(具有访问 Docker 套接字的权限的特权容器)。
在 Stein 中,事情发生了变化,因为我们希望支持 Podman。与 Docker 不同,Podman 不在主机上包含守护进程。所有 Podman 命令都通过 CLI 执行,直接在主机上运行命令。我们发布了补丁,要求 Podman 命令使用 nsenter 进入主机的命名空间并在那里直接运行命令。同样,这种机制需要授予 Neutron 代理容器额外的权限,以便它们能够启动这些命令。此外,这种机制有点晦涩,难以在现场支持和调试。
提议的变更¶
概述¶
使用主机上的 systemd 直接启动并行进程容器,并支持 Neutron 代理所需的网络命名空间。这种方法的优点是,我们不再需要向 Neutron 容器授予启动它们不应该需要的容器的权限。
这种模式可以这样工作
一个 systemd.path 文件监视主机上已知位置的变化。示例 (neutron-dhcp-dnsmasq.path)
[Path]
PathModified=/var/lib/neutron/neutron-dnsmasq-processes-timestamp
PathChanged=/var/lib/neutron/neutron-dnsmasq-processes-timestamp
[Install]
WantedBy=multi-user.target
当 systemd.path 检测到变化时,它会触发此路径文件的服务:示例 (neutron-dhcp-dnsmasq.service)
[Unit]
Description=neutron dhcp dnsmasq sync service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/neutron-dhcp-dnsmasq-process-sync
User=root
我们使用今天使用的相同的“包装器”脚本来写入两个文件。第一个文件是用于在主机上启动进程的 CLI 参数的转储。该文件可以选择包含额外的元数据,例如某些 Neutron 并行进程所需的网络命名空间。第二个文件是一个时间戳,由主机上的 systemd.path 监视变化,并用作信号,指示它需要使用带有参数的文件来处理第一个文件。
- # 当检测到变化时,上面的 systemd.service 在
主机上执行一个脚本,以干净地启动容器化的并行进程。当脚本完成启动进程后,它会截断该文件以从干净的状态开始。
- # 包装器脚本和主机脚本都使用 flock 来消除竞争
条件,这些条件可能导致重新启动或丢失容器的问题。
替代方案¶
使用 Podman,像 varlink 这样的 API 是一个选项,但它可能仍然需要暴露主机上的套接字,这需要像今天一样额外的权限。这将避免 nsenter 技巧,但会避免 nsenter 技巧。
像 Kubernetes 这样的架构将为我们提供一个可以通过 COE 直接启动容器的 API。
此外,可以编写一个“容器感知”的 Neutron 外部进程管理器,以改进上述两种选项。Neutron 中当前的 Python 主要用于在裸机上启动进程,并假设它启动的一些进程应该在容器重新启动后存活。实现一个可以通过干净的接口启动并行进程,而不是覆盖二进制文件的类是可取的。可以支持支持通过 Kubernetes 和/或 Systemd 通过主机直接启动容器的类。
安全影响¶
这种机制应该允许我们删除过去用于执行容器的一些 Neutron 代理容器权限。它是一个更严格的粗糙接口,只允许容器启动特定类型的进程,而不是它选择的任何容器。
升级影响¶
并行进程容器应该与它们的启动方式无关,因此升级应该很小。
实现¶
负责人¶
- 主要负责人
dan-prince
- 其他贡献者
emilienm
工作项¶
# Ansible playbook 用于创建 systemd 文件、包装器
# TripleO Heat 模板更新以使用新的 playbook
# 从 puppet-tripleo 中删除/弃用旧的 docker.socket 和 nsenter 代码