📘 第06课:数据卷

基础 掌握Docker数据持久化的三种方式,理解卷与绑定挂载的区别与应用场景

💾 为什么需要数据持久化?

容器文件系统是临时的——容器删除后所有数据丢失。这是容器的核心设计理念(不可变基础设施),但应用数据必须持久化:

容器文件系统(临时) ┌─────────────────────────┐ │ 可写层(Container Layer)│ ← 容器删除即消失 ├─────────────────────────┤ │ 只读层(Image Layers) │ ← 镜像提供,不可修改 └─────────────────────────┘ 数据持久化方案 ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Volume │ │Bind Mount│ │ tmpfs │ │Docker管理 │ │主机目录映射│ │内存文件系统│ │推荐使用 │ │开发调试 │ │临时数据 │ └──────────┘ └──────────┘ └──────────┘

📦 Volume(命名卷)

Volume由Docker管理,存储在 /var/lib/docker/volumes/ 下,是推荐的持久化方式:

# 创建命名卷
docker volume create my-data
# my-data

# 使用卷运行容器
docker run -d --name db1 \
  -v my-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:8.0-oracle

# 查看卷信息
docker volume inspect my-data
# [{
#     "CreatedAt": "2024-01-15T10:30:00Z",
#     "Driver": "local",
#     "Mountpoint": "/var/lib/docker/volumes/my-data/_data",
#     "Name": "my-data",
#     "Scope": "local"
# }]

# 列出所有卷
docker volume ls

卷的共享与只读

# 多容器共享同一卷
docker run -d --name writer \
  -v shared-data:/app/data \
  alpine:latest \
  sh -c "echo 'Hello from writer' > /app/data/message && sleep 3600"

docker run --rm \
  -v shared-data:/app/data \
  alpine:latest \
  cat /app/data/message
# Hello from writer ✅ 数据卷共享验证通过

# 只读挂载
docker run -d --name reader \
  -v shared-data:/app/data:ro \
  alpine:latest sh -c "sleep 3600"

docker exec reader sh -c "echo 'test' > /app/data/test"
# sh: can't create /app/data/test: Read-only file system ✅

使用 --mount 语法(推荐)

# --mount 更明确,推荐在生产环境使用
docker run -d --name mount-demo \
  --mount type=volume,source=my-data,target=/app/data \
  nginx:alpine

# bind mount 用 --mount
docker run -d --name bind-demo \
  --mount type=bind,source=/tmp/html,target=/usr/share/nginx/html \
  nginx:alpine

# tmpfs 用 --mount
docker run -d --name tmpfs-demo \
  --mount type=tmpfs,target=/app/cache,tmpfs-size=100000000 \
  nginx:alpine

📁 Bind Mount(绑定挂载)

将主机指定目录直接挂载到容器,适合开发环境代码热更新:

# 创建主机目录
mkdir -p /tmp/docker-html

# 绑定挂载——开发代码热更新
echo "<h1>Hello from host</h1>" > /tmp/docker-html/index.html

docker run -d --name dev-web \
  -v /tmp/docker-html:/usr/share/nginx/html:ro \
  -p 8085:80 \
  nginx:alpine

curl http://localhost:8085
# <h1>Hello from host</h1> ✅ 绑定挂载验证通过

# 修改主机文件,容器立即看到变化
echo "<h1>Updated!</h1>" > /tmp/docker-html/index.html
curl http://localhost:8085
# <h1>Updated!</h1> ✅ 热更新验证通过
⚠️ Bind Mount风险:容器可以修改主机文件系统!使用 :ro 只读挂载或确保挂载路径正确。容器内以root运行时尤其危险。

🧠 tmpfs挂载

tmpfs存储在内存中,容器停止后数据消失,适合敏感临时数据:

# tmpfs挂载
docker run -d --name tmp-test \
  --tmpfs /app/cache:size=100m,mode=1777 \
  alpine:latest \
  sh -c "dd if=/dev/zero of=/app/cache/test bs=1M count=50 && sleep 3600"

docker exec tmp-test df -h /app/cache
# Filesystem  Size  Used Available  Use%  Mounted on
# tmpfs       100M   50M     50M    50%  /app/cache ✅

# 容器停止后tmpfs数据消失
docker stop tmp-test && docker start tmp-test
docker exec tmp-test ls /app/cache/
# (空目录) ✅ tmpfs数据不持久

⚖️ 三种方式对比

特性VolumeBind Mounttmpfs
存储位置/var/lib/docker/volumes/主机任意目录内存
管理方式Docker CLI管理手动管理自动清理
可移植性✅ 好❌ 依赖主机路径N/A
性能良好优秀(直接主机IO)最优(内存)
适合场景生产数据持久化开发环境代码挂载临时/敏感数据
备份docker volume备份直接备份主机目录无需
多容器共享✅ 支持✅ 支持❌ 不支持
驱动插件✅ 支持NFS等❌ 仅本地❌ 仅本地

🧪 实战:数据库持久化与备份

# 1. 创建数据卷
docker volume create pg-data

# 2. 启动PostgreSQL
docker run -d --name my-postgres \
  -v pg-data:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 \
  postgres:16-alpine

# 3. 写入数据
docker exec my-postgres psql -U postgres -c \
  "CREATE TABLE users (id SERIAL, name TEXT); INSERT INTO users (name) VALUES ('Alice'), ('Bob');"

docker exec my-postgres psql -U postgres -c "SELECT * FROM users;"
#  id | name  
# ----+-------
#   1 | Alice
#   2 | Bob ✅ 数据写入成功

# 4. 删除容器
docker rm -f my-postgres

# 5. 用同一卷重新创建——数据还在
docker run -d --name my-postgres2 \
  -v pg-data:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:16-alpine

docker exec my-postgres2 psql -U postgres -c "SELECT * FROM users;"
# 数据持久化验证通过 ✅

# 6. 备份卷到tar文件
docker run --rm \
  -v pg-data:/source:ro \
  -v /tmp:/backup \
  alpine:latest \
  tar czf /backup/pg-backup.tar.gz -C /source .

# 7. 恢复卷
docker volume create pg-data-restored
docker run --rm \
  -v pg-data-restored:/target \
  -v /tmp:/backup \
  alpine:latest \
  tar xzf /backup/pg-backup.tar.gz -C /target

# 8. 清理
docker rm -f my-postgres2
docker volume rm pg-data pg-data-restored

🔧 卷驱动与高级特性

使用NFS卷驱动

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

# 使用NFS卷(多主机共享)
docker run -d --name shared-app \
  -v nfs-volume:/app/data \
  nginx:alpine

卷标签与过滤

# 带标签创建卷
docker volume create \
  --label env=production \
  --label app=database \
  prod-db-data

# 按标签过滤
docker volume ls --filter label=env=production

🔧 故障排查

挂载后容器目录为空 如果卷是空的且容器目录有内容,Docker会将容器目录内容复制到新卷。但如果卷已有数据,则容器目录内容被卷覆盖。
Bind mount权限问题 容器内进程通常以root运行,但如果应用以非root用户运行,需要确保主机目录权限匹配。可用 --user $(id -u):$(id -g) 修正。
docker volume prune 误删卷 命名卷在无容器引用时也会被清理。重要数据定期备份。
挂载后容器性能差 Bind mount 在macOS/Windows上性能较差。Linux上两者接近。可考虑使用 :cached:delegated 优化。

📝 练习

  1. 卷共享实验:创建命名卷,启动两个Alpine容器挂载它,一个写入文件,另一个读取验证
  2. Bind mount开发:在主机创建HTML文件,通过bind mount挂载到Nginx,修改文件观察热更新
  3. 卷备份恢复:创建含数据的卷,备份为tar,删除后从备份恢复
  4. tmpfs性能测试:分别使用Volume和tmpfs运行I/O密集任务,对比性能差异
  5. --mount vs -v:分别用两种语法挂载相同的卷,对比 docker inspect 输出的差异

🏆 成就解锁:数据持久化专家

📚 深度补充:数据卷进阶要点

【容器基础阶段】生产环境注意事项

要点说明最佳实践
资源规划根据业务负载合理分配CPU/内存先压测再上线,设置requests和limits
监控告警设置关键指标阈值和告警规则Prometheus + AlertManager,5分钟P99延迟告警
备份策略定期备份关键数据和配置自动化备份脚本 + 异地存储 + 定期恢复演练
灰度发布新版本逐步放量降低风险金丝雀发布5%→20%→50%→100%
回滚预案部署前确认回滚方案和步骤保留前一版本镜像,数据库迁移向前兼容
文档更新配置变更必须同步更新文档GitOps管理配置,变更即文档
安全基线遵循CIS Docker Benchmark非root运行、只读FS、最小能力
日志规范结构化日志,统一格式JSON格式日志,包含traceId

常见误区与避坑指南

  1. 过度配置:不是所有服务都需要高可用,根据实际需求选择架构复杂度,避免过度工程
  2. 忽略日志:日志是排障的关键,确保日志格式统一、级别合理、采集完整
  3. 资源超卖:容器资源限制不是摆设,超卖会导致性能下降甚至OOM Kill
  4. 安全忽视:默认配置不等于安全配置,生产环境必须加固(非root、只读FS、最小能力)
  5. 监控缺失:没有监控等于盲飞,至少要有基础的健康检查和资源监控
  6. 手动运维:能自动化的绝不手动,手动操作容易出错且不可追溯
  7. 忽略网络策略:默认所有容器互通不安全,应按最小权限原则配置网络

进阶阅读与参考

Docker命令速查卡

# 容器生命周期
docker create/start/stop/restart/rm/pause/unpause
docker logs/top/stats/inspect/exec diff

# 镜像构建
docker build/pull/push/tag/rmi/images/history
docker save/load/import/manifest

# 网络与存储
docker network create/ls/inspect/connect/disconnect/rm/prune
docker volume create/ls/inspect/rm/prune

# Docker Compose
docker compose up/down/ps/logs/build/exec
docker compose config/stop/start/scale/top/cp

# 系统维护
docker system df/prune/info
docker builder prune
docker container/prune/image prune

# K8s常用命令
kubectl get/describe/logs/exec/apply/delete
kubectl rollout status/undo/history
kubectl scale/autoscale/set

本阶段知识脉络

容器基础阶段知识体系: 核心概念实操演练最佳实践故障排查 每一课都遵循:概念讲解 → 命令实操 → 代码示例 → 常见问题 → 练习巩固 确保学完即能上手,理论实践并重。