Docker 架构概念图
Docker 的核心思想很简单:把应用和它的所有依赖打包成一个可移植的单元。点击下方的框,了解每个组件的关系。
关键术语树
- Image 镜像
- Layer 层
- Tag 标签
- Dockerfile
- Container 容器
- docker run
- Volume 卷
- Port 端口映射
- Registry 仓库
- Docker Hub
- docker pull
- docker push
Dockerfile 逐行解析
这是一个典型的 Python Web 应用 Dockerfile。点击左侧的每一行,右侧会显示详细解释。
点击左侧代码行查看解释
每一行 Dockerfile 指令都会生成一个镜像层,理解每个指令的含义是写好 Dockerfile 的基础。
层缓存模拟器
Docker 构建时,每个指令产生一个层。如果输入没变,层会被缓存复用。一旦某层失效,它后面所有层都得重建。试试调整顺序,看看缓存如何变化。
Docker 构建从上到下依次执行指令。如果第 N 层的输入发生了变化,那么第 N 层及其后面所有层都会失效,需要重新构建。
试试这样:把 COPY . . 移到 RUN pip install 前面,然后选择"源代码变化"。你会发现 pip install 层也变红了——意味着每次改代码都要重新安装依赖,非常慢。
所以原则是:先复制不常变的文件(如 requirements.txt),再复制常变的源代码。
容器生命周期
点击 Run 观察容器从构建到运行到清理的全过程。每个阶段对应一条 docker 命令。
docker stop 只是停止容器,容器仍然存在,可以用 docker start 重新启动。而 docker rm 才会真正删除容器。镜像在整个过程中不会被删除,需要单独用 docker rmi。Level 1 — 认知与探索
Recognition你已经在上方看到了一个完整的 Dockerfile。现在来检验一下理解:下面的命令匹配练习中,将左侧的 Dockerfile 指令与右侧的描述正确匹配。使用上下按钮调整顺序。
指令匹配练习
Q:"设置容器内的工作目录" 对应哪条指令?点击下方按钮查看答案。
WORKDIR /app — 设置后续指令的工作目录。如果目录不存在会自动创建。
其他指令对应:
FROM = 选择基础镜像COPY = 复制文件到容器RUN = 构建时执行命令EXPOSE = 声明监听端口CMD = 容器启动时执行的命令
回到上方的 Dockerfile 解析区,点击每一行复习一重。注意指令的执行顺序:FROM 永远在第一行,CMD 通常在最后。
为什么重要: Dockerfile 就是一个构建脚本,只是它构建的产物是可移植的镜像。理解每条指令的含义后,你就能读懂任何 Dockerfile。
Level 2 — 补全 Dockerfile
Guided Practice一个 Node.js Express 应用需要容器化。Dockerfile 中有 5 处 TODO,请补全。点击 Check 检查是否正确。
参考上方解析的 Python Dockerfile,结构类似:
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
注意:先复制 package.json 再安装依赖,最后才复制源代码——这是为了利用层缓存。
为什么重要: 手写 Dockerfile 是容器化的第一步。掌握这 7 条指令后,90% 的应用都可以用这个模板容器化。
Level 3 — 独立编写
Independent从零写一个 Python Flask 应用的 Dockerfile。要求:
- 基础镜像用 python:3.12-slim
- 工作目录 /app
- 先复制 requirements.txt 并安装依赖(用 --no-cache-dir 参数)
- 复制全部源代码
- 声明 5000 端口
- 启动命令为 python app.py(用 JSON 数组形式)
- 添加一条注释说明使用 slim 的原因
CMD 的 JSON 数组形式:CMD ["python", "app.py"]。这比 shell 形式 CMD python app.py 更推荐,因为它不经过 shell 处理,信号可以直接传递给进程。
--no-cache-dir 告诉 pip 不要在容器内保存缓存,可以减小镜像体积。
为什么重要: 从零编写是巩固理解的最佳方式。如果你能独立完成这个任务,说明你已经掌握了 Dockerfile 的核心结构。
Level 4 — 优化挑战
Challenge下面是一个"笨拙"的 Dockerfile。它能跑但层缓存很差。请用上方的层缓存模拟器测试你的优化方案,记录你的思考过程。
FROM python:3.12 WORKDIR /app COPY . . RUN pip install -r requirements.txt EXPOSE 5000 CMD ["python", "app.py"]
问题:
- 这个 Dockerfile 有哪些问题?(至少找出 3 个)
- 如何重写以最大化缓存命中?
- 在层缓存模拟器中验证你的方案:选择"源代码变化",优化后应该只有 2 层重建,而不是 4 层。
原始 Dockerfile 的 3 个问题:
1. 使用了 python:3.12 完整镜像(~900MB)而非 slim(~45MB)
2. COPY . . 在 pip install 之前,每次源代码变化都会重新安装依赖
3. pip install 没有 --no-cache-dir,会在镜像中留下缓存
优化后:
# 使用 slim 减小体积 FROM python:3.12-slim WORKDIR /app # 先复制依赖文件,利用层缓存 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 再复制源代码 COPY . . EXPOSE 5000 CMD ["python", "app.py"]
为什么重要: 在实际工程中,层缓存的优化可以把构建时间从几分钟缩短到几秒。这个"先复制依赖再复制源码"的模式适用于 Node.js、Go、Java 等所有语言。
自测
检验你对 Docker 核心概念的理解。共 8 题,每题选完后查看解析。
容器 vs 虚拟机
经常有人问"容器和虚拟机有什么区别"。这是理解 Docker 价值的关键。
| 虚拟机 (VM) | Docker 容器 | |
|---|---|---|
| 隔离级别 | 硬件级(虚拟 CPU/内存) | 进程级(共享内核) |
| 启动时间 | 分钟级 | 秒级 |
| 资源占用 | 每个 VM GB 级内存 | 每个容器 MB 级 |
| 包含内核 | 是(完整 OS) | 否(共享宿主内核) |
| 密度 | 低(数十个) | 高(数百个) |
| 安全隔离 | 强(硬件隔离) | 较弱(内核共享) |
术语表
点击卡片翻转查看定义。
Image 镜像
只读的模板,包含运行应用所需的所有文件和配置。由 Dockerfile 构建,由多个层组成。
Container 容器
镜像的运行实例。可以启动、停止、删除。一个镜像可以创建无数个容器。
Layer 层
Dockerfile 中每条指令产生的只读文件系统层。相同的层会被缓存复用,加速构建。
Dockerfile
构建镜像的纯文本配方。从 FROM 开始,包含 COPY、RUN、CMD 等指令。
Volume 卷
独立于容器生命周期的持久化存储。容器删除后数据不丢失。
Registry 仓库
存储和分发镜像的服务。Docker Hub 是官方公共仓库,也可以自建私有仓库。
Tag 标签
镜像的版本号。如 python:3.12-slim 中的 3.12-slim 就是 tag。不指定时默认 latest。
Build Context
docker build 时传入的路径。Docker 会将该目录下所有文件发送给 daemon,用于 COPY 指令。
总结与下一步
核心要点
- Dockerfile 是配方,镜像是模板,容器是运行实例
- 每条 Dockerfile 指令产生一个层
- 层缓存:输入不变则复用,变化则重建该层及其后所有层
- 先复制依赖文件,再复制源代码,最大化缓存
- 用 slim/alpine 基础镜像减小体积
- CMD 用 JSON 数组形式优于 shell 形式
下一步学习
- Docker Compose — 定义和运行多容器应用
- Docker Volumes — 持久化数据存储的深入理解
- Docker Networking — 容器间通信和端口映射
- 多阶段构建 (multi-stage build) — 进一步减小镜像
- Docker Hub — 发布和共享镜像
- .dockerignore — 排除不需要的文件
实践建议
- 安装 Docker Desktop,跑通今天的例子
- 用你自己的项目写一个 Dockerfile
- 对比 python:3.12 和 python:3.12-slim 的镜像大小
- 尝试 docker exec -it 进入容器内部查看文件
- 结合 Docker Compose 运行一个 Web + DB 的完整栈