Skip to content

第 1 章 重新认识 Docker ​

本章目标:建立"镜像 = 类、容器 = 实例"的心智模型,说清容器与虚拟机、普通进程的边界,并验证本机 Docker 环境就绪。

1. 容器解决了什么问题 ​

传统部署有三大痛点:

  • 环境不一致:开发、测试、生产三套环境,库版本、系统配置稍有差异就"在我机器上是好的"。
  • 依赖冲突:一台机器上跑多个应用,A 要 Python 3.9、B 要 3.11,装到一起互相打架。
  • 资源浪费:为隔离而用虚拟机,每个都自带完整操作系统,启动按分钟算,内存占用按 GB 算。

容器的思路是:把应用连同它的运行环境一起打包成一个标准工件(镜像),在任何装了 Docker 的机器上以隔离进程的方式运行。环境差异被锁进镜像里,依赖冲突被 Namespace 隔离化解,隔离开销只有进程级别。

2. 容器 vs 虚拟机 vs 进程 ​

维度普通进程容器虚拟机
隔离级别无(共享一切)进程级(Namespace + cgroups)硬件级(Hypervisor + 客户机内核)
是否共享宿主机内核—是否(自带内核)
启动速度即时毫秒级秒到分钟级
资源开销极小极小(MB 级)大(GB 级)
隔离强度无中(内核级隔离)强(完全独立)
可移植性差强(OCI 标准工件)中

一句话:容器是"加了隔离的进程",不是"省了内核的虚拟机"。容器靠 Linux 内核的两项机制实现隔离与限制:

  • Namespace:让容器里的进程看到独立的 PID、网络、文件系统、用户等视图;
  • cgroups:限制容器可用的 CPU、内存等资源,防止一个容器拖垮宿主机。

3. 镜像与容器:类与实例 ​

这是全教程最重要的心智模型:

  • 镜像(image):只读模板,里面是应用代码、运行时、系统库和配置,相当于面向对象里的类。
  • 容器(container):镜像的运行实例,有独立可写的文件层,相当于对象。
text
镜像(类)──docker run──▶ 容器(对象)
   │                        ├─ 容器 1(可写层)
   │                        ├─ 容器 2(可写层)
   └─ 只读、可复用          └─ ... 互不影响

一个镜像可以派生任意多个容器,彼此完全隔离;容器删了,镜像还在,可以再跑新的。下一章会看到镜像本身由多层只读层组成——这也是"类"的另一个含义:镜像可以基于基础镜像继承扩展。

4. Docker 架构:docker 命令只是客户端 ​

docker 不是单个程序,而是一套分层架构。你敲的每条命令都走了这条链路:

text
docker CLI(客户端)
   │  REST API(本地 socket 或远程)
   ▼
dockerd(守护进程,常驻后台)
   │  容器运行时 API
   ▼
containerd(容器生命周期管理)
   │
   ▼
runc(按 OCI 规范真正创建进程)
  • docker CLI:你敲 docker xxx 时执行的命令,只负责把请求发给 daemon;
  • dockerd:核心守护进程,管理镜像、容器、网络、数据卷;
  • containerd:负责容器生命周期(创建、启停、销毁),也是 Kubernetes 的默认运行时;
  • runc:按 OCI 规范在 Linux 上真正创建隔离进程。

在 Windows/macOS 上,Docker Desktop 还会在虚拟机里跑一个 Linux 内核,Linux 容器实际运行在那台虚拟机里——这也是"容器共享宿主机内核"的具体体现。

5. 验证环境就绪 ​

用三个命令确认环境可用。

① 查看版本(客户端 + 服务端)

bash
docker version --format 'Client: {{.Client.Version}} | Server: {{.Server.Version}} | OS: {{.Server.Os}}/{{.Server.Arch}}'

输出示例(本机实测):

text
Client: 29.7.2 | Server: 29.7.2 | OS: linux/amd64
  • Client:docker CLI 版本;
  • Server:dockerd 版本。如果 Server 部分报错或缺失,说明 daemon 没在运行,后面所有命令都会失败。

② 查看引擎信息

bash
docker info

输出很长,重点看:镜像与容器数量、存储驱动(如 overlay2)、网络插件、Docker Root Dir(镜像存放位置)。

③ 端到端验证:运行 hello-world

bash
docker run --rm hello-world

输出示例:

text
Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

请对照第 4 节的架构图逐条理解这四步:CLI 联系 daemon → daemon 拉镜像 → daemon 创建容器并运行 → 输出回流到终端。--rm 表示容器退出后自动删除,不留垃圾。

国内网络拉取镜像慢时,可配置镜像加速,配置方法见 README 环境准备。

常见误区 ​

  • 误区:容器就是"轻量虚拟机"。 正确:容器是共享宿主机内核的隔离进程,没有自己的内核;也因此启动是毫秒级、开销是 MB 级。
  • 误区:docker run 是 CLI 自己执行的。 正确:CLI 只是把请求发给 dockerd,由 daemon 完成拉镜像、建容器等全部工作。
  • 误区:容器里可以随便装 Windows/macOS。 正确:Linux 容器只能运行与宿主机同架构、同内核兼容的发行版;要跑不同内核得用虚拟机。
  • 误区:删除容器会丢失镜像。 正确:镜像与容器相互独立,删容器不影响镜像,镜像可反复创建容器。

小结 ​

  • Docker 解决环境不一致、依赖冲突、资源浪费三大问题,核心是"镜像打包 + 隔离进程运行"。
  • 容器是"加了 Namespace 隔离和 cgroups 资源限制的进程",不是虚拟机。
  • 镜像 = 类(只读模板),容器 = 对象(运行实例),一个镜像可派生无数互不影响的容器。
  • 命令链路:docker CLI → dockerd → containerd → runc。
  • 用 docker version(确认 Server 在运行)和 docker run --rm hello-world(端到端验证)确认环境就绪。

练习 ​

  1. 运行 docker version,分别说出 Client 和 Server 的职责;再运行 docker run --rm hello-world,对照第 4 节架构图复述四步过程。
  2. 运行 docker info,找到存储驱动、Docker Root Dir、镜像数量三项信息。
  3. 思考题:两个容器都基于同一个镜像运行,为什么其中一个容器里修改文件不影响另一个?(提示:与镜像的分层和"写时复制"有关,第 2 章揭晓。)
📖本文阅读--次|📊全站访问--次|👥访客--人