6月6日 20:23
Docker 存储驱动该选哪个?overlay2 和其他驱动的区别
Docker 镜像的每一层怎么存、容器怎么在只读层上写入、不同存储驱动有什么性能差异——理解存储驱动的工作原理,能帮你解决容器写入慢、镜像占磁盘、数据丢失这些问题。
容器文件系统的分层结构
Docker 镜像由多个只读层组成,容器启动时在最上面加一层可写层:
shell┌─────────────────────┐ │ 可写层(容器层) │ ← docker run 产生的可写层 ├─────────────────────┤ │ 镜像层 3(App) │ ← COPY / RUN 指令产生的层 ├─────────────────────┤ │ 镜像层 2(Runtime)│ ├─────────────────────┤ │ 镜像层 1(OS 基础) │ └─────────────────────┘
- 只读层:镜像的每一层都是只读的,多个容器可以共享同一组只读层
- 可写层:容器的所有修改(新建文件、修改配置、写日志)都写在这层
- 容器删除后,可写层一起消失——这就是为什么容器内写的数据会丢
Copy-on-Write 机制
容器修改镜像中的文件时,不是直接改只读层——而是把文件复制到可写层再修改:
bash# 镜像里有个 /etc/config.yml # 容器内修改它: echo "new value" >> /etc/config.yml # 发生了什么: # 1. 从只读层复制 config.yml 到可写层 # 2. 在可写层修改 # 3. 后续读这个文件时读可写层的版本(遮盖了只读层的原始文件)
CoW 的性能影响:第一次修改大文件时会比较慢(需要完整复制)。频繁修改大文件(比如数据库的数据文件)不适合放在容器可写层——应该用 Volume。
主流存储驱动对比
| 驱动 | 文件系统 | 适用场景 | 性能 |
|---|---|---|---|
| overlay2 | OverlayFS | 默认推荐,所有 Linux 发行版 | 好 |
| devicemapper | devicemapper | 老系统、RHEL/CentOS 6 | 一般 |
| btrfs | Btrfs | 需要快照、子卷管理 | 写入好,读取一般 |
| zfs | ZFS | 需要数据完整性校验 | 功能强但内存需求大 |
overlay2:默认且最优
overlay2 是当前 Docker 的默认存储驱动,也是性能最好的:
bash# 查看当前存储驱动 docker info --format '{{.Driver}}' # overlay2
overlay2 的工作方式:
shelllowerdir(只读层)= 镜像的所有层 upperdir(可写层)= 容器的修改 merged = 合并视图(容器看到的文件系统)
优势:
- 只有一层 lowerdir 目录,不像老版 overlay 有多层,inode 消耗少
- 页缓存共享——多个容器读同一个文件只占一份内存
- build 性能好——Docker Build 时层操作都是 O(1)
99% 的场景用 overlay2 就对了,不需要换。
devicemapper:老系统的选择
RHEL/CentOS 6 时代内核不支持 OverlayFS,只能用 devicemapper。有两种模式:
- loopback(默认):用文件模拟块设备,性能差,生产环境不能用
- direct-lvm:直接用 LVM 块设备,性能可以
json// /etc/docker/daemon.json - direct-lvm 配置 { "storage-driver": "devicemapper", "storage-opts": [ "dm.directlvm_device=/dev/sdb", "dm.thinp_percent=95", "dm.thinp_metapercent=1" ] }
现在大部分系统已经支持 overlay2,不需要再折腾 devicemapper。
存储驱动和磁盘占用的关系
每个存储驱动管理镜像层的方式不同,磁盘占用差异很大:
bash# 查看 Docker 占用的磁盘 docker system df # 详细信息 docker system df -v
shellTYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 15 5 4.2GB 2.1GB (50%) Containers 8 3 120MB 80MB (66%) Local Volumes 5 3 800MB 200MB (25%) Build Cache 50 0 1.5GB 1.5GB (100%)
RECLAIMABLE 是可以回收的空间。清理命令:
bash# 清理悬空镜像(没有标签的) docker image prune # 清理所有未使用的镜像 docker image prune -a # 清理构建缓存 docker builder prune # 一键全清 docker system prune -a
Volume:数据持久化的正确方式
容器可写层的数据会随容器删除而丢失。需要持久化的数据(数据库、日志、配置)用 Volume:
yamlservices: postgres: image: postgres:16 volumes: - pg_data:/var/lib/postgresql/data # 命名 Volume - ./init.sql:/docker-entrypoint-initdb.d/init.sql # 绑定挂载 app: image: myapp:latest volumes: - ./config:/app/config:ro # 只读绑定挂载 - app_logs:/app/logs # 命名 Volume volumes: pg_data: app_logs:
命名 Volume vs 绑定挂载
| 命名 Volume | 绑定挂载 | |
|---|---|---|
| 存储位置 | Docker 管理(/var/lib/docker/volumes/) | 宿主机任意目录 |
| 性能 | 好(Docker 优化) | Mac/Win 上可能慢 |
| 可移植性 | 好 | 依赖宿主机路径 |
| 备份 | docker volume inspect 找路径 | 直接访问宿主机目录 |
| 适用场景 | 数据库、应用数据 | 配置文件、代码挂载 |
存储驱动选择决策
| 你的情况 | 推荐驱动 |
|---|---|
| Linux 内核 4.0+ | overlay2(默认) |
| RHEL/CentOS 7+ | overlay2 |
| 需要 ZFS 快照和校验 | zfs |
| 需要 Btrfs 子卷管理 | btrfs |
| 老系统无法升级内核 | devicemapper (direct-lvm) |
不确定就用 overlay2——它已经是默认选项了。除非有明确的特殊需求,否则不需要改存储驱动。