15.8.5 数据卷与持久化策略(volumes、bind)

数据卷与持久化策略是多容器编排中确保数据可靠性的核心。本节聚焦 Compose 中的两类持久化方式:命名卷(volumes)与绑定挂载(bind),并给出可运行示例、命令解释、排错与练习。

原理草图(命名卷与绑定挂载对比):

文章图片

1. 完整示例:命名卷 + 绑定挂载#

目录结构:

demo-compose/
├── docker-compose.yml
├── app/
│   └── config/
│       └── app.conf
└── logs/

docker-compose.yml:

version: "3.9"
services:
  mysql:
    image: mysql:8.0
    container_name: demo-mysql
    environment:
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: demo
    volumes:
      - dbdata:/var/lib/mysql
    ports:
      - "3306:3306"

  app:
    image: alpine:3.19
    container_name: demo-app
    command: ["sh", "-c", "while true; do date >> /app/logs/app.log; sleep 2; done"]
    volumes:
      - ./app/config/app.conf:/app/config/app.conf:ro
      - ./logs:/app/logs
    depends_on:
      - mysql

volumes:
  dbdata:

启动与验证:

# 启动
docker compose up -d

# 查看命名卷
docker volume ls

# 查看命名卷详情(挂载位置)
docker volume inspect dbdata

# 宿主机查看绑定挂载产生的日志
tail -f ./logs/app.log

# 进入容器验证文件存在
docker exec -it demo-app sh -c "ls -l /app/config && tail -n 3 /app/logs/app.log"

命令说明:
- docker volume ls:列出命名卷,确认数据是否持久化。
- docker volume inspect dbdata:查看卷的实际挂载路径与驱动信息。
- docker exec -it:进入容器验证挂载结果。

预期效果:
- MySQL 数据存放在命名卷 dbdata 中,容器删除后数据仍在。
- ./logs 目录持续写入日志,宿主机可直接访问。
- app.conf 以只读方式挂载,避免容器内误改。

2. 绑定挂载与命名卷的选择策略#

  • 数据库/中间件数据:优先命名卷(统一管理、迁移方便)。
  • 配置文件:绑定挂载(方便快速修改与版本控制)。
  • 日志:绑定挂载或专用日志卷,便于采集系统接入。
  • 临时数据:匿名卷或 tmpfs(不需要持久化)。

tmpfs 示例(适合临时缓存):

services:
  cache:
    image: redis:7
    tmpfs:
      - /data

3. 常见排错与修复#

1)容器启动失败:路径不存在或权限不足
排查:

docker compose ps
docker compose logs app
ls -ld ./logs ./app/config

修复:

# 确保目录存在且可写
mkdir -p ./logs
chmod 755 ./logs

2)绑定挂载文件变成目录
原因:宿主机文件不存在,Docker 自动创建目录。
修复:

# 先创建文件再启动
echo "env=prod" > ./app/config/app.conf
docker compose up -d --force-recreate

3)命名卷误删导致数据丢失
避免:

# 删除容器时不要使用 -v
docker compose down   # 不带 -v

备份示例(命名卷导出):

# 将命名卷数据打包到宿主机当前目录
docker run --rm -v dbdata:/data -v "$PWD":/backup alpine \
  tar czf /backup/dbdata.tar.gz -C /data .

4. 练习#

1)将上例中的 MySQL 改为绑定挂载存储到 ./mysql-data,并验证数据重启后仍在。
2)给 app 增加一个只读配置目录挂载,验证容器内无法写入。
3)模拟权限问题:将 ./logs 改为只读权限,观察容器日志报错并修复。

5. 快速参考命令清单#

# 查看卷列表
docker volume ls

# 查看卷详情
docker volume inspect dbdata

# 删除孤儿卷
docker volume prune

# 查看容器挂载
docker inspect demo-mysql | grep -A5 Mounts

通过以上示例可形成可落地的持久化策略:数据库使用命名卷集中管理,配置与日志使用绑定挂载便于运维与排错,临时数据则使用 tmpfs 提升性能并降低持久化成本。