Docker存储驱动机制
约 1706 字大约 6 分钟
dockerstorage
2025-06-19
Docker 存储驱动是容器镜像和可写层管理的底层机制。理解联合文件系统(Union Filesystem)和 Copy-on-Write 策略,对镜像优化、存储性能调优和故障排查至关重要。
联合文件系统概念
联合文件系统(Union Filesystem)将多个只读层和一个可写层叠加为统一的文件系统视图:
Copy-on-Write(写时复制)
CoW 是所有存储驱动的核心策略:
关键机制:
- 读取:从最上层开始查找,找到即返回
- 修改:首次修改时将文件从只读层拷贝到可写层(copy-up),后续在可写层操作
- 删除:在可写层创建 whiteout 文件,遮蔽下层同名文件
- 创建新文件:直接在可写层创建
overlay2(推荐驱动)
overlay2 是当前 Docker 的默认和推荐存储驱动,基于 Linux OverlayFS:
# 检查当前存储驱动
docker info | grep "Storage Driver"
# Storage Driver: overlay2
# 查看镜像层结构
docker inspect nginx:1.25 --format '{{json .GraphDriver.Data}}' | jqOverlayFS 层结构
# 查看容器的 OverlayFS 挂载
docker inspect <container-id> --format '{{.GraphDriver.Data.MergedDir}}'
# 查看挂载信息
mount | grep overlay
# overlay on /var/lib/docker/overlay2/.../merged type overlay
# (lowerdir=...,upperdir=...,workdir=...)overlay2 目录结构
/var/lib/docker/overlay2/
├── l/ # 符号链接(缩短 lowerdir 路径)
│ ├── ABCD1234 -> ../aaa.../diff
│ └── EFGH5678 -> ../bbb.../diff
├── aaa.../ # 镜像层
│ ├── diff/ # 层内容
│ │ ├── etc/
│ │ └── usr/
│ ├── link # 符号链接名
│ ├── lower # 下层引用
│ └── committed # 标记为已提交层
├── bbb.../ # 另一个镜像层
│ ├── diff/
│ ├── link
│ └── lower
└── ccc.../ # 容器层
├── diff/ # 可写层内容
├── link
├── lower
├── merged/ # 合并视图(容器根文件系统)
└── work/ # OverlayFS 工作目录devicemapper
devicemapper 使用 Linux Device Mapper 框架的 thin provisioning 特性,以块级别实现 CoW:
# 配置 devicemapper (direct-lvm 模式)
# /etc/docker/daemon.json
{
"storage-driver": "devicemapper",
"storage-opts": [
"dm.thinpooldev=/dev/mapper/docker-thinpool",
"dm.use_deferred_removal=true",
"dm.use_deferred_deletion=true"
]
}devicemapper 有两种模式:
- loop-lvm(默认,仅用于测试):使用回环设备模拟块设备,性能极差
- direct-lvm(生产环境):直接使用物理块设备
注意:devicemapper 已被 Docker 官方标记为废弃,新部署应使用 overlay2。
btrfs 与 zfs
btrfs
# 配置 btrfs 存储驱动
# 前提:Docker 数据目录在 btrfs 文件系统上
{
"storage-driver": "btrfs"
}btrfs 使用子卷(subvolume)和快照(snapshot)实现层管理,每个镜像层是一个 btrfs 子卷的快照。优点是原生支持快照和文件级 CoW。
zfs
# 配置 zfs 存储驱动
{
"storage-driver": "zfs"
}zfs 使用数据集(dataset)和克隆(clone)实现层管理。zfs 提供数据校验、压缩、快照等高级功能,但资源消耗较大。
层管理
查看镜像层
# 查看镜像历史(每条指令对应一层)
docker history nginx:1.25
# IMAGE CREATED SIZE COMMAND
# <id> 2 weeks ago 0B CMD ["nginx" "-g" "daemon off;"]
# <id> 2 weeks ago 0B EXPOSE 80
# <id> 2 weeks ago 62MB RUN /bin/sh -c set -x && apt-get...
# <id> 2 weeks ago 0B ENV NGINX_VERSION=1.25.3
# <id> 2 weeks ago 77MB FROM ubuntu:22.04优化镜像层
# 反面示例:每条 RUN 创建一层
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y nginx
RUN rm -rf /var/lib/apt/lists/* # 无效!清理在新层,前面的层已固化
# 正面示例:合并 RUN 指令
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
nginx && \
rm -rf /var/lib/apt/lists/* # 同一层清理,有效减小体积多阶段构建
# 构建阶段
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o server .
# 运行阶段 - 仅包含二进制文件
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
CMD ["/server"]存储驱动选择
性能对比
| 操作 | overlay2 | devicemapper | btrfs | zfs |
|---|---|---|---|---|
| 读取 | 好(文件级 CoW) | 好 | 好 | 好 |
| 首次写入 | 中(copy-up 整个文件) | 好(块级 CoW) | 好 | 好 |
| 内存占用 | 低 | 中 | 中 | 高 |
| 大量小文件 | 好 | 中 | 好 | 好 |
| 大文件频繁修改 | 差(copy-up 开销) | 好 | 好 | 好 |
| 部署复杂度 | 低 | 高 | 中 | 高 |
overlay2 的性能注意事项
- copy-up 开销:首次修改大文件时,需将整个文件从只读层拷贝到可写层。对于大文件(如数据库文件),建议使用 Volume 而非容器层
- inode 消耗:每层文件占用独立 inode。大量镜像层可能耗尽 inode
- 元数据性能:层数过多时文件查找需遍历多层,影响
stat()性能
存储配置最佳实践
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.size=20G"
],
"data-root": "/data/docker",
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}关键实践:
- 使用 overlay2,它是性能和兼容性的最佳平衡
- 将 Docker 数据目录放在独立磁盘,避免与系统盘争抢 I/O
- 使用 ext4 或 xfs 作为底层文件系统(xfs 在大规模场景表现更好)
- 频繁写入的数据使用 Volume,而非容器可写层
- 定期清理未使用的镜像和容器:
docker system prune - 监控磁盘空间:
docker system df查看空间占用
总结
存储驱动是 Docker 的核心基础设施之一。overlay2 凭借其简洁的架构、良好的性能和最低的运维成本成为绝大多数场景的首选。理解 Copy-on-Write 机制和层管理有助于优化镜像体积和容器 I/O 性能。对于数据密集型应用,务必使用 Volume 而非容器可写层来存储持久化数据。
贡献者
更新日志
2026/3/14 13:09
查看所有更新日志
9f6c2-feat: organize wiki content and refresh site setup于