Docker 存储管理
Docker 提供了多种数据持久化和共享机制,理解这些存储选项对于构建可靠的容器化应用至关重要。本文将详细介绍 Docker 的各种存储解决方案。
目录
存储概述
1.1 容器存储问题
默认情况下,容器写入的数据存储在可写容器层中:
容器层结构:
┌─────────────────────────────────┐
│ 可写容器层 │ ← 数据随容器删除而丢失
├─────────────────────────────────┤
│ 镜像只读层 │
├─────────────────────────────────┤
│ 镜像只读层 │
└─────────────────────────────────┘问题:
- 数据不能持久化
- 难以在容器间共享数据
- 性能开销较大
- 与主机紧密耦合
1.2 Docker 存储选项对比
| 特性 | 数据卷 | 绑定挂载 | tmpfs |
|---|---|---|---|
| 主机位置 | Docker 管理 | 用户指定 | 内存中 |
| 挂载示例 | /var/lib/docker/volumes/ | 任意路径 | 不适用 |
| 支持共享 | 是 | 是 | 否 |
| 非 Docker 进程访问 | 否 | 是 | 否 |
| 持久化 | 是 | 是 | 否 |
| 性能 | 好 | 依赖主机 | 最好 |
数据卷(Volumes)
2.1 创建和管理卷
bash
# 创建命名卷
docker volume create my-data
# 创建带标签的卷
docker volume create \
--label environment=production \
--label project=myapp \
app-data
# 列出所有卷
docker volume ls
# 查看卷详情
docker volume inspect my-data
# 删除卷
docker volume rm my-data
# 清理未使用的卷
docker volume prune2.2 使用数据卷
bash
# 运行容器并挂载卷
docker run -d \
-v my-data:/app/data \
--name myapp \
myimage:latest
# 使用 --mount 语法(推荐)
docker run -d \
--mount source=my-data,target=/app/data \
--name myapp \
myimage:latest
# 只读挂载
docker run -d \
--mount source=my-data,target=/app/data,readonly \
myimage:latest2.3 卷的存储位置
bash
# 查看卷的实际存储路径
docker volume inspect my-data --format='{{ .Mountpoint }}'
# Linux 默认位置
/var/lib/docker/volumes/my-data/_data
# Docker Desktop (Windows/Mac)
# 存储在虚拟机内部,通过 docker volume 命令管理2.4 卷驱动
bash
# 使用本地驱动(默认)
docker volume create --driver local my-volume
# 使用 NFS 驱动
docker volume create \
--driver local \
--opt type=nfs \
--opt o=addr=192.168.1.100,rw \
--opt device=:/path/to/export \
nfs-volume
# 使用第三方驱动(如 RexRay)
docker volume create \
--driver rexray/ebs \
--opt size=20 \
ebs-volume2.5 卷容器模式
bash
# 创建数据卷容器
docker create \
-v /dbdata \
--name dbstore \
postgres:15-alpine \
/bin/true
# 其他容器使用数据卷容器
docker run -d \
--volumes-from dbstore \
--name db1 \
postgres:15-alpine
docker run -d \
--volumes-from dbstore \
--name db2 \
postgres:15-alpine绑定挂载(Bind Mounts)
3.1 基本使用
bash
# 绑定挂载主机目录到容器
docker run -d \
-v /host/path:/container/path \
nginx:alpine
# 使用 --mount 语法
docker run -d \
--mount type=bind,source=/host/path,target=/container/path \
nginx:alpine
# 只读绑定挂载
docker run -d \
--mount type=bind,source=/host/config,target=/etc/nginx,readonly \
nginx:alpine3.2 开发环境应用
bash
# 前端开发 - 实时代码同步
docker run -d \
-p 3000:3000 \
-v $(pwd):/app \
-v /app/node_modules \
--name dev-server \
node:18-alpine \
npm run dev
# Python 开发 - 热重载
docker run -d \
-p 5000:5000 \
-v $(pwd):/app \
--name flask-dev \
python:3.11-slim \
flask run --host=0.0.0.0 --reload3.3 配置文件挂载
bash
# 挂载 Nginx 配置
docker run -d \
-p 80:80 \
-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
-v $(pwd)/html:/usr/share/nginx/html:ro \
nginx:alpine
# 挂载多个配置文件
docker run -d \
--name postgres \
-v $(pwd)/postgres.conf:/etc/postgresql/postgresql.conf:ro \
-v $(pwd)/pg_hba.conf:/etc/postgresql/pg_hba.conf:ro \
postgres:15-alpine \
-c config_file=/etc/postgresql/postgresql.conf3.4 绑定挂载注意事项
bash
# 文件不存在时的行为差异
# -v 语法:自动创建目录(可能导致问题)
docker run -v /nonexistent/path:/container/path alpine
# --mount 语法:如果源不存在则报错(更安全)
docker run --mount type=bind,source=/nonexistent/path,target=/container/path alpine
# 错误:bind source path does not existtmpfs 挂载
4.1 使用 tmpfs
tmpfs 挂载将数据存储在主机内存中,适合敏感数据或临时文件。
bash
# 使用 --tmpfs
docker run -d \
--tmpfs /app/cache:noexec,nosuid,size=100m \
myapp:latest
# 使用 --mount
docker run -d \
--mount type=tmpfs,target=/app/cache,tmpfs-size=100m \
myapp:latest4.2 tmpfs 选项
| 选项 | 说明 |
|---|---|
size | 最大存储空间 |
mode | 文件权限模式 |
uid | 所有者用户 ID |
gid | 所有者组 ID |
noexec | 禁止执行 |
nosuid | 忽略 setuid/setgid |
nodev | 不解释字符/块设备 |
4.3 使用场景
bash
# 敏感数据(访问令牌、密钥)
docker run -d \
--mount type=tmpfs,target=/run/secrets,tmpfs-size=10m \
myapp:latest
# 临时缓存
docker run -d \
--tmpfs /tmp:noexec,nosuid,size=500m \
myapp:latest
# 会话数据
docker run -d \
--mount type=tmpfs,target=/var/lib/php/sessions,tmpfs-size=50m \
php-app:latest存储驱动
5.1 存储驱动概述
存储驱动管理镜像层和容器层的存储。
存储驱动对比:
┌─────────────────┬─────────────┬─────────────┬──────────────┐
│ 驱动 │ 性能 │ 稳定性 │ 适用场景 │
├─────────────────┼─────────────┼─────────────┼──────────────┤
│ overlay2 │ 优秀 │ 优秀 │ 推荐(默认) │
│ fuse-overlayfs │ 良好 │ 良好 │ 无 root 权限 │
│ btrfs │ 优秀 │ 良好 │ 需要 btrfs │
│ zfs │ 优秀 │ 优秀 │ 需要 zfs │
│ devicemapper │ 一般 │ 一般 │ 旧系统兼容 │
└─────────────────┴─────────────┴─────────────┴──────────────┘5.2 查看和配置存储驱动
bash
# 查看当前存储驱动
docker info --format '{{ .Driver }}'
# 查看存储驱动详情
docker info | grep -A 10 "Storage Driver"
# 配置存储驱动(daemon.json)
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}5.3 Overlay2 详解
bash
# 查看 overlay2 存储结构
ls -la /var/lib/docker/overlay2/
# 查看镜像层
docker inspect nginx:alpine --format='{{ .GraphDriver.Data }}'
# 容器层结构
# lowerdir: 底层(镜像层)
# upperdir: 可写层(容器层)
# workdir: 工作目录
# merged: 合并视图5.4 存储驱动性能优化
bash
# 使用 SSD 存储 Docker 数据
# /etc/docker/daemon.json
{
"data-root": "/ssd/docker"
}
# 限制容器日志大小
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
# 启用直接 I/O(特定场景)
{
"storage-opts": [
"overlay2.mountopt=nodev"
]
}数据备份与恢复
6.1 卷备份
bash
# 方法 1: 使用 tar 备份
docker run --rm \
-v my-data:/data \
-v $(pwd):/backup \
alpine \
tar czf /backup/my-data-backup.tar.gz -C /data .
# 方法 2: 使用 cp 命令
docker cp my-container:/app/data $(pwd)/backup/
# 方法 3: 使用卷快照(支持快照的存储驱动)
# btrfs 快照
btrfs subvolume snapshot /var/lib/docker/volumes/my-data \
/var/lib/docker/volumes/my-data-snapshot6.2 卷恢复
bash
# 从 tar 归档恢复
docker run --rm \
-v my-data:/data \
-v $(pwd):/backup \
alpine \
sh -c "cd /data && tar xzf /backup/my-data-backup.tar.gz"
# 使用 cp 命令恢复
docker cp $(pwd)/backup/. my-container:/app/data/6.3 自动化备份脚本
bash
#!/bin/bash
# backup-volumes.sh
VOLUMES="db-data app-data cache-data"
BACKUP_DIR="/backup/docker-volumes"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
for volume in $VOLUMES; do
echo "Backing up $volume..."
docker run --rm \
-v $volume:/data \
-v $BACKUP_DIR:/backup \
alpine \
tar czf /backup/${volume}_${DATE}.tar.gz -C /data .
done
# 清理旧备份(保留最近 7 天)
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
echo "Backup completed!"6.4 跨主机迁移
bash
# 源主机:导出卷
docker run --rm \
-v my-data:/data \
alpine tar czf - -C /data . > my-data.tar.gz
# 传输到目标主机
scp my-data.tar.gz user@target-host:/tmp/
# 目标主机:导入卷
docker volume create my-data
# 方法 1: 使用临时容器
docker run --rm \
-v my-data:/data \
-v /tmp:/backup \
alpine sh -c "cd /data && tar xzf /backup/my-data.tar.gz"
# 方法 2: 使用 stdin
cat my-data.tar.gz | docker run --rm \
-i -v my-data:/data \
alpine tar xzf - -C /data存储最佳实践
7.1 选择合适的存储类型
yaml
# Docker Compose 示例
version: '3.8'
services:
web:
image: nginx:alpine
volumes:
# 配置文件使用绑定挂载
- ./nginx.conf:/etc/nginx/nginx.conf:ro
# 静态资源使用绑定挂载
- ./html:/usr/share/nginx/html:ro
app:
image: myapp:latest
volumes:
# 应用数据使用命名卷
- app-data:/app/data
# 临时文件使用 tmpfs
- type: tmpfs
target: /app/tmp
tmpfs:
size: 100M
db:
image: postgres:15-alpine
volumes:
# 数据库使用命名卷
- db-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db-password
secrets:
- db-password
volumes:
app-data:
driver: local
db-data:
driver: local
secrets:
db-password:
file: ./secrets/db-password.txt7.2 性能优化
bash
# 1. 使用卷而不是绑定挂载(生产环境)
# 卷的 I/O 性能更好
# 2. 避免在容器中存储大量小文件
# 使用卷存储数据,避免复制到镜像层
# 3. 合理使用缓存
# Dockerfile 中先复制依赖文件,再复制代码
# 4. 日志管理
{
"log-driver": "local",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
# 5. 监控磁盘使用
docker system df -v7.3 安全最佳实践
bash
# 1. 敏感数据使用 tmpfs 或 secrets
docker run -d \
--mount type=tmpfs,target=/run/secrets \
myapp:latest
# 2. 配置文件使用只读挂载
docker run -d \
--mount type=bind,source=/host/config,target=/etc/app,readonly \
myapp:latest
# 3. 限制卷大小(使用 btrfs/zfs)
docker volume create --driver local \
--opt type=btrfs \
--opt size=10G \
limited-volume
# 4. 定期清理未使用数据
docker volume prune -f
docker system prune -a --volumes7.4 监控和维护
bash
# 查看卷使用情况
docker system df -v
# 查看容器磁盘 I/O
docker stats --format "table {{.Name}}\t{{.BlockIO}}"
# 查找大文件
docker run --rm -v my-volume:/data alpine \
sh -c "find /data -type f -size +100M -exec ls -lh {} \;"
# 清理容器日志
# 方法 1: 清空日志文件
docker exec container sh -c 'truncate -s 0 /var/log/app/*.log'
# 方法 2: 配置日志轮转
docker run -d \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
myapp:latest故障排查
8.1 常见问题
bash
# 卷挂载失败
# 检查权限
ls -la /var/lib/docker/volumes/my-volume/_data
# 检查 SELinux(如果启用)
docker run -d -v my-volume:/data:z myapp:latest # 共享标签
docker run -d -v my-volume:/data:Z myapp:latest # 私有标签
# 磁盘空间不足
# 检查 Docker 磁盘使用
docker system df
# 清理未使用资源
docker system prune -a --volumes8.2 调试存储问题
bash
# 查看容器挂载点
docker inspect container --format='{{ json .Mounts }}'
# 进入容器查看文件系统
docker exec -it container sh -c 'df -h && mount'
# 检查存储驱动日志
journalctl -u docker.service | grep -i storage