
Docker 入门第一课虚拟机 vs 容器彻底搞懂容器为什么快一、从部署之痛说起几乎每个后端开发者都经历过这样的场景代码在开发机上跑得好好的一部署到测试环境就报错。排查半天最后发现是 Python 版本差了个小版本、某个动态库没装、或者配置文件路径不一致。这类问题的根源在于传统的部署方式依赖运行环境本身。应用和它依赖的操作系统、库、运行时是分离的环境一变行为就变。虚拟机VM曾经是这个问题的标准答案把整个操作系统连同应用一起打包走到哪带到哪。但虚拟机太重了——一台虚拟机动辄十几 GB、启动要几分钟一台物理机上跑十几台虚拟机就已经捉襟见肘。容器的出现就是要在环境一致性和轻量高效之间兼得。二、虚拟机是怎么解决问题的又贵在哪里虚拟机的核心是Hypervisor虚拟机监视器。它在物理硬件之上虚拟出一套完整的硬件环境CPU、内存、磁盘、网卡每台虚拟机在这个虚拟硬件上安装一个完整的操作系统Guest OS应用再运行在 Guest OS 之上。┌─────────┐ ┌─────────┐ ┌─────────┐ │ App A │ │ App B │ │ App C │ ├─────────┤ ├─────────┤ ├─────────┤ │ Bins/库 │ │ Bins/库 │ │ Bins/库 │ ├─────────┤ ├─────────┤ ├─────────┤ │ GuestOS │ │ GuestOS │ │ GuestOS │ └─────────┘ └─────────┘ └─────────┘ ┌──────────────────────────────────┐ │ Hypervisor │ ├──────────────────────────────────┤ │ 物理硬件 │ └──────────────────────────────────┘这套架构的代价非常直白每台虚拟机都要背一个完整的操作系统。一个最小化的 Linux 发行版也要几百 MB 到几 GB其中绝大多数内核、驱动、系统服务和你的应用毫无关系。启动本质上是启动一个操作系统。BIOS 自检、内核加载、服务拉起分钟级是常态。资源开销是乘数级的。十台虚拟机就是十个 Guest OS 的内存常驻开销物理机资源被大量浪费在跑系统上。虚拟机的隔离性确实强——毕竟连内核都是独立的——但为了隔离付出整个操作系统的成本对只跑一个应用这个诉求来说太奢侈了。三、容器操作系统的隔离术容器换了一个思路不虚拟硬件不装 Guest OS直接在宿主机的内核上隔离进程。┌─────────┐ ┌─────────┐ ┌─────────┐ │ App A │ │ App B │ │ App C │ ├─────────┤ ├─────────┤ ├─────────┤ │ Bins/库 │ │ Bins/库 │ │ Bins/库 │ └─────────┘ └─────────┘ └─────────┘ ┌──────────────────────────────────┐ │ Docker Engine容器运行时 │ ├──────────────────────────────────┤ │ 宿主机操作系统内核共享 │ ├──────────────────────────────────┤ │ 物理硬件 │ └──────────────────────────────────┘注意两张架构图的关键差异容器层里没有 Guest OS。所有容器共享宿主机的同一个内核容器里只装应用自己和它依赖的库、二进制文件。这不是 Docker 凭空发明的魔法而是 Linux 内核早就有的两项能力Namespace命名空间给进程创建一个楚门的世界——它看到的进程树、网络、文件系统、用户都是独立的以为自己独占了一台机器实际上只是宿主机上的一个组进程。Cgroups控制组给进程圈定资源配额——最多用几个 CPU、多少内存防止某个容器把宿主机吃干抹净。Docker 把这些内核能力 分层镜像 统一工具链封装成了开发者友好的产品。所以严格来说容器是被隔离的进程不是一台机器。这一句认知差是后面所有容器为什么快的答案源头。在《03 核心概念》一篇里我们会把镜像、容器、仓库的关系彻底拆开这里先建立一个直觉镜像是打包好的应用快照容器是这个快照跑起来的实例。四、虚拟机 vs 容器全方位对比维度虚拟机容器虚拟化层级硬件级Hypervisor操作系统级内核隔离Guest OS每虚拟机一套完整系统无共享宿主机内核启动速度分钟级秒级甚至毫秒级镜像体积十几 GB 起步几十 MB ~ 几 GB单机密度几台 ~ 几十台数百 ~ 上千个性能损耗较大硬件虚拟化 Guest OS接近原生隔离强度强独立内核较强进程级隔离跨系统能力可在 VM 内跑不同内核的 OS与宿主内核强相关Linux 容器跑在 Linux 内核上两个常见的追问答案也在这张表里“容器能装不同的操作系统吗”不能。容器共享宿主内核Linux 容器只能跑 Linux 系内核支持的东西。这也是为什么 Windows 上跑 Linux 容器需要 WSL2 或轻量虚拟机做一层内核桥接——Docker Desktop 在 Windows 背后其实就是偷偷开了一台微型 Linux 虚拟机。“容器隔离不如虚拟机强安全吗”隔离边界确实不同虚拟机隔的是机器容器隔的是进程视图。容器逃逸的风险在理论上高于 VM 逃逸但工程上通过最小化基础镜像、非 root 运行、内核及时打补丁容器隔离强度对绝大多数业务场景是足够的。这个问题会在《28 容器安全加固》展开。下图是 Docker 官方对两种架构的直观对比图源Docker 官网 What is a Container五、容器凭什么秒级启动三个真正的原因把前面的内容收拢容器快的原因可以归结为三条这也是面试时的标准答题框架1. 没有 Guest OS启动的是进程而不是系统虚拟机启动 操作系统开机内核加载、服务拉起容器启动 宿主机上 fork 一个进程并套上 Namespace 隔离。一个是开机一个是启动进程量级天然不同。2. 镜像分层 写时复制UnionFSDocker 镜像是分层的基础镜像层、依赖层、应用层层层叠加。启动容器时不需要复制整个镜像而是把各只读层联合挂载再加一个薄薄的可写层写时复制Copy-on-Write。同一台宿主机上十个容器用同一个镜像只读层在磁盘和内存里都只存一份。3. 体积小分发快几十 MB 的镜像秒级拉取十几 GB 的虚拟机镜像分钟级传输。在需要弹性扩容的场景比如突发流量要 30 秒内扩出 100 个实例这个差距直接决定方案是否可行。反过来理解也很有启发容器牺牲的是可以跑不同内核的自由换来的是数量级的效率提升。而现实中绝大多数应用根本不需要独立的内核——这就是容器化席卷云原生世界的原因。六、动手验证5 分钟跑起你的第一个容器理论讲完上手验证。如果你还没安装 Docker可以先看下一篇《安装全指南》已装好的话直接在终端里敲# 拉取并运行 hello-world 容器dockerrun hello-world第一次执行时Docker 会自动从 Docker Hub 拉取镜像几 MB几秒钟然后启动容器、打印一段说明、退出。注意输出里的两句话Unable to find image hello-world:latest locally latest: Pulling from library/hello-world ... Hello from Docker! This message shows that your installation appears to be working correctly.接着用 nginx 体验一个常驻服务型容器# 后台运行 nginx并把宿主机 8080 端口映射到容器 80 端口dockerrun-d-p8080:80--namemy-nginx nginx# 查看运行中的容器dockerps# 浏览器访问 http://localhost:8080 看到 nginx 欢迎页后停止并删除dockerstop my-nginxdockerrmmy-nginx留意docker run到浏览器看到页面之间花了多久——在镜像已拉取的情况下容器启动通常在1 秒内完成。对比你平时启动一台虚拟机的时间容器为什么快就从概念变成了手感。