为什么 Docker 在 MicroVM 中会出问题(第 1 部分):你不知道自己依赖的 Linux 假设

发布: (2026年4月24日 GMT+8 13:36)
5 分钟阅读
原文: Dev.to

Source: Dev.to

初始失败

我们尝试在 microVM 中运行 Docker,结果在第一个容器启动之前就失败了:

cgroup mountpoint does not exist

在普通的 EC2 实例上,同样的 Docker 二进制文件可以正常工作。问题并不是 Docker 本身或内核 bug——而是更微妙的东西:我们依赖了在 microVM 中不存在的 Linux 部分。

Cgroup 假设

Docker 的错误信息提到了 cgroup,于是我们检查了文件系统:

ls /sys/fs/cgroup

没有输出。

mount | grep cgroup

同样,什么也没有。

在典型的 Linux 系统中,/sys/fs/cgroup 会因为启动时有程序挂载而自动存在。但在 microVM 中,这一步从未发生,导致 Docker 试图创建自己的 cgroup 层次结构时,内核返回“这里没有接口”。

我们手动挂载了层次结构:

mount -t cgroup2 none /sys/fs/cgroup

Docker 继续前进了一点,但随后又遇到了下一道墙。这让我们学会了问 “Docker 现在假设存在哪些东西?” 而不是单纯地问 “Docker 为什么会失败?”。

Systemd 与启动过程

完整的 Linux 发行版(带有 systemd)在你登录之前会完成许多任务:

  • 挂载 /proc/sys/dev
  • 设置 cgroup
  • 初始化网络
  • 准备运行时环境

microVM 并不会自动完成这些——除非你显式加入 systemd,否则它们都不存在。因此,缺失或不完整的 /proc/sys 挂载永远得不到后续修复,你实际上必须自己重新实现启动过程。

容器内部的网络栈

在修复了基本挂载后,Docker 开始初始化容器,但网络出现了问题。弄清容器如何访问互联网后,问题便迎刃而解。

容器网络命名空间

  • 每个容器都有自己的 IP(例如 172.17.x.x)。
  • 与宿主机的网络接口共享。
  • 它运行在自己的网络命名空间中。

Docker 的网络设置

  1. Docker 为容器创建一个新的网络命名空间。
  2. 它创建一对 veth 设备:一端留在宿主机,另一端移入容器。
  3. veth 的宿主机端连接到 docker0 桥,使容器之间能够互相通信。
  4. 对离开容器的报文进行 NAT,将源 IP 改写为宿主机的 IP,从而实现外部连通性。

MicroVM 中的额外层

在我们的场景里,容器运行在一个 microVM 内,而该 microVM 本身又有一个由宿主机上的 tap 设备提供的虚拟 NIC。出站流量的完整路径如下:

container → bridge (docker0) → VM eth0 → virtual NIC → host → internet

两层堆叠的网络环境意味着,只要任意一层缺失关键环节,报文就会无法到达目的地,而产生的错误往往难以直观判断。

iptables 与报文过滤

Docker 最终报错:

iptables: Failed to initialize nft: Protocol not supported

此错误根源于更底层的内核能力:

  • 内核中报文过滤的实现方式。
  • iptables 与该实现的交互方式。
  • 内核编译时使用的 netfilter(nftables)后端。

要解决它,需要了解这些底层细节。

思维模型的转变

最大的变化不是技术层面的,而是概念层面的。我们不再把 microVM 当作普通机器来对待,而是采用了全新的思路:

  • 不假设任何东西已经存在。
  • 每一层都必须自行验证。
  • 每一次修复都会揭示下一个依赖。

我们不再是“调试 Docker”,而是 在发现“一个可工作的 Linux 环境到底由哪些组成”

第 2 部分将探讨这种思维模型如何在解决 iptables 失败时发挥作用,尤其是在面对更复杂的情况时。

0 浏览
Back to Blog

相关文章

阅读更多 »