Hacker News Best

Docker is weird

8.5内容质量

TL;DR · AI 摘要

Docker的实现机制与常见认知存在显著差异,容器本质是受限进程,镜像仅是文件夹,Go语言在容器运行时存在设计缺陷。

核心要点

  • 容器本质是受限进程,通过Linux命名空间和cgroups实现隔离
  • Docker镜像实际是文件夹结构,通过chroot实现根目录切换
  • runc容器运行时因Go语言多线程特性需特殊处理系统调用

结构提纲

按章节快速跳转。

  1. 作者通过实现自定义容器运行时揭示Docker的实现细节

  2. 容器是受限进程,依赖Linux内核特性实现隔离

  3. Docker镜像本质是文件夹结构,通过chroot实现根目录切换

  4. ·Go语言的挑战

    Go语言的多线程特性导致runc需要特殊处理系统调用

  5. Docker需要root权限,rootless模式需重构网络栈

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • Docker实现机制解析
    • 容器本质
      • 受限进程
      • Linux命名空间
      • cgroups
    • 镜像结构
      • 文件夹结构
      • chroot机制
    • 技术挑战
      • Go语言限制
      • root权限需求
      • 网络栈重构

金句 / Highlights

值得收藏与分享的关键句。

#Docker#容器技术#Linux内核#Go语言
打开原文

Code and Cake - Docker很奇怪

Code and Cake

关注

关于我

Docker很奇怪

2026年10月22日

出于兴趣,我决定自己动手实现一个容器运行时。尽管目前还处于早期阶段,但我已经发现Docker有很多奇怪的地方。以下是我学到的一些关于Docker的奇怪之处。

容器并不是真实的

与虚拟机不同,容器只是受到特殊限制的进程。你的内核仍然在运行这个进程!如果你知道在哪里查找,甚至可以在/proc中找到这个进程。

容器真的不是真实的

实际上Linux内核也没有专门用于"容器"的功能。相反,容器实际上是用"trenchcoat"包装的五个不同的Linux内核特性。

命名空间(Namespaces)隐藏了主机和容器可见的内容,cgroups限制容器的资源使用,还有一些我完全不理解的网络魔法,可以让容器网络实现隔离。

镜像只是文件夹

镜像只是Docker放置在某个位置的文件夹,然后通过chroot(或pivot_root)将其变为容器进程的新根目录。所以当你运行docker run -it --rm ubuntu /bin/bash时,Docker会执行以下操作:

  • 从Dockerhub下载Ubuntu镜像
  • 将镜像存放到某个位置
  • 运行/some/weird/docker/path/bin/bash

Go语言对于容器运行时来说有点糟糕

Docker的容器运行时runc是用Go语言编写的,而Go语言的一些设计选择使其不太适合容器运行时。Go的运行时是原生多线程的,这虽然很酷,但给runc带来了一些挑战。

容器运行时需要执行的许多Linux系统调用都必须在单线程环境中完成,因此runc包含了一个疯狂的C模块,它在Go运行时初始化之前运行,以便执行这些系统调用。

Go语言也没有fork!fork只会复制主线程,这在Go这样的运行时中可能成为脚枪。但容器运行时确实需要fork,设置很多限制,然后执行你实际在docker run中指定的进程。为了解决这个问题,runc通过向/proc/self/exe传递一个特殊命令行参数来模拟fork,这个参数标识它是一个子进程。

Docker需要root权限!

如果你像我一样在MacOS上运行Docker,这一点很容易被忽略。容器运行时使用的许多Linux内核API都需要(或至少以前需要)以root身份运行,因此Docker守护进程通常以root身份运行。我只能想象容器安全一定是个非常困难的工作。

如今情况似乎有所改善。最大的例外似乎是网络。如果你运行无root容器运行时,看起来必须在用户空间中重新实现整个网络栈,才能给你提供相同的功能!

学习是有趣的!

我刚刚开始自己的容器运行时开发,但已经感觉对Docker了解了很多。自己实现一个工具的版本是学习它的绝佳方式!