找回密码
 立即注册

QQ登录

只需一步,快速开始

peUfSYR.png
查看: 48|回复: 0

Day 17 Compose 数据卷与持久化

[复制链接]

876

主题

13

回帖

2808

积分

管理员

积分
2808
发表于 2026-3-29 16:17:12 | 显示全部楼层 |阅读模式
Day 17 Compose 数据卷与持久化
Docker 30天实战系列 · Day 17

你有没有经历过这种心碎时刻:辛辛苦苦往 MySQL 里灌了几万条数据,结果一个
  1. docker-compose down
复制代码
之后,数据全没了?那种感觉,就像你花了一下午整理好的房间,被熊孩子五分钟给拆了。

容器天生就是"用完即弃"的,这是它的优点,也是它让人抓狂的地方。今天我们就来解决这个问题——让数据在容器的生死轮回中"永生"。

本文你将学到

  • Named Volumes 和 Bind Mounts 的区别与使用场景
  • 如何在 Compose 中配置数据卷实现持久化
  • MySQL 容器数据持久化的完整方案
  • 数据卷的备份与恢复实操
  • 生产环境数据卷的最佳实践

    | 阅读时间 | 实操时间 | 难度等级 |
    |---------|---------|---------|
    | 10 分钟 | 30 分钟 | 中级 |

    ━━━━━━━━━━━━━━━━━━━━

    一、容器数据的"前世今生"

    先搞清楚一个基本事实:容器的文件系统是临时的。每次容器重建,里面的数据就会从头开始。这就好比你住酒店——退房之后,房间会被打扫得干干净净,你放在床头柜上的东西不会给你留着。

    那问题来了,数据库、上传的文件、配置信息这些东西怎么办?总不能每次重启都从零开始吧。

    Docker 给了我们两种"保险箱":
    1. ┌──────────────────────────────────────────────────┐
    2. │                   宿主机文件系统                    │
    3. │                                                    │
    4. │   ┌─────────────┐        ┌─────────────────┐      │
    5. │   │ Named Volume │        │   Bind Mount    │      │
    6. │   │             │        │                 │      │
    7. │   │ Docker 管理  │        │  你指定目录      │      │
    8. │   │ /var/lib/    │        │  /home/data/    │      │
    9. │   │  docker/     │        │  /app/config/   │      │
    10. │   │  volumes/    │        │                 │      │
    11. │   └──────┬──────┘        └────────┬────────┘      │
    12. │          │                        │                │
    13. │          ▼                        ▼                │
    14. │   ┌─────────────────────────────────────────┐     │
    15. │   │            容器内部文件系统               │     │
    16. │   │                                         │     │
    17. │   │   /var/lib/mysql    /app/config          │     │
    18. │   └─────────────────────────────────────────┘     │
    19. └──────────────────────────────────────────────────┘
    复制代码

    二、Named Volumes vs Bind Mounts

    这两兄弟经常被搞混,我们用一个生活类比来理解。

    Named Volumes(命名卷) 就像银行的保险柜。你把东西交给银行保管,银行给你一个编号,你不需要知道具体放在哪个抽屉里。Docker 帮你管理存储位置、权限、生命周期,你只管用就行。

    Bind Mounts(绑定挂载) 就像你在家里买了个保险箱,放在你指定的位置。你清楚地知道东西在哪,可以直接打开看,但维护工作得你自己来。

    | 对比维度 | Named Volumes | Bind Mounts |
    |---------|--------------|-------------|
    | 存储位置 | Docker 管理(/var/lib/docker/volumes/) | 你指定的宿主机路径 |
    | 可移植性 | 高,跨机器方便迁移 | 低,依赖宿主机目录结构 |
    | 性能 | Linux 原生性能 | 取决于文件系统 |
    | 权限管理 | Docker 自动处理 | 需要手动管理 |
    | 适用场景 | 数据库、应用数据 | 开发时代码热更新、配置文件 |
    | Compose 语法 |
    1. volume_name:/container/path
    复制代码
    |
    1. ./host/path:/container/path
    复制代码
    |

    选择原则:生产环境优先用 Named Volumes,开发环境按需用 Bind Mounts。

    三、Compose 中配置数据卷

    3.1 Named Volumes 基础用法
    1. # docker-compose.yml
    2. services:
    3.   db:
    4.     image: mysql:8.0
    5.     environment:
    6.       MYSQL_ROOT_PASSWORD: my-secret-pw
    7.       MYSQL_DATABASE: myapp
    8.     volumes:
    9.       - mysql_data:/var/lib/mysql
    10.     ports:
    11.       - "3306:3306"
    12. volumes:
    13.   mysql_data:
    复制代码

    注意最后那个顶层的
    1. volumes:
    复制代码
    声明,这是在告诉 Docker:"帮我创建一个叫
    1. mysql_data
    复制代码
    的命名卷。" 如果你忘了写这个声明,Docker Compose 会报错。

    3.2 Bind Mounts 用法
    1. services:
    2.   web:
    3.     image: nginx:alpine
    4.     volumes:
    5.       - ./nginx.conf:/etc/nginx/nginx.conf:ro
    6.       - ./html:/usr/share/nginx/html
    复制代码

    这里
    1. ./nginx.conf
    复制代码
    是宿主机上的相对路径,
    1. :ro
    复制代码
    表示只读挂载——容器只能读这个文件,不能改。

    3.3 混合使用

    实际项目中,两种方式经常一起用:
    1. services:
    2.   app:
    3.     image: node:20-alpine
    4.     volumes:
    5.       - ./src:/app/src          # Bind Mount: 开发时代码热更新
    6.       - node_modules:/app/node_modules  # Named Volume: 依赖包持久化
    7.       - app_logs:/app/logs      # Named Volume: 日志持久化
    8. volumes:
    9.   node_modules:
    10.   app_logs:
    复制代码

    这个组合很经典:源码用 Bind Mount 方便开发调试,而
    1. node_modules
    复制代码
    用 Named Volume 避免宿主机和容器的依赖冲突。

    四、MySQL 数据持久化实操

    说了这么多理论,我们来动手。这是大家最常遇到的场景——MySQL 数据持久化。

    4.1 创建项目
    1. mkdir -p ~/docker-mysql-demo && cd ~/docker-mysql-demo
    复制代码

    创建
    1. docker-compose.yml
    复制代码

    1. services:
    2.   mysql:
    3.     image: mysql:8.0
    4.     container_name: demo-mysql
    5.     environment:
    6.       MYSQL_ROOT_PASSWORD: rootpass123
    7.       MYSQL_DATABASE: demo_db
    8.       MYSQL_USER: demo_user
    9.       MYSQL_PASSWORD: demopass123
    10.     volumes:
    11.       - mysql_data:/var/lib/mysql
    12.       - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    13.     ports:
    14.       - "3307:3306"
    15.     restart: unless-stopped
    16. volumes:
    17.   mysql_data:
    18.     name: demo-mysql-data
    复制代码

    创建初始化脚本
    1. init.sql
    复制代码

    1. USE demo_db;
    2. CREATE TABLE IF NOT EXISTS users (
    3.     id INT AUTO_INCREMENT PRIMARY KEY,
    4.     name VARCHAR(100) NOT NULL,
    5.     email VARCHAR(100) NOT NULL,
    6.     created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    7. );
    8. INSERT INTO users (name, email) VALUES
    9. ('张三', 'zhangsan@example.com'),
    10. ('李四', 'lisi@example.com'),
    11. ('王五', 'wangwu@example.com');
    复制代码

    4.2 启动并验证
    1. # 启动服务
    2. docker compose up -d
    3. # 预期输出:
    4. # [+] Running 2/2
    5. #  ✔ Volume "demo-mysql-data"  Created
    6. #  ✔ Container demo-mysql      Started
    复制代码

    等待 MySQL 完全启动(大约 10-15 秒),然后验证数据:
    1. # 连接数据库查询数据
    2. docker exec demo-mysql mysql -udemo_user -pdemopass123 demo_db \
    3.   -e "SELECT * FROM users;"
    4. # 预期输出:
    5. # +----+--------+----------------------+---------------------+
    6. # | id | name   | email                | created_at          |
    7. # +----+--------+----------------------+---------------------+
    8. # |  1 | 张三   | zhangsan@example.com | 2026-03-06 10:00:00 |
    9. # |  2 | 李四   | lisi@example.com     | 2026-03-06 10:00:00 |
    10. # |  3 | 王五   | wangwu@example.com   | 2026-03-06 10:00:00 |
    11. # +----+--------+----------------------+---------------------+
    复制代码

    4.3 验证持久化

    关键时刻到了——删掉容器,数据还在不在?
    1. # 插入一条新数据
    2. docker exec demo-mysql mysql -udemo_user -pdemopass123 demo_db \
    3.   -e "INSERT INTO users (name, email) VALUES ('赵六', 'zhaoliu@example.com');"
    4. # 停止并删除容器(注意:不要加 -v 参数!)
    5. docker compose down
    6. # 重新启动
    7. docker compose up -d
    8. # 等待 MySQL 启动后查询
    9. docker exec demo-mysql mysql -udemo_user -pdemopass123 demo_db \
    10.   -e "SELECT * FROM users;"
    11. # 预期输出: 4条数据都在,包括后来插入的"赵六"
    复制代码

    数据完好无损。这就是 Named Volume 的威力。

    注意一个大坑:
    1. docker compose down -v
    复制代码
    会把 volumes 一起删掉。生产环境千万别手滑加
    1. -v
    复制代码
    ,这比
    1. rm -rf /
    复制代码
    还让人心痛。

    五、数据卷的备份与恢复

    数据持久化只是第一步。如果磁盘坏了呢?如果要迁移到另一台服务器呢?备份才是真正的保险。

    5.1 备份
    1. # 创建备份目录
    2. mkdir -p ~/backups
    3. # 方法一:使用 mysqldump(推荐,跨版本兼容性好)
    4. docker exec demo-mysql mysqldump -udemo_user -pdemopass123 demo_db \
    5.   > ~/backups/demo_db_backup.sql
    6. # 方法二:直接备份数据卷(适合整体迁移)
    7. docker run --rm \
    8.   -v demo-mysql-data:/source:ro \
    9.   -v ~/backups:/backup \
    10.   alpine tar czf /backup/mysql_volume_backup.tar.gz -C /source .
    11. # 预期输出(方法二):
    12. # (无输出表示成功,可以 ls 验证)
    13. ls -lh ~/backups/
    14. # -rw-r--r-- 1 root root  1.2K ... demo_db_backup.sql
    15. # -rw-r--r-- 1 root root  5.8M ... mysql_volume_backup.tar.gz
    复制代码

    方法二的原理:启动一个临时的 Alpine 容器,把数据卷挂载为只读,然后打包压缩到备份目录。用完容器自动销毁(
    1. --rm
    复制代码
    ),干净利落。

    5.2 恢复
    1. # 方法一:从 SQL 文件恢复
    2. docker exec -i demo-mysql mysql -udemo_user -pdemopass123 demo_db \
    3.   < ~/backups/demo_db_backup.sql
    4. # 方法二:从卷备份恢复
    5. docker compose down
    6. docker volume rm demo-mysql-data
    7. docker volume create demo-mysql-data
    8. docker run --rm \
    9.   -v demo-mysql-data:/target \
    10.   -v ~/backups:/backup:ro \
    11.   alpine tar xzf /backup/mysql_volume_backup.tar.gz -C /target
    12. docker compose up -d
    复制代码

    5.3 定时备份脚本

    生产环境建议用 crontab 定时备份:
    1. #!/bin/bash
    2. # backup-mysql.sh
    3. BACKUP_DIR=~/backups/mysql
    4. DATE=$(date +%Y%m%d_%H%M%S)
    5. KEEP_DAYS=7
    6. mkdir -p "$BACKUP_DIR"
    7. docker exec demo-mysql mysqldump -udemo_user -pdemopass123 \
    8.   --all-databases --single-transaction \
    9.   > "$BACKUP_DIR/all_db_$DATE.sql"
    10. # 清理过期备份
    11. find "$BACKUP_DIR" -name "*.sql" -mtime +$KEEP_DAYS -delete
    12. echo "Backup completed: all_db_$DATE.sql"
    复制代码
    1. # 添加定时任务:每天凌晨 3 点执行
    2. crontab -e
    3. # 添加这一行:
    4. # 0 3 * * * /path/to/backup-mysql.sh >> /var/log/mysql-backup.log 2>&1
    复制代码

    六、数据卷管理常用命令

    日常运维中,这几个命令用得最多:
    1. # 查看所有数据卷
    2. docker volume ls
    3. # 查看数据卷详情(存储路径、创建时间等)
    4. docker volume inspect demo-mysql-data
    5. # 删除未使用的数据卷(危险操作,先确认!)
    6. docker volume prune
    7. # 删除指定数据卷
    8. docker volume rm demo-mysql-data
    复制代码
    1. docker volume prune
    复制代码
    会删掉所有没被容器引用的卷。听起来很智能,但如果你刚好
    1. docker compose down
    复制代码
    了一个项目,它的卷就变成"未使用"了。所以执行前一定要三思。

    七、常见问题 Q&A

    Q1: Named Volume 的数据到底存在宿主机哪里?

    Linux 上默认在
    1. /var/lib/docker/volumes/<volume_name>/_data/
    复制代码
    。你可以用
    1. docker volume inspect <name>
    复制代码
    查看
    1. Mountpoint
    复制代码
    字段。但不建议直接操作这个目录,通过 Docker 命令管理更安全。

    Q2: docker compose down 和 docker compose down -v 有什么区别?
    1. down
    复制代码
    只删除容器和网络,数据卷会保留。
    1. down -v
    复制代码
    会把声明的 Named Volumes 也一起删掉。生产环境请务必确认你的肌肉记忆里没有
    1. -v
    复制代码
    。我见过不止一个团队因为这个参数翻车。

    Q3: Bind Mount 的权限问题怎么解决?

    这是 Linux 上最常见的坑。容器内进程的 UID/GID 和宿主机不一致,会导致"Permission denied"。解决方案有三个:
  • 在 Dockerfile 中用
    1. RUN chown
    复制代码
    设置正确权限
  • 在 Compose 中用
    1. user: "1000:1000"
    复制代码
    指定运行用户
  • 对于 MySQL 等官方镜像,它们通常会自动处理权限,不需要额外配置

    八、小结

    今天我们学了 Docker 数据持久化的核心知识:

  • Named Volumes 适合生产环境,Docker 帮你管理,省心省力
  • Bind Mounts 适合开发环境,代码热更新、配置文件挂载很方便
  • MySQL 数据持久化的关键是把
    1. /var/lib/mysql
    复制代码
    挂载到 Named Volume
  • 持久化不等于安全,定期备份才是数据的最后防线
    1. docker compose down -v
    复制代码
    是个危险命令,生产环境慎用

    数据卷解决了"数据往哪放"的问题。但真实项目不只有数据库,还有前端、后端、缓存、消息队列……它们之间怎么协作?

    明天 Day 18 多容器全栈应用实战,我们会用 Compose 编排一个完整的前后端 + 数据库 + Redis 的全栈应用,把前几天学的网络、数据卷、环境变量全部串起来。这才是 Compose 真正发力的地方,敬请期待。
  • 您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    扫码关注微信公众号

    Archiver|手机版|小黑屋|风叶林

    GMT+8, 2026-5-8 23:00 , Processed in 0.270259 second(s), 19 queries .

    Powered by 风叶林

    © 2001-2026 Discuz! Team.

    快速回复 返回顶部 返回列表