Docker
核心思想:Docker 用 轻量级容器 解决”环境一致性”问题。基于 Linux 内核的 Namespace、Cgroups 和 UnionFS 三大技术,让进程”以为自己独占系统”。
Docker 是什么
- Docker 是一种轻量级虚拟化技术,核心价值是 环境一致性
- 与虚拟机相比,Docker 更轻量、更快速、资源利用率更高
- Docker 基于 Linux 内核的 Namespace、Cgroups 和 Union FS 技术
- Docker 推动了容器技术的标准化(OCI)和生态发展
三个基本概念
- 镜像(Image):一个特殊的只读文件系统,包含容器运行时所需的程序、库、资源、配置文件以及运行时配置(如匿名卷、环境变量、用户等)。镜像构建后不会改变。
- 容器(Container):镜像运行时的实体。镜像和容器的关系就像面向对象里的类和实例——镜像是定义,容器是运行实体。可被创建、启动、停止、删除、暂停。
- 仓库(Repository):镜像构建后需要在多台服务器分发,Docker Registry 就是集中存储与分发镜像的服务,类似代码界的 GitHub。
镜像
Docker 镜像是一个只读模板,包含运行应用所需的一切:代码、运行时、库、环境变量、配置文件。
类比:镜像就像一张光盘或 ISO 文件。同一张光盘可以在不同电脑上安装系统,光盘本身不会被修改;同样,一个镜像可以创建多个容器,镜像本身保持不变。
镜像与操作系统的关系

操作系统分为内核和用户空间。Linux 内核启动后挂载 root 文件系统来提供用户空间支持。Docker 镜像本质上就是一个 root 文件系统。
例如官方镜像 ubuntu:24.04 包含完整的 Ubuntu 24.04 最小系统的 root 文件系统——但 不包含 Linux 内核(容器共享宿主机的内核)。
镜像包含什么
Docker 镜像是一个特殊的文件系统,包含:
| 内容类型 | 示例 |
|---|---|
| 程序文件 | 应用二进制文件、Python / Node 解释器 |
| 库文件 | libc、OpenSSL、各种依赖库 |
| 配置文件 | nginx.conf、my.cnf 等 |
| 环境变量 | PATH、LANG 等预设值 |
| 元数据 | 启动命令、暴露端口、数据卷定义 |
关键特性:
- ✅ 镜像是 只读 的
- ✅ 镜像 不包含 动态数据
- ✅ 镜像构建后 内容不会改变
分层存储
这是镜像的核心设计。通过 Union FS 技术,Docker 能够高效地构建和管理镜像。

用一个最佳实践的 Dockerfile 解释分层:
FROM ubuntu:24.04 # 第 1 层:基础系统(约 78MB)
RUN apt-get update && apt-get install -y nginx # 第 2 层:更新索引并安装 nginx
COPY app.conf /etc/nginx/ # 第 3 层:复制配置文件
这里特意把
apt-get update和apt-get install放在同一个RUN里,避免构建缓存导致包索引过期。
构建后的镜像结构:

每一层的特点:
- 只读:构建完成后不可修改
- 可共享:多个镜像可以共享相同的层
- 有缓存:未变化的层不会重新构建
分层存储的”陷阱”
关键原理:每一层的文件变化会被记录,但 删除操作只是标记,不会真正减小镜像体积。
## 错误示范 ❌
FROM ubuntu:24.04
RUN apt-get update
RUN apt-get install -y build-essential # 安装编译工具(约 200MB)
RUN make && make install # 编译应用
RUN apt-get remove build-essential # 试图删除编译工具
## 结果:镜像仍然包含 200MB 的编译工具!
## 正确做法 ✅
FROM ubuntu:24.04
RUN apt-get update && \
apt-get install -y build-essential && \
make && make install && \
apt-get remove -y build-essential && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
## 在同一层完成安装、使用、清理
容器
容器是镜像的运行实例。如果把镜像比作程序,那么容器就是进程。
镜像是类(Class),容器是对象(Instance)。
容器的本质是一个特殊的进程:

这种隔离主要通过 Linux 内核的 Namespace 实现,资源限制通常与 cgroups 配合:
- 进程空间:容器看不到宿主机上的其他进程
- 网络:默认网络模式下,容器拥有独立的网络命名空间并可分配独立 IP;使用 host 或 container: 模式时则例外
- 文件系统:容器拥有独立的 root 目录
- 用户:默认 uid 0,但通常只拥有受限 capabilities;启用 userns-remap 或 rootless 时会进一步映射为宿主机低权限用户
容器的存储层
镜像层 + 容器层
当容器运行时,Docker 会在镜像的只读层上创建一个 可写层(容器存储层)。

Copy-on-Write:写时复制
当容器需要修改镜像层中的文件时:
- Docker 将该文件 复制 到容器存储层
- 在容器层中进行修改
- 原始镜像层保持不变
读取文件:直接从镜像层读取(共享,高效)
修改文件:复制到容器层,然后修改(只有这个容器能看到修改)
容器存储层的生命周期
⚠️ 新手最容易踩的坑:容器存储层与容器生命周期绑定。容器删除,数据就没了!
## 创建容器,写入数据
$ docker run -it ubuntu bash
root@abc123:/# echo "important data" > /data.txt
root@abc123:/# exit
## 删除容器
$ docker rm abc123
## 数据丢了!没有任何办法恢复!
正确的数据持久化方式
容器存储层应该保持 无状态。需要持久化的数据应该使用数据卷或绑定挂载:
| 方式 | 说明 | 适用场景 |
|---|---|---|
| 数据卷(Volume) | Docker 管理的存储 | 数据库、应用数据 |
| 绑定挂载(Bind Mount) | 挂载宿主机目录 | 开发时共享代码 |
## 使用数据卷(推荐)
$ docker run -v mydata:/var/lib/mysql mysql
## 使用绑定挂载
$ docker run -v /host/path:/container/path nginx
这些位置的读写 会跳过容器存储层,直接写入宿主机,性能更好,也不会随容器删除而丢失。
容器的生命周期

常用的生命周期命令:
## 创建并启动容器(最常用)
$ docker run nginx
## 分步操作
$ docker create nginx # 创建容器(不启动)
$ docker start abc123 # 启动容器
## 停止容器
$ docker stop abc123 # 优雅停止(发送 SIGTERM,等待后发送 SIGKILL)
$ docker kill abc123 # 强制停止(直接发送 SIGKILL)
## 暂停 / 恢复(不常用,但有时有用)
$ docker pause abc123 # 暂停容器内所有进程
$ docker unpause abc123 # 恢复
## 删除容器
$ docker rm abc123 # 删除已停止的容器
$ docker rm -f abc123 # 强制删除运行中的容器
容器与进程的关系
核心:容器的生命周期 = 主进程(PID 1)的生命周期。
## 主进程运行,容器运行
## 主进程退出,容器停止
这就是为什么:
## 这个容器会立即退出(bash 没有输入就退出了)
$ docker run ubuntu
## 这个容器会持续运行(官方 nginx 镜像让 nginx 在前台运行)
$ docker run nginx
官方 nginx 镜像默认使用 nginx -g 'daemon off;',让主进程保持在前台运行,这样容器才会持续处于运行状态。
容器的隔离性
Docker 容器通过以下 namespace 实现隔离:
| Namespace | 隔离内容 | 效果 |
|---|---|---|
| PID | 进程 ID | 容器为 PID 1 是应用进程,看不到宿主机其他进程 |
| NET | 网络 | 独立的网络栈、IP 地址、端口 |
| MNT | 文件系统 | 独立的根目录和挂载点 |
| UTS | 主机名 | 独立的主机名和域名 |
| IPC | 进程间通信 | 独立的信号量、消息队列 |
| USER | 用户 | 独立的用户和组 ID |
仓库
Docker Registry 是存储和分发 Docker 镜像的服务,类似于代码的 GitHub 或包管理的 npm。
镜像构建完成后可以在当前机器上运行,但要在其他服务器上使用就需要一个集中的存储与分发服务——这就是 Docker Registry。
Registry / 仓库 / 标签的关系
Docker Registry 中可以包含多个 Repository,每个 Repository 可以包含多个 Tag。

| 概念 | 说明 | 示例 |
|---|---|---|
| Registry | 存储镜像的服务 | Docker Hub、ghcr.io |
| Repository(仓库) | 同一软件的镜像集合 | nginx、mysql、mycompany/myapp |
| Tag(标签) | 仓库内的版本标识 | latest、1.28、alpine |
镜像的完整名称
一个完整的 Docker 镜像名称由 Registry 地址、用户名 / 组织名、仓库名和标签组成:
[registry 地址/][用户名/]仓库名[:标签]
示例:
## 完整格式
registry.example.com/mycompany/myapp:v1.2.3
│ │ │ │
│ │ │ └── 标签
│ │ └── 仓库名
│ └── 用户名 / 组织名
└── Registry 地址
## Docker Hub 官方镜像(省略 registry 和用户名)
nginx:1.28
ubuntu:24.04
## Docker Hub 用户镜像
jwilder/nginx-proxy:latest
## 其他 Registry
ghcr.io/username/myapp:v1.0
us-west1-docker.pkg.dev/my-project/my-repo/myapp:v1.0
漏洞扫描
## 使用 Docker Scout 扫描镜像漏洞
$ docker scout cves nginx:latest
## 使用 Trivy(开源工具)
$ trivy image nginx:latest
Dockerfile
Dockerfile 是一个文本文件,包含一条条的 指令(Instruction)。每条指令构建一层,描述该层应当如何构建。
使用 Dockerfile 构建镜像的优势:
- 自动化:通过
docker build自动构建镜像 - 可重复性:文本文件确保每次构建结果一致
- 版本控制:可以纳入 Git,便于追踪变更
- 透明性:任何人都可以通过阅读 Dockerfile 了解构建过程
参考:Dockerfile reference · Dockerfile best practices
常用指令
- FROM:指定基础镜像,必须是第一条指令
- RUN:在镜像中执行命令,用于安装软件包等
- COPY:复制文件到镜像中
- ADD:更高级的复制(支持 URL 和自动解压)
- CMD:容器默认执行的命令
- ENTRYPOINT:容器启动时的入口点
- ENV:设置环境变量
- ARG:构建时的参数变量
- VOLUME:定义匿名卷挂载点
- EXPOSE:声明容器监听的端口
- WORKDIR:设置工作目录
- USER:指定运行容器时的用户
- HEALTHCHECK:配置容器健康检查
- ONBUILD:设置触发器指令,在子镜像构建时执行
- LABEL:为镜像添加元数据标签
- SHELL:指定 RUN 等指令使用的 shell
最佳实践
- 使用具体的基础镜像版本标签而非
latest - 最小化镜像层数,合并
RUN指令 - 使用
.dockerignore排除不必要的文件 - 安装必要的软件包后清理缓存
- 使用多阶段构建减小最终镜像体积
- 避免以 root 身份运行容器应用
数据管理
Docker 数据管理围绕三类挂载方式展开:

数据卷(Volume)
容器存储层有一个关键问题:容器删除后,数据就没了。

数据卷的生命周期 独立于容器,解决了这个问题。
特性
| 特性 | 说明 |
|---|---|
| 持久化 | 容器删除后数据仍然保留 |
| 共享 | 多个容器可以挂载同一个数据卷 |
| 即时生效 | 对数据卷的修改立即可见 |
| 不影响镜像 | 数据卷中的数据不会打包进镜像 |
| 性能更好 | 绕过 UnionFS,直接读写 |
数据卷 vs 容器存储层

挂载数据卷
方式一:--mount(推荐)
$ docker run -d \
--name web \
--mount source=my-vol,target=/usr/share/nginx/html \
nginx
参数说明:
| 参数 | 说明 |
|---|---|
source |
数据卷名称(不存在会自动创建) |
target |
容器内挂载路径 |
readonly |
可选,只读挂载 |
方式二:-v(简写)
$ docker run -d \
--name web \
-v my-vol:/usr/share/nginx/html \
nginx
格式:-v 数据卷名:容器路径[:选项]
两种方法对比:
| 特性 | --mount |
-v |
|---|---|---|
| 语法 | 键值对,更清晰 | 冒号分隔,更简洁 |
| 数据卷(Volume)挂载行为 | 卷不存在会自动创建,与 -v 结果一致 |
卷不存在会自动创建 |
| 绑定挂载(Bind Mount)行为 | ❌ 宿主机路径不存在会报错,不会自动创建 | 宿主机路径不存在会自动创建为目录 |
| 推荐程度 | ✅ 推荐(更明确安全,避免误创建) | 常用(更简洁) |
数据卷 vs 绑定挂载
Docker 有两种主要数据持久化方式:
| 特性 | 数据卷(Volume) | 绑定挂载(Bind Mount) |
|---|---|---|
| 管理方式 | Docker 管理 | 用户管理 |
| 存储位置 | /var/lib/docker/volumes/ |
任意宿主机路径 |
| 可移植性 | 更好 | 依赖宿主机路径 |
| 适用场景 | 生产数据持久化 | 开发时同步代码 |
| 备份 | 需要工具 | 直接访问文件 |
绑定挂载(Bind Mount)
将 Docker daemon 所在主机 上的目录或文件直接挂载到容器中。容器可以读写这台主机上的文件系统。

目录结构(同一份文件):
/home/user/code/ (或 /usr/share/nginx/html/)
├── index.html
├── style.css
└── app.js
Bind Mount vs Volume
| 特性 | Bind Mount | Volume |
|---|---|---|
| 数据位置 | 宿主机任意路径 | Docker 管理的目录 |
| 路径指定 | 必须是绝对路径 | 卷名 |
| 可移植性 | 依赖宿主机路径 | 更好(Docker 管理) |
| 性能 | 依赖宿主机文件系统 | 优化的存储驱动 |
| 适用场景 | 开发环境、配置文件 | 生产数据持久化 |
| 备份 | 直接访问文件 | 需要通过 Docker |
选择建议:
| 需求 | 推荐方案 |
|---|---|
| 开发时同步代码 | Bind Mount |
| 持久化数据库数据 | Volume |
| 共享配置文件 | Bind Mount |
| 容器间共享数据 | Volume |
| 备份方便 | Bind Mount(直接访问) |
| 生产环境 | Volume |
基本语法
使用 --mount(推荐):
$ docker run -d \
--mount type=bind,source=/宿主机路径,target=/容器路径 \
nginx
使用 -v(简写):
$ docker run -d \
-v /宿主机路径:/容器路径 \
nginx
两种语法对比:
| 特性 | --mount |
-v |
|---|---|---|
| 语法 | 键值对,更清晰 | 冒号分隔,更简洁 |
| 路径不存在时 | 直接报错(Fail Fast) | 静默自动创建目录 |
| 推荐程度 | ✅ 推荐 | 常用 |
⚠️ 陷阱:如果不小心挂载了一个不存在的主机路径,使用
-v会在 daemon 主机上 静默创建 一个空目录。这也是 Docker 官方更推荐使用--mount的原因:它会直接报错,避免因路径拼写错误而挂错位置。
Tmpfs 挂载
tmpfs 挂载会把数据放在 内存 中,而不是写入容器可写层或数据卷。只适用于 Linux 容器,适合需要快速读写但不要求持久化的数据。
适用场景:
- 临时缓存
- 会话数据
- 不希望落盘的敏感中间文件
基本用法
使用 --mount(推荐):
$ docker run --mount type=tmpfs,destination=/run,tmpfs-size=67108864,tmpfs-mode=1770 nginx
也可以使用 --tmpfs 简写:
$ docker run --tmpfs /run:size=64m nginx
注意事项
- 容器停止后,tmpfs 数据会丢失
- tmpfs 占用宿主机内存,建议显式限制大小
- 不适合需要持久化的数据
- 不适合多个容器共享同一份数据
- 在内存压力较高时,部分数据可能受系统交换机制影响——不要把 tmpfs 当作绝对不会落盘的安全边界
三种挂载方式比较
| 类型 | 数据位置 | 持久化 | 典型用途 |
|---|---|---|---|
| Volume | Docker 管理目录 | 是 | 数据库、长期业务数据 |
| Bind Mount | 宿主机指定目录 | 是 | 开发联调、配置文件共享 |
| tmpfs | 内存 | 否 | 高速缓存数据、敏感临时文件 |
网络配置
Docker 启动时自动创建以下网络组件:

| 概念 | 要点 |
|---|---|
| DNS 配置 | 自定义网络支持嵌入式 DNS,可通过容器名解析 |
| 网络类型 | bridge(默认)、host、none、overlay、macvlan |
| 自定义网络 | 推荐使用,支持容器名 DNS 解析和更好的隔离 |
| 容器互联 | 同一自定义网络下容器可直接通过容器名通信 |
| 端口映射 | -p 宿主机端口:容器端口,暴露服务到外部 |
| 网络隔离 | 不同网络默认隔离,增强安全性 |
--link |
已废弃,使用自定义网络替代 |
Overlay 网络工作原理
Overlay 网络通过 隧道封装(通常是 VXLAN)将容器网络流量封装在宿主机物理网络的 UDP 数据包中传输。Docker overlay 默认使用 4789/udp 作为数据通道端口;Swarm 控制面与节点通信还需要开放 2377/tcp、7946/tcp 和 7946/udp。
容器 A (192.168.0.2)
↓ veth 对
↓ br-net (网桥)
↓ Docker 引擎 (VXLAN 封装)
↓ 物理网络 (172.16.0.0/24)
↓ Docker 引擎 (VXLAN 解封装)
↓ br-net (网桥)
↓ veth 对
容器 B (192.168.0.3, 不同宿主机)
创建和使用 Overlay 网络
Overlay 网络依赖 Swarm。若要让独立容器加入,需要把网络创建为
attachable。
# 初始化 Swarm
docker swarm init
# 创建 attachable overlay 网络
docker network create --driver overlay \
--attachable \
--subnet 192.168.0.0/24 \
--opt com.docker.network.driver.mtu=1450 \
my-overlay-net
# 验证网络创建
docker network ls
docker network inspect my-overlay-net
# 在 Swarm 服务中使用 overlay 网络
docker service create --name web \
--network my-overlay-net \
--replicas 3 \
nginx
# 单机环境下让普通容器加入 attachable overlay 网络
docker run -d --name container1 --network my-overlay-net busybox sleep 1d
docker run -d --name container2 --network my-overlay-net busybox sleep 1d
# 测试跨容器通信
docker exec container1 ping -c 1 container2
性能优化
# 调整 MTU(Maximum Transmission Unit)避免分片
# VXLAN 开销 50 字节,物理 MTU 1500 时建议设置为 1450
docker network create --driver overlay \
--attachable \
--opt com.docker.network.driver.mtu=1450 \
optimized-overlay
# 启用 IP 地址管理(IPAM)自定义
docker network create --driver overlay \
--attachable \
--subnet 10.0.9.0/24 \
--aux-address "my-router=10.0.9.2" \
my-custom-overlay
Docker Compose
Compose 让用户能够以 声明式方式 定义和管理多容器应用。核心价值在于用一个 YAML 文件取代一连串手动的 docker run 命令,使得复杂应用的启动、停止和重建变得一键可达。
对开发团队而言,Compose 解决了三个关键问题:
- 环境一致性:”在我机器上能跑”的问题
- 服务依赖管理:确保数据库在应用之前启动
- 配置差异管理:开发 / 测试 / 生产通过
compose.override.yaml实现多环境适配
底层实现
Docker 底层的核心技术包括 Linux 上的 Namespace(命名空间)、Cgroups(控制组)、Union 文件系统 和容器格式。
传统虚拟机通过 hypervisor 模拟一整套完整硬件环境给虚拟机操作系统——隔离彻底但资源浪费严重。容器则共享宿主机内核,用 namespace 做隔离、cgroups 做资源分配——大家共用一个内核和某些运行时环境,但彼此看不到,”都以为系统中只有自己的存在”。
基本架构
Docker 采用 C/S(客户端 / 服务端) 架构。Client 向 Daemon 发送请求,Daemon 负责构建、运行和分发容器。

Namespace(命名空间)
Namespace 是 Linux 内核提供的资源隔离机制,让容器内的进程仿佛运行在独立的操作系统中。
它回答一个关键问题:如何让一个进程”以为”自己独占整个系统?

Namespace 的类型
| Namespace | 隔离内容 | 容器中的效果 |
|---|---|---|
| PID | 进程 ID | 容器为 PID 1 开始,看不到其他容器和宿主机进程 |
| NET | 网络栈 | 独立的网卡、IP 地址、端口、路由表 |
| MNT | 挂载点 | 独立的文件系统视图,自己的根目录 |
| UTS | 主机名 | 独立的主机名和域名 |
| IPC | 进程间通信 | 独立的信号量、消息队列、共享内存 |
| USER | 用户 / 组 ID | 容器内的 root 可以映射为宿主机的普通用户 |
| Cgroup | Cgroup 根目录 | 隔离 cgroup 层级视图(Linux 4.6+) |
| Time | 系统时钟 | 隔离 CLOCK_MONOTONIC 和 CLOCK_BOOTTIME(Linux 5.6+) |
Namespace 的局限性
Namespace 提供了 隔离 但不是 安全边界。
| 方面 | 说明 |
|---|---|
| 共享内核 | 所有容器共享宿主机内核,内核漏洞可能影响所有容器 |
| 部分资源未隔离 | /proc、/sys 部分内容仍可见;Time Namespace(Linux 5.6+)虽已可用,但 Docker 默认不启用 |
| 非虚拟化 | 比虚拟机隔离性弱 |
需要更强隔离时,可考虑 gVisor、Kata Containers 等安全容器方案。
Cgroups(控制组)
Cgroups 是 Linux 内核的特性,用于 限制、记录和隔离 进程组的资源使用(CPU、内存、磁盘 I/O、网络等)。
核心作用:让多个容器公平共享宿主机资源,防止单个容器耗尽系统资源。

Cgroup 可以限制的资源
| 资源类型 | 子系统 | 说明 |
|---|---|---|
| CPU | cpu、cpuset |
CPU 使用时间和核心分配 |
| 内存 | memory |
内存使用上限和 swap |
| 块设备 I/O | blkio |
磁盘读写速度限制 |
| 网络 | net_cls、net_prio |
网络带宽优先级 |
| 进程数 | pids |
限制进程 / 线程数量 |
Docker 中的资源限制
内存限制:
## 限制容器最多使用 512MB 内存
$ docker run -m 512m myapp
## 限制内存 + swap
$ docker run -m 512m --memory-swap 1g myapp
## 软限制(超过时警告,不会 OOM Kill)
$ docker run --memory-reservation 256m myapp
| 参数 | 说明 |
|---|---|
-m / --memory |
硬限制(超过会 OOM Kill) |
--memory-swap |
内存 + swap 总限制 |
--memory-reservation |
软限制(内存竞争时生效) |
--oom-kill-disable |
禁用 OOM Killer(谨慎使用) |
CPU 限制:
## 限制使用 1.5 个 CPU 核心
$ docker run --cpus=1.5 myapp
## 限制使用 CPU 0 和 1
$ docker run --cpuset-cpus="0,1" myapp
## 设置 CPU 使用权重(相对值,默认 1024)
$ docker run --cpu-shares=512 myapp
| 参数 | 说明 |
|---|---|
--cpus |
限制 CPU 核心数(如 1.5) |
--cpuset-cpus |
绑定到特定 CPU 核心 |
--cpu-shares |
CPU 时间片权重(相对值) |
--cpu-period / --cpu-quota |
精细控制 CPU 配额 |
I/O 限制:
## 限制设备写入速度为 10MB/s
$ docker run --device-write-bps /dev/sda:10mb myapp
## 限制设备读取速度
$ docker run --device-read-bps /dev/sda:10mb myapp
## 限制 IOPS
$ docker run --device-write-iops /dev/sda:100 myapp
进程数限制:
## 限制最多 100 个进程
$ docker run --pids-limit=100 myapp
联合文件系统(UnionFS)
UnionFS 是一种 分层、轻量级 的文件系统,将多个目录”联合”挂载到同一个虚拟目录,形成统一的文件系统视图。

Docker 选择 UnionFS 作为其存储驱动的核心优势:
1. 镜像分层复用

多个镜像共享相同的底层,节省磁盘空间。
2. 快速构建
每个 Dockerfile 指令创建一层,只有变化的层需要重建:
FROM node:22 # 层 1:基础镜像
COPY package.json ./ # 层 2:依赖定义
RUN npm install # 层 3:安装依赖
COPY . . # 层 4:应用代码
代码变更时,只需要重建层 4,其他层使用缓存。
3. 容器启动快
容器启动时不需要复制镜像,只需:
- 在镜像层上创建一个薄的可写层
- 联合挂载所有层
Docker 支持的存储驱动
| 存储后端 / 驱动 | 核心特性说明 | 推荐程度 |
|---|---|---|
| containerd image store | (v29.x 新一代默认后端,新装就默认)基于 containerd 的 snapshotters,原生支持 OCI Image Index、多架构镜像、Attestations 构建源数据存储 | ✅ 强烈推荐(现代默认) |
| overlay2 | (经典 Graph Driver)传统架构下的现代 Linux 默认驱动,性能优秀,但在处理复杂源数据(索引)时受限 | ✅ 推荐(主要后备) |
| aufs | 早期默认,兼容性好 | 遗留系统 |
| btrfs / zfs | 使用原生稳定文件系统快照能力 | 特定场景 |
| devicemapper | 块设备级存储 | 遗留系统(已被逐步弃用) |
| vfs | 不使用 CoW,每层完整复制 | 仅测试 |
网络
Docker 的网络实现利用了 Linux 上的 网络命名空间 和 虚拟网络设备(特别是 veth pair)。
基本原理
要实现网络通信,机器需要至少一个网络接口(物理或虚拟)来收发数据包;不同子网通信需要路由机制。
Docker 中的网络接口默认都是 虚拟接口——优势之一是转发效率较高。Linux 在内核中进行数据复制来实现虚拟接口之间的数据转发,发送接口的发送缓存中的数据包被直接复制到接收接口的接收缓存中。对系统内部看来就像一个正常的以太网卡,只是不需要真正同外部网络设备通信,速度要快很多。
Docker 容器网络就利用了这项技术:在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做 veth pair)。
创建网络流程
Docker 创建容器时:
- 创建一对虚拟接口,分别放到本地主机和新容器中
- 本地主机一端桥接到默认的
docker0或指定网桥上,并具有唯一名字(如veth65f9) - 容器一端放到新容器中,并修改名字为
eth0(只在容器命名空间可见) - 从网桥可用地址段获取一个空闲地址分配给容器的
eth0,并配置默认路由到桥接网卡
--network 参数
docker run 时可通过 --network 指定容器的网络配置:
--network=bridge(默认):连接到默认的网桥--network=host:不将容器网络放到隔离命名空间,使用本地主机网络。拥有完整的主机接口访问权限——慎用,因为容器可以打开低范围端口、访问本地服务、甚至重启主机--network=container:NAME_or_ID:将新容器进程放到一个已存在容器的网络栈中,与该容器共享 IP 地址和端口--network=none:将新容器放到隔离的网络栈中但不进行网络配置,用户自己配置
容器编排(Kubernetes)
Kubernetes(K8s)是 Google 开源的容器编排引擎。如果说 Docker 解决了”如何打包和运送集装箱”,那么 K8s 解决的就是”如何管理海量集装箱的调度、运行和维护”。
为什么需要 K8s
单机几个容器,Docker Compose 就够了。但生产环境要面对:
- 多主机调度:容器应该运行在哪台机器上?
- 自动恢复:容器崩溃了怎么办?节点挂了怎么办?
- 服务发现:容器 IP 变了,其他服务怎么找到它?
- 负载均衡:流量大了,如何分发给多个副本?
- 滚动更新:如何不中断服务升级应用?
K8s 完美解决了这些问题。
核心概念
| 概念 | 要点 |
|---|---|
| Pod | 最小调度单位,包含一组共享网络和存储的容器 |
| Deployment | 管理无状态应用的 Pod 副本集,支持滚动更新和回滚 |
| StatefulSet | 管理有状态应用,提供稳定的网络标识和持久化存储 |
| DaemonSet | 确保每个节点运行一个 Pod 副本,适用于日志、监控等场景 |
| Job / CronJob | 运行一次性或定时任务,确保任务成功完成 |
| Service | 为 Pod 提供稳定的网络访问入口和负载均衡 |
| Namespace | 资源隔离和多租户支持 |
| ConfigMap / Secret | 配置与敏感信息的管理 |
| Master 节点 | 运行 API Server、Scheduler、Controller Manager |
| Worker 节点 | 运行 kubelet、kube-proxy 和容器运行时 |
Docker 与 K8s
如果你已经熟悉 Docker,学习 K8s 会很容易:
| Docker 概念 | Kubernetes 概念 | 说明 |
|---|---|---|
| Container | Pod | K8s 增加了一层 Pod 包装 |
| Volume | PersistentVolume | K8s 的存储更抽象和强大 |
| Network | Service / Ingress | K8s 的网络模型更高级 |
| Compose | Deployment + Service | 声明式配置的理念是一致的 |
Kubernetes 是当前最主流的容器编排平台,其 声明式管理模型 和丰富的 API 为大规模容器化应用提供了坚实的基础。
参考资料
- Docker 从入门到实践 — 中文社区最完整的 Docker 入门教程