LabHub
学习 学习路径 课程

虚拟化与 QEMU/KVM

隔离边界在哪里

在 LabHub 中继续学习

一句话总结

VM 通过硬件边界实现隔离,容器则通过**内核功能(namespace/cgroup)**实现隔离。其余差异都可以从这句话推导出来。

概念图: 硬件边界 · 内核功能(namespace/cgroup) · 需要哪一种隔离 · 一个内核漏洞就可能彻底击穿隔离。

为什么需要了解这些

“该用容器还是 VM?”至今仍是常见问题。答案并不取决于工作负载本身,而取决于需要哪一种隔离

工作原理

[VM]                          [컨테이너]
앱                             앱
게스트 라이브러리               라이브러리
게스트 커널      <- 별개!       (호스트 커널을 공유)
가상 하드웨어                   namespace + cgroup
하이퍼바이저                    컨테이너 런타임
호스트 커널                     호스트 커널

容器没有客户机内核。因此启动速度快(几十毫秒)、几乎没有内存开销,而且部署密度高。但代价是,一个内核漏洞就可能彻底击穿隔离。

项目 VM 容器
内核 每个客户机各自独立 与宿主机共享
启动时间 几十秒 几十毫秒
内存开销 客户机内核 + 虚拟机监控器 几乎没有
密度 每台宿主机数十个 每台宿主机数百个
隔离强度 (硬件边界) 弱(内核边界)
不同 OS/内核 可以 不可以
镜像大小 GB MB

何时选择哪一种

应当选择 VM 的情况

应当选择容器的情况

两者之间——microVM

Firecracker、Kata Containers 等技术填补了二者之间的空白。它们在保留 VM 隔离边界的同时,将启动时间和开销降低到接近容器的水平。通过极度精简设备模型(只保留少量 virtio 设备)并缩短启动路径,可以在约 100 毫秒内启动。AWS Lambda 就运行在 Firecracker 之上。

Kata Containers 又向前迈了一步:它保留容器接口(OCI/CRI),内部实际启动轻量 VM。从 Kubernetes 视角看,它只是一个普通 Pod,实际上却由 VM 边界隔离。可以通过 RuntimeClass 为不同工作负载分别选择,因此能够实施“只对这个命名空间使用强隔离”等策略。

生产现场中的常见情况

“我需要在容器里修改内核参数,但无法操作。” 这是必然的。由于内核与宿主机共享,在容器内执行 sysctl -w 会影响整台宿主机,因此大多数情况下会被禁止。如果确实需要,应在节点级设置(DaemonSet、节点引导),或改用 VM。

这个实验环境本身就是例子。 容器移除了 capability,因此无法执行 mount、tcpdump、iptables。本课程没有绕过这些限制,而是把限制本身作为教材——理解某项操作为什么需要特权,也就是理解隔离结构。

密度与资源管理为何不同

VM 和容器管理资源的方式也不同,这些差异在运维中经常令人意外。

VM 的内存会被预先占用。 分配 4GB 的 VM,无论内部实际使用多少,都会在宿主机上占用相应容量(虽然气球驱动等机制可以返还一部分,但并非即时完成)。相较之下,容器只占用实际使用的内存。 限额只是上限;没有使用的部分可以由其他容器使用。因此,容器的实际总用量远低于请求量总和是正常现象,这正是其高密度的来源。

但容器也要承担过量分配的风险。 如果所有限额之和大于节点内存,而多个容器又同时大量用内存,节点就会进入内存压力状态,内核会选择并终止进程。此时被终止的并不一定是绝对用量最大的进程,而是相对于请求量超用最多的进程,因此未设置请求量的工作负载会最先被牺牲。

CPU 的性质又有所不同。 CPU 与内存不同,稍后还能重新分配,所以超出限额时不会终止进程,而会通过前面介绍的节流使其变慢。因此,通常建议CPU 限额留得宽裕,内存限额设得准确

在 VM 内运行容器时,两层资源管理会相互叠加。 分配给 VM 的内存会成为节点的全部内存,内部容器再共享这些内存。如果 VM 层启用了内存回收机制,节点并不知道自身内存已经减少,仍会继续调度,最终导致难以预测的故障。这正是通常建议在虚拟化节点中关闭内存回收功能的原因。

前面介绍的 microVM 也是两者的折中方案。它采用 VM 隔离,但内存开销很小,可以在不过多牺牲密度的情况下获得内核边界。

下一步要做什么

下一个课程将介绍 rootless 容器。深入理解容器为何能在没有特权的情况下运行(user namespace)后,本模块所说的“通过内核边界隔离”就会落实为具体的系统调用和文件。