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。

主流存储驱动对比

驱动文件系统适用场景性能
overlay2OverlayFS默认推荐,所有 Linux 发行版
devicemapperdevicemapper老系统、RHEL/CentOS 6一般
btrfsBtrfs需要快照、子卷管理写入好,读取一般
zfsZFS需要数据完整性校验功能强但内存需求大

overlay2:默认且最优

overlay2 是当前 Docker 的默认存储驱动,也是性能最好的:

bash
# 查看当前存储驱动 docker info --format '{{.Driver}}' # overlay2

overlay2 的工作方式:

shell
lowerdir(只读层)= 镜像的所有层 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
shell
TYPE 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:

yaml
services: 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——它已经是默认选项了。除非有明确的特殊需求,否则不需要改存储驱动。

标签:Docker