Docker存储

Docker存储

📖 快速导航

速看要点:

  • Docker存储驱动原理
  • 数据卷管理
  • 挂载点配置
  • 存储性能优化

本文重点:

  • ✅ 理解Docker存储架构
  • ✅ 掌握数据卷使用
  • ✅ 学会挂载点配置
  • ✅ 掌握存储性能优化

一、Docker存储概述

1.1 存储架构

1.1.1 Docker存储原理

存储架构图:

┌─────────────────────────────────────────┐
│         Docker存储层次结构               │
├─────────────────────────────────────────┤
│                                         │
│  ┌─────────────────────────────────┐   │
│  │   容器可写层 (Container Layer)   │   │
│  └─────────────────────────────────┘   │
│                 ↑                       │
│  ┌─────────────────────────────────┐   │
│  │   镜像只读层 (Image Layers)      │   │
│  │   ┌───────────────────────────┐ │   │
│  │   │ Layer 3: Application      │ │   │
│  │   ├───────────────────────────┤ │   │
│  │   │ Layer 2: Runtime          │ │   │
│  │   ├───────────────────────────┤ │   │
│  │   │ Layer 1: Base OS          │ │   │
│  │   └───────────────────────────┘ │   │
│  └─────────────────────────────────┘   │
│                 ↓                       │
│  ┌─────────────────────────────────┐   │
│  │   存储驱动 (Storage Driver)     │   │
│  │   Overlay2 / AUFS / Btrfs ...   │   │
│  └─────────────────────────────────┘   │
│                 ↓                       │
│  ┌─────────────────────────────────┐   │
│  │   主机文件系统                   │   │
│  │   ext4 / xfs / btrfs ...        │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

东巴文理解:

Docker存储 = 分层存储系统
就像:一本书的每一页都是独立的

存储组成:
├─ 镜像层(Image Layers): 只读,不可修改
├─ 容器层(Container Layer): 可写,存储修改
├─ 存储驱动(Storage Driver): 管理分层存储
└─ 主机文件系统: 底层存储介质

工作原理:
├─ 镜像由多个只读层组成
├─ 容器在镜像层之上添加可写层
├─ 修改文件时使用Copy-on-Write
└─ 存储驱动管理层的合并和访问

1.1.2 Copy-on-Write机制

CoW原理:

Copy-on-Write(写时复制):
├─ 读取文件: 直接从镜像层读取
├─ 修改文件: 复制到容器层,然后修改
├─ 删除文件: 在容器层标记删除
└─ 节省空间,提高效率

示例:
┌─────────────────────────────────────┐
│ 容器层: /etc/config.conf (修改后)   │
├─────────────────────────────────────┤
│ 镜像层: /etc/config.conf (原始)     │
└─────────────────────────────────────┘

读取: 直接读取镜像层
修改: 复制到容器层 → 修改容器层副本
删除: 在容器层创建删除标记

1.2 存储驱动

1.2.1 存储驱动类型

驱动列表:

Overlay2(推荐):
├─ 性能优秀
├─ 稳定性好
├─ 支持主流文件系统
└─ Docker默认驱动

AUFS:
├─ 历史悠久
├─ 稳定性好
├─ 需要内核支持
└─ Ubuntu默认

Btrfs:
├─ 支持快照
├─ 支持压缩
├─ 需要Btrfs文件系统
└─ 适合高级用户

Device Mapper:
├─ 块级存储
├─ 性能一般
├─ RHEL/CentOS默认
└─ 生产环境不推荐

ZFS:
├─ 支持快照
├─ 支持压缩
├─ 需要ZFS文件系统
└─ 适合高级用户

VFS:
├─ 简单实现
├─ 性能较差
├─ 不支持CoW
└─ 仅用于测试

1.2.2 查看存储驱动

查看当前驱动:

# 查看Docker信息
docker info | grep "Storage Driver"

# 输出
Storage Driver: overlay2

# 查看详细信息
docker info | grep -A 10 "Storage Driver"

# 输出
Storage Driver: overlay2
 Backing Filesystem: extfs
 Supports d_type: true
 Native Overlay Diff: true

修改存储驱动:

# 编辑Docker配置文件
vim /etc/docker/daemon.json

# 添加配置
{
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.override_kernel_check=true"
  ]
}

# 重启Docker
systemctl restart docker

# 验证修改
docker info | grep "Storage Driver"

二、数据卷(Volume)

2.1 数据卷概述

2.1.1 数据卷特点

数据卷优势:

数据卷特点:
├─ 独立于容器生命周期
├─ 数据持久化存储
├─ 容器间共享数据
├─ 高效数据迁移
└─ 易于备份和恢复

与容器存储对比:
┌──────────────────┬──────────────┬──────────────┐
│     特性         │  容器存储     │   数据卷      │
├──────────────────┼──────────────┼──────────────┤
│ 生命周期         │ 跟随容器      │ 独立管理      │
│ 数据持久化       │ 容器删除丢失  │ 永久保存      │
│ 容器间共享       │ 不支持        │ 支持         │
│ 性能             │ 较低         │ 较高         │
│ 备份恢复         │ 困难         │ 简单         │
└──────────────────┴──────────────┴──────────────┘

2.1.2 数据卷类型

三种类型:

Volume(命名卷):
├─ Docker管理
├─ 存储在/var/lib/docker/volumes/
├─ 推荐使用
└─ 适合持久化数据

Bind Mount(绑定挂载):
├─ 主机目录挂载
├─ 可以挂载到任意位置
├─ 适合开发环境
└─ 配置文件、代码目录

tmpfs(临时文件系统):
├─ 存储在内存中
├─ 容器停止后消失
├─ 适合敏感数据
└─ 密钥、证书

2.2 管理数据卷

2.2.1 docker volume命令

基本语法:

docker volume COMMAND

COMMAND:
  create      创建数据卷
  inspect     查看数据卷详情
  ls          列出数据卷
  prune       删除未使用的数据卷
  rm          删除数据卷

创建数据卷:

# 创建数据卷
docker volume create myvolume

# 创建数据卷并指定驱动
docker volume create --driver local myvolume

# 创建数据卷并指定选项
docker volume create \
  --driver local \
  --opt type=tmpfs \
  --opt device=tmpfs \
  --opt o=size=100m,uid=1000 \
  myvolume

# 查看数据卷
docker volume ls

# 输出
DRIVER    VOLUME NAME
local     myvolume

查看数据卷:

# 查看数据卷详情
docker volume inspect myvolume

# 输出
[
    {
        "CreatedAt": "2024-01-01T00:00:00Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/myvolume/_data",
        "Name": "myvolume",
        "Options": {},
        "Scope": "local"
    }
]

# 查看数据卷内容
ls /var/lib/docker/volumes/myvolume/_data

删除数据卷:

# 删除指定数据卷
docker volume rm myvolume

# 删除所有未使用的数据卷
docker volume prune

# 输出
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
myvolume

# 强制删除
docker volume prune -f

2.2.2 使用数据卷

创建容器时挂载数据卷:

# 使用-v参数
docker run -d --name mynginx -v myvolume:/usr/share/nginx/html nginx:latest

# 使用--mount参数(推荐)
docker run -d --name mynginx \
  --mount source=myvolume,target=/usr/share/nginx/html \
  nginx:latest

# 查看挂载信息
docker inspect mynginx | grep -A 10 "Mounts"

# 输出
"Mounts": [
    {
        "Type": "volume",
        "Name": "myvolume",
        "Source": "/var/lib/docker/volumes/myvolume/_data",
        "Destination": "/usr/share/nginx/html",
        "Driver": "local",
        "Mode": "z",
        "RW": true,
        "Propagation": ""
    }
]

挂载参数对比:

# -v 参数(简写)
-v myvolume:/usr/share/nginx/html:ro

格式: [卷名]:[容器路径]:[选项]
选项:
  ro: 只读
  rw: 读写(默认)
  z: 共享SELinux标签
  Z: 私有SELinux标签

# --mount 参数(详细)
--mount source=myvolume,target=/usr/share/nginx/html,readonly

格式: key=value,多个参数用逗号分隔
参数:
  source: 数据卷名称
  target: 容器内路径
  readonly: 只读
  volume-driver: 指定驱动

2.3 数据卷应用场景

2.3.1 数据持久化

数据库数据持久化:

# MySQL数据持久化
docker run -d \
  --name mysql \
  -e MYSQL_ROOT_PASSWORD=root123 \
  -v mysql_data:/var/lib/mysql \
  mysql:5.7

# 即使删除容器,数据仍然保留
docker rm -f mysql

# 重新创建容器,挂载相同数据卷
docker run -d \
  --name mysql \
  -e MYSQL_ROOT_PASSWORD=root123 \
  -v mysql_data:/var/lib/mysql \
  mysql:5.7

# 数据仍然存在!

2.3.2 容器间数据共享

共享数据卷:

# 创建共享数据卷
docker volume create shared_data

# 创建容器1,写入数据
docker run -d --name app1 \
  -v shared_data:/data \
  alpine sh -c "echo 'Hello from app1' > /data/hello.txt && sleep 3600"

# 创建容器2,读取数据
docker run -d --name app2 \
  -v shared_data:/data \
  alpine cat /data/hello.txt

# 输出
Hello from app1

# 查看日志
docker logs app2

2.3.3 数据备份与恢复

备份数据卷:

# 备份数据卷到tar文件
docker run --rm \
  -v myvolume:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/myvolume_backup.tar.gz -C /data .

# 查看备份文件
ls -lh myvolume_backup.tar.gz

# 输出
-rw-r--r-- 1 root root 1.2K Jan  1 00:00 myvolume_backup.tar.gz

恢复数据卷:

# 创建新数据卷
docker volume create myvolume_restored

# 恢复数据
docker run --rm \
  -v myvolume_restored:/data \
  -v $(pwd):/backup \
  alpine sh -c "cd /data && tar xzf /backup/myvolume_backup.tar.gz"

# 验证恢复
docker run --rm \
  -v myvolume_restored:/data \
  alpine ls -l /data

三、绑定挂载(Bind Mount)

3.1 绑定挂载概述

3.1.1 绑定挂载特点

特点:

绑定挂载:
├─ 挂载主机目录到容器
├─ 主机目录必须存在
├─ 可以挂载到任意位置
├─ 性能优于数据卷
└─ 适合开发环境

优势:
✅ 性能优秀
✅ 配置简单
✅ 易于访问
✅ 适合开发调试

劣势:
❌ 依赖主机路径
❌ 可移植性差
❌ 安全风险
❌ 不推荐生产环境

3.2 使用绑定挂载

3.2.1 基本使用

挂载主机目录:

# 创建主机目录
mkdir -p /data/nginx/html

# 创建测试文件
echo "Hello Docker" > /data/nginx/html/index.html

# 使用-v参数挂载
docker run -d --name mynginx \
  -v /data/nginx/html:/usr/share/nginx/html \
  -p 8080:80 \
  nginx:latest

# 使用--mount参数挂载(推荐)
docker run -d --name mynginx \
  --mount type=bind,source=/data/nginx/html,target=/usr/share/nginx/html \
  -p 8080:80 \
  nginx:latest

# 访问测试
curl http://localhost:8080

# 输出
Hello Docker

只读挂载:

# 只读挂载
docker run -d --name mynginx \
  --mount type=bind,source=/data/nginx/html,target=/usr/share/nginx/html,readonly \
  -p 8080:80 \
  nginx:latest

# 尝试修改
docker exec mynginx sh -c "echo 'test' > /usr/share/nginx/html/test.txt"

# 输出
sh: can't create /usr/share/nginx/html/test.txt: Read-only file system

3.2.2 应用场景

开发环境:

# 挂载代码目录
docker run -d --name myapp \
  -v /home/user/myapp:/app \
  -p 3000:3000 \
  node:14

# 挂载配置文件
docker run -d --name nginx \
  -v /data/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
  -v /data/nginx/html:/usr/share/nginx/html \
  -p 80:80 \
  nginx:latest

# 挂载日志目录
docker run -d --name app \
  -v /var/log/app:/var/log/app \
  myapp:latest

共享主机文件:

# 共享主机时间
docker run -d --name myapp \
  -v /etc/localtime:/etc/localtime:ro \
  myapp:latest

# 共享主机DNS配置
docker run -d --name myapp \
  -v /etc/resolv.conf:/etc/resolv.conf:ro \
  myapp:latest

# 共享主机证书
docker run -d --name myapp \
  -v /etc/ssl/certs:/etc/ssl/certs:ro \
  myapp:latest

四、tmpfs挂载

4.1 tmpfs概述

4.1.1 tmpfs特点

特点:

tmpfs挂载:
├─ 存储在内存中
├─ 容器停止后数据消失
├─ 读写速度极快
├─ 适合临时数据
└─ 适合敏感数据

优势:
✅ 性能极高
✅ 安全性好
✅ 不占用磁盘
✅ 自动清理

劣势:
❌ 数据不持久
❌ 占用内存
❌ 容量有限

4.2 使用tmpfs

4.2.1 基本使用

创建tmpfs挂载:

# 使用--tmpfs参数
docker run -d --name myapp \
  --tmpfs /tmp \
  myapp:latest

# 使用--mount参数(推荐)
docker run -d --name myapp \
  --mount type=tmpfs,target=/tmp \
  myapp:latest

# 指定大小和权限
docker run -d --name myapp \
  --mount type=tmpfs,target=/tmp,tmpfs-size=100m,tmpfs-mode=1777 \
  myapp:latest

4.2.2 应用场景

临时文件存储:

# 存储临时文件
docker run -d --name myapp \
  --tmpfs /tmp:rw,size=100m \
  myapp:latest

# 存储会话数据
docker run -d --name php \
  --tmpfs /var/lib/php/sessions \
  php:7.4-fpm

敏感数据存储:

# 存储密钥
docker run -d --name myapp \
  --tmpfs /run/secrets \
  myapp:latest

# 存储密码
docker run -d --name mysql \
  -e MYSQL_ROOT_PASSWORD=root123 \
  --tmpfs /var/run/mysqld \
  mysql:5.7

五、存储驱动详解

5.1 Overlay2驱动

5.1.1 Overlay2原理

工作原理:

Overlay2存储结构:
┌─────────────────────────────────────┐
│        Merged View (容器视图)        │
│   /usr /bin /etc /var /home ...     │
└──────────────┬──────────────────────┘
               │
       ┌───────┴───────┐
       │               │
┌──────┴──────┐ ┌──────┴──────┐
│   Upper     │ │   Lower     │
│ (可写层)    │ │ (只读层)    │
│  容器修改    │ │  镜像层     │
└─────────────┘ └─────────────┘

Lower层: 镜像的只读层
Upper层: 容器的可写层
Merged: 合并后的视图
Work: 工作目录

文件操作:

读取文件:
├─ 在Upper层查找
├─ 如果存在,直接读取
└─ 如果不存在,从Lower层读取

修改文件:
├─ 从Lower层复制到Upper层
├─ 在Upper层修改
└─ Lower层保持不变

删除文件:
├─ 在Upper层创建whiteout文件
└─ 标记文件已删除

5.1.2 Overlay2配置

查看配置:

# 查看存储驱动信息
docker info | grep -A 10 "Storage Driver"

# 输出
Storage Driver: overlay2
 Backing Filesystem: extfs
 Supports d_type: true
 Native Overlay Diff: true

优化配置:

# 编辑Docker配置
vim /etc/docker/daemon.json

# 添加配置
{
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.size=20G",
    "overlay2.override_kernel_check=true"
  ]
}

# 重启Docker
systemctl restart docker

5.2 其他存储驱动

5.2.1 Device Mapper

配置Device Mapper:

# 编辑Docker配置
vim /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"
  ]
}

# 重启Docker
systemctl restart docker

5.2.2 Btrfs

配置Btrfs:

# 创建Btrfs文件系统
mkfs.btrfs -f /dev/sdb

# 挂载Btrfs
mount /dev/sdb /var/lib/docker

# 编辑Docker配置
vim /etc/docker/daemon.json

# 添加配置
{
  "storage-driver": "btrfs"
}

# 重启Docker
systemctl restart docker

六、存储性能优化

6.1 性能对比

6.1.1 存储类型性能

性能排序:

性能从高到低:
1. tmpfs (内存)
2. Bind Mount (主机文件系统)
3. Volume (Docker管理)
4. 容器存储 (Overlay2)

说明:
├─ tmpfs: 最快,但数据不持久
├─ Bind Mount: 性能优秀,适合开发
├─ Volume: 性能良好,推荐生产
└─ 容器存储: 性能一般,适合临时数据

6.2 优化建议

6.2.1 存储驱动优化

Overlay2优化:

# 使用高性能文件系统
# 推荐: ext4, xfs

# 调整文件系统参数
tune2fs -O ^has_journal /dev/sda1

# 禁用文件系统访问时间
mount -o remount,noatime /var/lib/docker

# 配置Docker
vim /etc/docker/daemon.json

{
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.override_kernel_check=true"
  ]
}

6.2.2 数据卷优化

选择合适的存储类型:

应用场景选择:
├─ 数据库: Volume + 高性能磁盘
├─ 日志: Bind Mount + 日志轮转
├─ 配置文件: Bind Mount + 只读
├─ 临时数据: tmpfs
└─ 代码目录: Bind Mount

性能优化:
├─ 使用SSD存储
├─ 合理设置挂载选项
├─ 定期清理无用数据
└─ 监控存储使用情况

七、本章小结

7.1 存储类型总结

7.1.1 三种存储类型对比

Volume(命名卷):
├─ Docker管理
├─ 数据持久化
├─ 容器间共享
├─ 易于备份
└─ 推荐生产环境

Bind Mount(绑定挂载):
├─ 主机目录挂载
├─ 性能优秀
├─ 配置简单
├─ 依赖主机路径
└─ 推荐开发环境

tmpfs(临时文件系统):
├─ 内存存储
├─ 性能极高
├─ 数据不持久
├─ 适合敏感数据
└─ 推荐临时数据

7.1.2 存储命令总结

数据卷管理:
├─ docker volume create: 创建数据卷
├─ docker volume ls: 列出数据卷
├─ docker volume inspect: 查看数据卷详情
├─ docker volume rm: 删除数据卷
└─ docker volume prune: 删除未使用的数据卷

容器挂载:
├─ -v: 简写挂载参数
├─ --mount: 详细挂载参数
└─ --tmpfs: tmpfs挂载

7.2 最佳实践

7.2.1 存储选择建议

生产环境:
✅ 使用Volume存储数据
✅ 使用高性能存储介质
✅ 定期备份重要数据
✅ 监控存储使用情况

开发环境:
✅ 使用Bind Mount挂载代码
✅ 使用Bind Mount挂载配置
✅ 方便实时修改和调试

安全建议:
✅ 敏感数据使用tmpfs
✅ 配置文件只读挂载
✅ 定期清理无用数据
✅ 设置存储配额

7.2.2 性能优化建议

存储驱动:
✅ 使用Overlay2驱动
✅ 使用高性能文件系统
✅ 禁用文件系统访问时间
✅ 定期清理无用镜像和容器

数据卷:
✅ 使用SSD存储
✅ 合理设置挂载选项
✅ 避免大量小文件
✅ 定期清理无用数据卷

7.3 下一章预告

下一章: Docker Compose基础

将学习:

  • 🎼 Compose文件语法
  • 🚀 多容器应用编排
  • 📦 服务依赖管理
  • 🔧 环境变量配置

📝 练习题

基础题

  1. 数据卷管理: 创建一个名为"mydata"的数据卷,查看其详情,然后删除它。

  2. 数据持久化: 创建一个MySQL容器,使用数据卷存储数据库文件,插入测试数据后删除容器,重新创建容器验证数据是否保留。

  3. 绑定挂载: 创建一个nginx容器,挂载主机目录到容器,修改主机文件后在浏览器中访问验证。

进阶题

  1. 容器间数据共享: 创建两个容器,使用同一个数据卷共享数据,在一个容器中写入数据,在另一个容器中读取。

  2. 数据备份恢复: 创建一个包含重要数据的数据卷,将其备份到tar文件,然后恢复到新的数据卷中。

  3. 存储性能对比: 创建三个容器,分别使用Volume、Bind Mount和tmpfs存储数据,测试它们的读写性能差异。

实践题

  1. 完整应用存储方案: 为一个Web应用设计存储方案,要求:

    • 数据库数据持久化
    • 应用代码挂载(开发环境)
    • 配置文件只读挂载
    • 日志文件持久化
    • 临时会话数据使用tmpfs
  2. 存储监控脚本: 编写一个脚本,监控Docker存储使用情况,包括:

    • 数据卷使用情况
    • 容器存储使用情况
    • 镜像存储使用情况
    • 存储增长趋势