首页 › programming › 容器不是虚拟机:内核如何骗进程

容器不是虚拟机:内核如何骗进程

导语:一个被普遍误解的技术

几乎所有开发者都"用过"容器——docker run、Kubernetes、云原生、CI/CD——但你若问一句"容器到底是个什么东西",十个人里能有八个会答错。最主流的错误答案是:容器就是轻量级的虚拟机。

这个误解如此普遍,以至于它本身成了理解一切云原生技术的最大障碍。今天我们要拆解的这支视频——《How Docker Actually Works: Namespaces & Cgroups Explained》(作者 Doogal,资深软件工程师)——要传递的核心反常识只有一句话:容器里根本没有虚拟机,它甚至没有一个新的操作系统内核。容器只是一群普通的 Linux 进程,只不过内核骗了它们,让它们以为自己是世界上唯一的进程。

这不是一个"科普向"的边角知识。当你真正理解"容器 = 受约束的普通进程"之后,你就能瞬间想通一长串原本只能死记硬背的问题:为什么容器启动只要毫秒级?为什么同一个内核漏洞可以让一台宿主机上所有容器一起沦陷?为什么容器里的 root 和宿主机上的 root 不是一回事?为什么 docker run 启动的程序会变成 PID 1,而 PID 1 在信号处理上有那么多坑?为什么 gVisor、Kata Containers 这些"给容器套一层虚拟机内核"的方案在 2026 年突然成了高安全场景的标配?

本文先还原视频的核心论证,再补齐视频来不及展开的内核机制、安全模型和实战陷阱。读完之后,你对"容器"的认知会从"一种打包工具"升级为"一组 Linux 内核特性的组合产物"。

视频背景卡片

项目 内容
视频标题 How Docker Actually Works: Namespaces & Cgroups Explained
频道 Doogal | Senior Software Engineer
平台 YouTube
核心主题 剥离 Docker 的"魔法外壳",揭示容器在 Linux 内核层面的真实构成:namespaces + cgroups
一句话定位 把"容器"从黑盒变成可被源码级理解的一组系统调用

核心内容拆解

一、最关键的认知翻转:容器 ≠ 虚拟机

视频的第一块基石,是先把"容器是轻量虚拟机"这个图景彻底砸碎。

虚拟机(VM)走的是硬件级虚拟化路线:在物理机之上跑一个 hypervisor(如 KVM、VMware ESXi),它截获并模拟出一套完整的虚拟硬件——虚拟 CPU、虚拟网卡、虚拟磁盘。每个 VM 里都装有一份完整的客户操作系统内核(guest kernel),自己调度进程、自己管内存页表、自己跑驱动。代价是巨大的:每个 VM 至少要占用几百 MB 到几 GB 的内存来养那份 guest OS,冷启动要等内核整个引导流程走一遍,常常是几十秒。

容器走的是完全不同的操作系统级虚拟化路线。这里没有 hypervisor,没有虚拟硬件,更没有第二个内核。所有容器和宿主机共享同一个 Linux 内核。容器里跑的那个程序,本质上和你在宿主机终端里直接敲 ./myapp 启动的进程,是同一种东西——都是内核调度器眼里的 task_struct,都排队等同一个 CPU 时间片。区别只在于:内核用一些"障眼法",让这个进程看不到其他进程、看不到真实的网络拓扑、看不到真实的文件系统挂载点。

视频原话主旨:Docker 并没有发明任何新东西,它只是把 Linux 内核早就在用的两个特性——namespaces 和 cgroups——打包成了一个易用的工具。容器不是新物种,是旧积木的新组合。

这个翻转的含金量极高。一旦接受"容器就是普通进程",后续所有现象都能用进程语义解释:容器秒级启动,是因为启动一个进程本来就只要毫秒,根本不用等内核引导;容器内存占用低,是因为省掉了 guest OS 那一大坨常驻开销;而所谓"容器逃逸(container escape)",本质上不是攻破了一台虚拟机,而是让一个进程骗过内核的隔离,重新看到了宿主机的世界——这是内核漏洞利用问题,和"VM 越界"是完全不同等级的威胁。

二、Namespaces:给进程造一个"楚门的世界"

视频用大量篇幅讲 namespaces,这是"骗进程"的主力武器。namespaces 的机制可以用一个比喻概括:它给每个进程戴上一副特制眼镜,让进程看到的"系统全局状态"是一份被精心裁剪过的副本。 进程以为自己看到了全世界,其实看到的是楚门的世界。

Linux 内核目前提供了8 种 namespace(视频重点讲了前几种,这里补齐全部,并标注哪些是后期加入的),每一种负责隔离一类"系统资源视图":

技术内核(延伸补充):从系统调用层面看,创建和进入 namespace 只有三个 API——clone()(在 fork 时顺便创建新 namespace)、unshare()(让已存在进程"脱离"当前 namespace)、setns()(让进程加入一个已有的 namespace)。所谓"启动一个容器",落到最底层就是带着 CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWNS | ... 这串标志位去调用 clone()。理解了这一点,你就明白容器技术没有任何"魔法内核模块"——它就是几个 flag。

三、Cgroups:给资源画一道"围栏"

视频强调,namespaces 解决的是"能不能看见"的问题,但光隔离是不够的。一个容器即使看不见别的进程,如果它发起一个死循环把 CPU 占满,或者疯狂 malloc 把内存吃光,宿主机上所有其他容器照样被拖死。于是需要第二根支柱:cgroups(control groups),它解决的是"能用多少"的问题——做资源计量(accounting)和资源限制(limiting)。

cgroups 把资源切成几个控制器(controller),视频提到的核心几类包括:

延伸(视频未细讲的历史包袱):cgroup 有两个大版本。v1 是每个控制器一棵独立的层级树(hierarchy),灵活但难用、容易配置混乱;v2(约内核 4.5 起逐步成熟)统一成单一层级树,所有控制器挂在同一棵树上,语义更一致。如今 Docker/Kubernetes 默认已切到 cgroup v2。理解 v1/v2 的差异,是排查很多"为什么我配的限制没生效"问题的前提。

值得注意的是一个常被忽略的设计哲学:cgroups 和 namespaces 是正交的两套机制。你可以只隔离不限资源(进程能看见的很少,但能把机器吃垮),也可以只限资源不隔离(进程什么都看得见,但最多用 1 核)。"容器"只是把这两者同时拉满的约定俗成的组合,内核里并不存在一个叫"container"的对象。

四、安全三件套:capabilities、seccomp、LSM

容器在 namespaces+cgroups 之上,还叠加了第三层——安全策略,因为前两层解决不了"容器内的 root 还是太危险"。视频在这块点到为止,这里展开补充,因为它是理解"为什么容器安全"的关键。

把这三层和前两层叠起来,才是一个生产级容器真正的防御纵深。延伸观点:这也是为什么高安全场景在 2026 年流行 gVisor(Google)和 Kata Containers——它们思路相反:既然共享内核是最大风险点,那就给每个容器配一个专用的轻量级 guest 内核(gVisor 用用户态实现的兼容内核拦截系统调用,Kata 直接套一个极简 VM)。等于把容器和虚拟机的优点缝合——这恰恰反证了"容器 = 共享内核的进程"这个本质。

五、OverlayFS 与镜像分层:Copy-on-Write 的艺术

视频还涉及一个常被忽略但至关重要的机制——镜像(image)是怎么存的、容器怎么从镜像里"跑"起来。

Docker 镜像不是一个单一的大文件,而是一堆只读层(read-only layer)的堆叠,每执行一条 Dockerfile 指令(RUN、COPY)就新增一层。启动容器时,内核用 OverlayFS(联合挂载文件系统,overlay/overlay2)把这些只读层当成 lowerdir,在上面叠一层薄薄的可写层(upperdir),再统一挂成一个 merged 视图给容器看。

这套设计的精髓是 copy-on-write(COW):

延伸:这个分层模型直接决定了 Dockerfile 的最佳实践——把变化频率低的层放前面(基础镜像、装依赖),变化频率高的放后面(拷业务代码),这样 layer cache 命中率最高,构建飞快。"为什么 Dockerfile 顺序影响构建速度"——根因就是 COW 分层。

六、runc 与 OCI:真正干活的那几行系统调用

视频反复强调的一个观点,也是 2015 年容器生态一次重要标准化运动的产物:Docker 只是门面,真正"造容器"的是一个叫 runc 的小程序。

早期 Docker 把"管理镜像""管理网络""创建容器"全塞进一个巨型 daemon,既臃肿又不利于生态。后来社区拆出了 OCI(Open Container Initiative) 标准,把容器的定义拆成三块:镜像格式标准(image spec)、运行时标准(runtime spec)、分发标准(distribution spec)。runc 是 reference runtime——它做的事情极其纯粹:读一份 runtime spec 配置(一个 JSON),然后忠实地调用 clone()/unshare()/setns() 去创建 namespaces,写 cgroup 文件去设限制,挂载 rootfs,最后 exec 用户指定的进程。

于是一个完整的调用栈是:docker CLI → dockerd(daemon)→ containerd(高层运行时,管镜像、生命周期)→ runc(底层运行时,真正发系统调用)。你甚至可以完全不用 Docker,直接拿 runc + 一份手写的 JSON 就能起一个容器。

这个事实极其重要:容器没有任何专利技术、没有任何神秘内核模块。它是 Linux 内核 + 一组标准化的 CLI 工具。 正因如此,containerd、CRI-O、podman、runc 才能无缝互操作——大家都遵守同一份 OCI spec。

七、为什么这套设计统治了云(以及它的命门)

最后,视频隐含了一个值得点透的结论:容器之所以从 2013 年一个 dotCloud 公司的副业,长成今天云计算的事实标准,靠的是上面这套机制带来的结构性优势:

  1. 极致密度:没有 guest OS 开销,一台宿主机可以塞几十到几百个容器,而 VM 时代的密度通常是个位数。
  2. 毫秒级弹性:启动一个进程 = 启动一个容器,扩缩容近乎瞬时,这是 Kubernetes 能做秒级 autoscaling 的物理基础。
  3. 不可变基础设施(immutable infrastructure):镜像是只读、版本化、可复现的制品,"在我机器上能跑"的问题被根除。
  4. 可组合性:基于 OCI 标准,构建、传输、运行可以被不同厂商的不同工具各司其职。

但硬币的另一面是这套设计固有的命门——视频没有展开,这里补上:共享内核意味着"共享命运"(shared fate)。 所有容器趴在同一个内核上,一旦出现一个能绕过 namespaces/capabilities 的内核提权漏洞(历史上 CVE-2019-5736 runc、CVE-2022-0185 heap overflow 等都让容器逃逸成为现实),整个宿主机一锅端。这就是为什么多租户公有云必须用 VM 做硬隔离(如 AWS Nitro、GCP Shielded VM),而容器更适合"信任边界之内"或"每租户独占一台 VM"。理解了"容器是进程而非 VM",你自然就理解了它的安全边界画在哪里。

知识结构图

flowchart TD
    A["容器 = 受约束的普通 Linux 进程<br/>共享同一个内核,无 hypervisor"] --> B["Namespaces<br/>隔离'能不能看见'<br/>给进程造楚门世界"]
    A --> C["Cgroups<br/>限制'能用多少'<br/>给资源画围栏"]
    A --> D["安全三件套<br/>缩小攻击面"]
    A --> E["OverlayFS<br/>镜像分层 + Copy-on-Write"]

    B --> B1["PID — 独立进程树,主进程=PID1"]
    B --> B2["NET — 独立网卡/路由/IP"]
    B --> B3["MNT — 独立挂载视图/rootfs"]
    B --> B4["USER — UID映射,容器root≠宿主root"]
    B --> B5["UTS/IPC/Cgroup/Time"]

    C --> C1["CPU — shares/max 节流"]
    C --> C2["memory — 上限→OOMKilled"]
    C --> C3["IO/pids — 防IO霸占与fork炸弹"]
    C --> C4["v1多树 / v2统一树"]

    D --> D1["Capabilities — 拆root为~40个bit"]
    D --> D2["seccomp — 屏蔽危险系统调用"]
    D --> D3["AppArmor/SELinux — MAC策略"]

    E --> E1["lowerdir 只读镜像层(共享)"]
    E --> E2["upperdir 可写层(每容器独立)"]
    E --> E3["merged 联合视图 → COW"]

    B & C & D & E --> F["OCI runtime: runc<br/>clone/unshare/setns + 写cgroup"]
    F --> G["docker → containerd → runc<br/>CLI 栈"]
    G --> H["统治云的优势:密度/毫秒弹性/不可变/可组合"]
    H --> I["命门:共享内核=共享命运<br/>→ 公有云仍靠VM硬隔离"]

实战启示

把上面这套机制装进脑子,你在工程实践中会获得一组可以直接兑现的能力:

第一,看懂容器"出问题"的真正原因。 Kubernetes 里 OOMKilled 不是你的程序 bug,是 cgroup memory 控制器按你设的 limit 主动杀人;CPUThrottling 是 cpu.max 在节流。调试的第一步永远是分清"我的代码慢"还是"我被内核限流了"。 学会读 /sys/fs/cgroup/ 下的计数文件,能让你在监控告警里一眼定位根因。

第二,理解 PID 1 的特殊责任,避免踩坑。 容器的主进程就是 PID 1,而 PID 1 在 Linux 里有两条特殊使命——转发信号和回收僵尸子进程(reaping zombies)。很多直接把 Node.js / Java 程序塞进容器的人发现:docker stop 要等 10 秒才强杀、容器莫名积压僵尸进程,根因就是他们的运行时没有当好 PID 1。解法是用 tini 或 dumb-init 这种 init 进程包一层,或者在 Kubernetes 里正确配置 restartPolicy 和优雅关闭钩子。这是"容器=进程"视角直接产出的一个高频生产知识点。

第三,资源设置要走 request/limit 两段式。 Kubernetes 的 request(调度依据,告诉调度器你要多少)和 limit(cgroup 硬上限,内核真的拦住你)是两回事。只设 limit 不设 request,会导致调度器以为节点很空、实则被 cgroup 频繁压制;反过来则容易 OOM。理解它们分别对应 cgroup 的哪条配置,才能调出健康的资源画像。

第四,镜像与构建优化有了底层依据。 既然镜像是 OverlayFS 分层 + COW,那"小镜像"(用 alpine/distroless)、"层复用"(指令排序、合并 RUN)、"多阶段构建"(编译镜像和运行镜像分离)就不是玄学,而是直接减少层数和每层大小。安全上,用非 root user 跑容器、最小化 capabilities(Kubernetes 的 securityContext.runAsNonRoot、drop ALL)、跑 seccomp 默认 profile,能把容器逃逸的攻击面压到最小。

第五,学会在"进程视角"和"容器视角"间切换。 进容器里 top、netstat、ls /proc/1/root 时,要意识到你看到的是 namespaces 裁剪过的视图;要诊断"宿主机层面到底发生了什么",得用 nsenter、crictl、docker inspect 这些工具跳出 namespace 看"上帝视角"。这一对视角的切换,是从"会跑容器"到"懂容器"的分水岭。

延伸学习路径

一句话总结

容器没有任何魔法内核——它只是一群被 namespaces 蒙住双眼、被 cgroups 套上枷锁、再用 capabilities/seccomp 削掉獠牙的普通 Linux 进程;Docker 不过是这套古老内核特性的一个漂亮外壳。

参考来源

  1. How Docker Actually Works: Namespaces & Cgroups Explained — 本文分析的核心视频(频道:Doogal | Senior Software Engineer)
  2. How Docker Actually Works: A Deep Dive into Namespaces and Cgroups — dev.to/Doogal — 同作者配套长文,系统调用级补充
  3. namespaces(7) — Linux manual page — 8 种 namespace 的一手权威定义
  4. cgroup v2 — Linux kernel documentation — 统一层级语义官方文档
  5. Open Container Initiative — Runtime Specification — runc 所遵循的容器运行时标准

本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入