Podman 是用于运行 容器 的新工具。它是 无守护进程 的(与 docker 不同),并且从一开始就设计得更加适应 Linux 生态系统。
Podman 的架构类似于经典的 Linux 工具 —— 它轻量级,不会要求超出其需要的权限,并且 愿意与 SELinux 合作。(不像我们中的某些人!)
然而,你可能已经意识到一些在 Docker 中运行良好的命令,在 Podman 中却无法正常工作;尤其是在 SELinux 控制下时。
你可能会抓狂(如果你有头发的话)。
你会遇到各种各样关于权限的错误,你急于避免禁用 SELinux,但最终你还是会使用 setenforce 0 并以 sudo 运行所有东西。
停下!🚏 不,我是说 停下。🛑 我们可以解决这个问题。
说不就对了。
在本文中,我将介绍如何在运行无根容器时,与 Podman 共享主机上的卷。
我还将分享一个在这些情况下非常有用的 podman 命令。
无根 Podman vs 有根 Podman 概览
当你使用 Podman 运行容器时,你可能会在 无根 或 有根 模式下运行。你选择如何运行 Podman 会影响进程运行的 用户 ID。因此,这也会影响你的容器化进程被允许执行的操作。
下表展示了 Podman 的四种可能的无根/有根操作模式,以及它们在实际中的工作方式:
| 你以..运行 podman | 容器化进程以..运行 | 主机上实际可见的 UID 是… |
|---|---|---|
| root | root | 0 |
| root | 非 root | 容器内部运行进程的用户的 UID |
| 非 root | root | 你的 UID |
| 非 root | 非 root | 一个非 root UID |
-
第一列是你如何运行 podman。你是以 root 用户运行命令(例如
sudo podman run....),还是不是? -
第二列是容器进程运行的用户。你可以使用 podman 的
-u选项显式设置,例如podman run -u root... -
第三列显示了进程在主机上 真正 运行的用户 ID。如果你在 主机 上运行
ps -ef并找到你的容器化进程,这就是你在进程列表中看到的 UID。
如你所见,进程的最终用户 ID 可能会有很大的变化!让我们看看这一部分是如何工作的。
理解无根 Podman 的工作原理
无根 Podman(以非 root 用户运行 Podman)需要一些技巧来获得你从 docker 中了解的相同容器体验,但不需要 root。
Podman 处理 UID
当你运行无根 Podman 时,它使用 用户命名空间 来映射容器中的用户 ID 和主机上的用户 ID。
什么是用户命名空间?
用户命名空间 是一种 Linux 功能,它允许你包裹和隔离进程,使其看起来在自己的不同安全身份(如用户 ID 和组 ID)下运行。
从 manpage:
进程的用户和组 ID 在用户命名空间内部和外部可以不同。
例如,用户命名空间允许容器化进程在用户命名空间内作为一个用户运行,如 UID 200。但在用户命名空间(容器)外部,它实际上运行的是完全不同的 UID(例如 100199)。
查看 Dan Walsh 关于用户命名空间如何与 Podman 一起工作的这篇优秀文章。
在 Podman 的 用户命名空间 中,有一组新的用户 ID 和组 ID,这些 ID 与主机上的 UID 和 GID 是分开的。
通过使用用户命名空间和 UID 的 映射,Podman 可以使一个容器进程 看起来 在容器中作为用户 200 运行,但实际上它在主机上运行的是不同的用户 ID。
你可以通过查看容器内的文件 /proc/self/uid_map 来查看实际的映射。
Linux manpages 有更多关于这方面的详细信息。查看 man 7 user_namespaces
如果你有几分钟时间,并且正在寻找一个简洁的视频来解释这一点,可以查看 Red Hat 关于无根 Podman 的视频:
所有由你运行的无根容器,都是在同一个 用户命名空间 内运行的。Podman 背后的工程师在 这篇关于 Podman 背后过程的文章 中解释了这一点:
所有无根容器 必须在同一个用户命名空间内运行。如果它们不在同一个命名空间内,有些事情(如共享另一个容器的网络命名空间)将变得不可能。
通过使用相同的用户命名空间,你的容器可以彼此共享资源,而无需请求 root 权限。
它使用这个 用户命名空间 来挂载文件系统,或运行一个访问多个用户 ID(UID)或组 ID(GID)的容器。
这种映射对于大多数情况来说是可以的,除非容器需要与主机共享一些东西,比如一个卷。
但是 - 这里有一个重要的事情:
当容器运行时,任何与它共享的卷在用户命名空间内将显示为由 root/root 拥有。 因为映射将主机上的 UID(如 1000)映射为容器中的 root(0)。
以下是我的意思的一个例子。我以非 root 用户(用户 200)运行无根 podman,并从我的主机挂载一个卷: