Docker 的工作原理——构建、分发、运行

Docker 的工作原理,在一张交互式画布上讲清楚:从 Dockerfile 到镜像、从镜像仓库到容器,以及隔离进程的命名空间与 cgroups。

Docker 把应用和它运行所需的一切打包成一个镜像,再把镜像作为容器运行——一个隔离的进程,在任何装有 Docker 引擎的机器上表现完全一致。

Docker 的工作原理——构建、分发、运行

The interactive FlowJam canvas for this explanation — every lane, row and arrow above is a real QueryChart diagram you can open and edit.

How to read this visual

  • 三列从左往右读:构建、分发、运行。
  • 最下面那条带子“宿主操作系统”是地基:流程末端的容器就运行在它之上,共享它的内核。
  • 镜像是其他一切围着搬运的制品——从 Docker 引擎到镜像仓库,再回到容器运行时。

构建镜像

“开发者编写描述应用的 Dockerfile”是一份配方,不是结果:一个基础镜像、要安装的依赖、要拷贝的文件和要执行的命令。“docker build 把应用及其依赖打包”执行这份配方,“构建产出一个镜像——一份只读快照”则是产物——一个存放在分层文件系统里的不可变制品。可复现才是重点:同一个 Dockerfile 在任何机器上都产出同一个镜像。

分发制品

“镜像被推送到镜像仓库,或从中拉取”就是“分发”这一幕。镜像仓库是镜像的分发网络——给团队用的私有仓库,给基础镜像用的公共仓库。镜像在分发过程中不会改变,这正是部署具备确定性的原因:你测过的东西,就是你运行的东西。

运行容器

“docker run 从镜像启动一个容器”跨过 Docker 引擎,进入“容器运行时”分组。“容器由命名空间和 cgroups 隔离”是隔离机制——命名空间让进程拥有自己的一套文件系统、网络和进程表视图,cgroups 则给它的 CPU 与内存设上限。“容器共享宿主机内核,但拥有自己的文件系统”落在“宿主操作系统”这条带子上,把它与虚拟机的对比讲明白;而“应用进程在任何地方都以同样的方式运行”就是让整套栈值得存在的回报。

Key relationships and takeaways

  • 镜像是只读快照;容器是它跑起来的一个实例。
  • 容器共享宿主机内核,用命名空间伪造出隔离——所以它几秒钟就能启动,而虚拟机要几分钟。
  • cgroups 限制容器能消耗的资源,一个吵闹的邻居就饿不死整台主机。
  • 镜像的不可变性,正是“到哪儿跑都一样”成立的原因。
  • Docker 提供打包方式和运行时;Kubernetes 这类编排系统负责跨多台主机管理大量容器。

When to use this visual

  • 向开发者解释 Docker 为什么能工作,以及容器与虚拟机有什么不同。
  • 在团队写下第一个 Dockerfile 之前,用构建—分发—运行这套模型带他们上手。
  • 为一场关于环境漂移的讨论打底——为什么“在我机器上是好的”不再站得住脚。

运作方式

  1. 标注你的 Dockerfile 步骤

    在 Dockerfile 方框上列出你构建中真实的层——基础镜像、安装依赖、拷贝源码、启动命令——让这份配方变得具体。

  2. 写出你的镜像仓库

    把镜像仓库那一步改成你实际使用的仓库(一个私有 ECR、一个公共镜像),并注明哪些镜像是你推送的、哪些是你拉取的。

  3. 补上网络与数据卷方框

    在运行这一列里扩展出容器之间如何通信——一个 bridge 网络——以及状态存放在哪里——一个数据卷,因为这两处正是初学者最常漏掉的。

  4. 连到编排

    如果你在用 Kubernetes,就从运行中的容器加一条注释指向 Kubernetes 图解,说明 kubelet 改变了容器运行在什么位置。

常见问题

容器和虚拟机有什么区别?

虚拟机虚拟的是硬件:它携带一个完整的操作系统,一切都由虚拟机管理程序(hypervisor)居中调度,所以它笨重、启动慢。容器虚拟的是操作系统:它共享宿主机内核,用命名空间做隔离,用 cgroups 做资源限制。这就是容器能在几秒内启动、并且轻量得多的原因——代价是同一台主机上的所有容器共用这台主机的内核。

镜像和容器分别是什么?

镜像是静态制品——由 Dockerfile 构建出来的、应用及其依赖的只读快照。容器是这个镜像动起来的样子:一个运行中的进程,上面叠了一层可写层。你只构建一次镜像,却可以从它启动上千个容器,彼此互相隔离。

Docker 怎么隔离容器?

靠两个 Linux 内核特性。命名空间让每个容器拥有自己的一套系统视图——自己的文件系统、网络栈、进程表和用户 id——于是它看起来就像一台独立的机器。cgroups(控制组)限制并计量资源,给一个容器能消耗的 CPU、内存和 I/O 设上限,这样它就无法把邻居拖垮。

Docker 为什么让应用可移植?

因为镜像把应用连同它的运行时、库和配置一起打包——除了内核之外的一切。只要有 Docker 引擎在,镜像就以同样的方式运行,于是开发者的笔记本、CI runner 和生产服务器之间的差距,就缩小到那些被刻意留在镜像之外的配置。

在 QueryChart(FlowJam)中编辑这张图解

把上面这张 Docker 画布原样打开成你自己的图表,把泳道分组改成你自己的技术栈,再给你的镜像流水线加上注释。

在 QueryChart(FlowJam)中编辑这张图解

可视化图解中的更多内容