跳到主要内容

Docker 数据卷管理

数据持久化是容器化应用的关键需求。本章将详细介绍 Docker 的存储机制和数据管理方法。

存储基础概念

容器层的局限性

默认情况下,容器内创建的所有文件都存储在可写容器层上:

┌─────────────────────────────────────┐
│ 可写容器层 │ ← 容器特有数据
├─────────────────────────────────────┤
│ 只读镜像层 │ ← 基础镜像
└─────────────────────────────────────┘

问题

  • 容器删除后数据丢失
  • 数据难以在容器间共享
  • 写入性能较低(需要存储驱动)

存储挂载类型

Docker 提供三种将数据存储在容器外的方式:

类型说明管理方式适用场景
VolumesDocker 管理的持久化存储Docker 管理生产环境、数据持久化
Bind Mounts主机目录直接挂载用户管理开发环境、配置文件
tmpfs内存存储临时存储敏感数据、缓存

数据卷(Volumes)

什么是数据卷?

数据卷是 Docker 管理的持久化存储:

  • 存储在主机的特定目录(/var/lib/docker/volumes/
  • 由 Docker 完全管理
  • 与容器生命周期独立
  • 可在多个容器间共享

创建和管理数据卷

# 创建数据卷
docker volume create my-volume

# 列出所有数据卷
docker volume ls

# 查看数据卷详情
docker volume inspect my-volume

# 输出示例
[
{
"CreatedAt": "2024-01-15T10:00:00Z",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/my-volume/_data",
"Name": "my-volume",
"Scope": "local"
}
]

# 删除数据卷
docker volume rm my-volume

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

# 查看数据卷占用空间
docker system df -v

使用数据卷

基本用法

# 使用 --mount(推荐,更明确)
docker run -d \
--name web \
--mount source=my-volume,target=/app \
nginx:latest

# 使用 -v(简写)
docker run -d \
--name web \
-v my-volume:/app \
nginx:latest

语法对比

# --mount 语法
--mount type=volume,source=<卷名>,destination=<容器路径>[,options]

# -v 语法
-v <卷名>:<容器路径>[:<选项>]

挂载选项

# 只读挂载
docker run -d \
--name web \
--mount source=config,target=/etc/nginx,readonly \
nginx:latest

# 使用 -v 简写
docker run -d --name web -v config:/etc/nginx:ro nginx

命名卷与匿名卷

# 命名卷:有明确名称
docker run -v mydata:/data myapp

# 匿名卷:随机生成名称
docker run -v /data myapp

# Dockerfile 中定义的匿名卷
# Dockerfile:
# VOLUME /data

# 查看匿名卷
docker volume ls

# 匿名卷在容器删除时自动删除(使用 --rm)
docker run --rm -v /data myapp

数据卷预填充

当挂载空数据卷到有数据的容器目录时,容器中的内容会自动复制到数据卷:

# nginx 镜像的 /usr/share/nginx/html 有默认内容
# 挂载空卷时,默认内容会复制到卷中
docker run -d \
--name nginx-test \
-v nginx-html:/usr/share/nginx/html \
nginx:latest

# 验证
docker volume inspect nginx-html
# 数据已复制到卷中

挂载子目录

# 只挂载卷的子目录
docker run -d \
--name app \
--mount source=logs,target=/var/log/app,volume-subpath=app1 \
myapp:latest

使用卷驱动

# 创建使用特定驱动的卷
docker volume create \
--driver local \
--opt type=nfs \
--opt o=addr=10.0.0.1,rw \
--opt device=:/export/data \
nfs-volume

# 创建 NFS 卷
docker volume create \
--driver local \
--opt type=nfs \
--opt o=addr=192.168.1.100,nfsvers=4 \
--opt device=:/export/shared \
nfs-shared

绑定挂载(Bind Mounts)

什么是绑定挂载?

绑定挂载将主机上的文件或目录直接映射到容器中:

# 主机目录结构
/home/user/app/
├── src/
├── config/
└── data/

# 绑定挂载到容器
docker run -v /home/user/app:/app myapp

使用绑定挂载

# 使用 --mount(推荐)
docker run -d \
--name dev-app \
--mount type=bind,source=/home/user/app,target=/app \
myapp:latest

# 使用 -v
docker run -d \
--name dev-app \
-v /home/user/app:/app \
myapp:latest

# 只读绑定挂载
docker run -d \
--name web \
--mount type=bind,source=/home/user/config,target=/etc/nginx,readonly \
nginx:latest

绑定挂载的特点

优点

  • 主机和容器可以同时访问文件
  • 适合开发环境(代码热更新)
  • 配置文件管理方便

缺点

  • 依赖主机目录结构
  • 可移植性差
  • 安全风险(可访问主机任意目录)

常用场景

开发环境

# 挂载源代码目录,支持热更新
docker run -d \
--name dev-server \
--mount type=bind,source=/home/user/project,target=/app \
-p 3000:3000 \
node:18 \
npm run dev

配置文件挂载

# 挂载 Nginx 配置
docker run -d \
--name nginx \
--mount type=bind,source=/home/user/nginx.conf,target=/etc/nginx/nginx.conf,readonly \
-p 80:80 \
nginx:latest

# 挂载多个配置文件
docker run -d \
--name app \
--mount type=bind,source=./config/app.yaml,target=/app/config.yaml \
--mount type=bind,source=./config/database.yaml,target=/app/database.yaml \
myapp:latest

tmpfs 挂载

什么是 tmpfs?

tmpfs 将数据存储在内存中,不会写入磁盘:

  • 临时存储,容器停止后数据消失
  • 高性能(内存速度)
  • 安全(敏感数据不留存)

使用 tmpfs

# 创建 tmpfs 挂载
docker run -d \
--name app \
--mount type=tmpfs,destination=/tmp \
myapp:latest

# 指定大小限制
docker run -d \
--name app \
--mount type=tmpfs,destination=/tmp,tmpfs-size=100m \
myapp:latest

# 禁止执行权限
docker run -d \
--name app \
--mount type=tmpfs,destination=/tmp,tmpfs-mode=1770 \
myapp:latest

适用场景

  • 临时缓存目录
  • 敏感信息存储(如密码、密钥)
  • 高速临时数据处理
# 示例:Redis 使用 tmpfs 作为临时存储
docker run -d \
--name redis \
--mount type=tmpfs,destination=/data,tmpfs-size=512m \
redis:alpine

存储类型对比

特性VolumesBind Mountstmpfs
主机位置Docker 管理用户指定内存
持久化
性能最高极高
可移植性N/A
多容器共享
远程存储支持不支持不支持
适用场景生产环境开发环境临时数据

选择建议

生产数据持久化 → Volumes
开发环境代码挂载 → Bind Mounts
敏感临时数据 → tmpfs

数据卷备份与恢复

备份数据卷

# 方法 1:使用临时容器备份
docker run --rm \
-v my-volume:/source:ro \
-v $(pwd):/backup \
alpine \
tar czf /backup/my-volume-backup.tar.gz -C /source .

# 方法 2:备份运行中容器的卷
docker run --rm \
--volumes-from my-container \
-v $(pwd):/backup \
alpine \
tar czf /backup/backup.tar.gz /data

恢复数据卷

# 创建新卷
docker volume create restored-volume

# 恢复数据
docker run --rm \
-v restored-volume:/target \
-v $(pwd):/backup \
alpine \
tar xzf /backup/my-volume-backup.tar.gz -C /target

数据迁移

# 从容器 A 迁移到容器 B
# 1. 创建临时容器备份数据
docker run --rm \
--volumes-from source-container \
-v $(pwd):/backup \
alpine \
tar czf /backup/data.tar.gz /data

# 2. 恢复到目标容器
docker run --rm \
--volumes-from target-container \
-v $(pwd):/backup \
alpine \
tar xzf /backup/data.tar.gz -C /

Docker Compose 中的数据卷

基本配置

# compose.yaml
services:
db:
image: mysql:8.0
volumes:
- db-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: secret

app:
image: myapp
volumes:
- ./src:/app/src # 绑定挂载
- app-data:/app/data # 命名卷

volumes:
db-data: # 声明命名卷
app-data:

高级配置

services:
app:
image: myapp
volumes:
# 命名卷
- type: volume
source: app-data
target: /app/data
volume:
nocopy: true

# 绑定挂载
- type: bind
source: ./config
target: /app/config
read_only: true

# tmpfs
- type: tmpfs
target: /tmp
tmpfs:
size: 100M

volumes:
app-data:
driver: local
driver_opts:
type: nfs
o: addr=192.168.1.100,nfsvers=4
device: ":/export/data"

# 使用外部卷
external-data:
external: true

开发环境配置

# compose.dev.yaml
services:
app:
volumes:
- ./src:/app/src # 代码目录
- ./config:/app/config # 配置目录
- /app/node_modules # 匿名卷(避免覆盖)

共享数据

容器间共享卷

# 多个容器共享同一个卷
docker run -d --name writer -v shared-data:/data myapp
docker run -d --name reader -v shared-data:/data:ro myapp

使用 --volumes-from

# 从另一个容器挂载所有卷
docker run -d --name data-container -v /data busybox

# 其他容器使用数据容器的卷
docker run -d --name app1 --volumes-from data-container myapp
docker run -d --name app2 --volumes-from data-container myapp

注意--volumes-from 已不推荐使用,建议直接使用命名卷。

实战示例

数据库持久化

# MySQL 数据持久化
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: myapp
volumes:
- mysql-data:/var/lib/mysql
- ./init-scripts:/docker-entrypoint-initdb.d:ro
ports:
- "3306:3306"

volumes:
mysql-data:

Web 应用开发环境

# 开发环境配置
services:
frontend:
image: node:18
working_dir: /app
volumes:
- ./frontend:/app # 源代码
- /app/node_modules # 保持容器内的 node_modules
ports:
- "3000:3000"
command: npm run dev

backend:
image: python:3.11
working_dir: /app
volumes:
- ./backend:/app
- backend-cache:/root/.cache
ports:
- "8000:8000"
command: python manage.py runserver 0.0.0.0:8000

db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
volumes:
- db-data:/var/lib/postgresql/data
ports:
- "5432:5432"

volumes:
backend-cache:
db-data:

日志收集

services:
app:
image: myapp
volumes:
- app-logs:/var/log/app

log-collector:
image: logstash
volumes:
- app-logs:/var/log/app:ro
depends_on:
- app

volumes:
app-logs:

清理未使用的卷

# 查看所有卷
docker volume ls

# 查看卷使用情况
docker system df -v

# 删除未使用的卷
docker volume prune

# 强制删除
docker volume prune -f

# 删除所有卷(谨慎使用)
docker volume rm $(docker volume ls -q)

最佳实践

1. 生产环境使用命名卷

# 推荐:使用命名卷
docker run -v myapp-data:/data myapp

# 不推荐:使用绑定挂载(依赖主机目录)
docker run -v /home/user/data:/data myapp

2. 开发环境使用绑定挂载

# 适合开发环境:代码实时更新
docker run -v ./src:/app/src myapp

3. 敏感数据使用 tmpfs 或 secrets

# 使用 tmpfs 存储临时敏感数据
docker run --mount type=tmpfs,target=/secrets myapp

# Docker Swarm secrets
docker service create --secret db_password myapp

4. 定期备份重要数据

# 定时备份脚本
#!/bin/bash
docker run --rm \
-v production-db:/source:ro \
-v /backup:/backup \
alpine \
tar czf /backup/db-$(date +%Y%m%d).tar.gz -C /source .

5. 使用 .dockerignore 避免挂载冲突

# .dockerignore
node_modules
.git
.env

小结

本章我们学习了:

  1. 存储类型:Volumes、Bind Mounts、tmpfs
  2. 数据卷管理:创建、查看、删除
  3. 绑定挂载:开发环境应用
  4. tmpfs:临时内存存储
  5. 数据备份恢复:迁移和容灾
  6. Docker Compose 配置:声明式管理

练习

  1. 创建一个命名卷并在两个容器间共享数据
  2. 使用绑定挂载配置一个开发环境
  3. 备份一个数据卷并恢复到新卷
  4. 配置 tmpfs 挂载用于临时缓存