冷蟊初退 孤灯野澜 志起鸡鸣 墓不悲秋 技术交流 软件开发 商业合作 加Q:411239339

Day 17 Compose 数据卷与持久化

浏览:14次阅读
没有评论

共计 6302 个字符,预计需要花费 16 分钟才能阅读完成。

Day 17 Compose 数据卷与持久化

Docker 30 天实战系列 · Day 17

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

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

本文你将学到

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

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

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

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

Docker 给了我们两种 " 保险箱 ":

┌──────────────────────────────────────────────────┐
│                   宿主机文件系统                    │
│                                                    │
│   ┌─────────────┐        ┌─────────────────┐      │
│   │ Named Volume │        │   Bind Mount    │      │
│   │             │        │                 │      │
│   │ Docker 管理  │        │  你指定目录      │      │
│   │ /var/lib/    │        │  /home/data/    │      │
│   │  docker/     │        │  /app/config/   │      │
│   │  volumes/    │        │                 │      │
│   └──────┬──────┘        └────────┬────────┘      │
│          │                        │                │
│          ▼                        ▼                │
│   ┌─────────────────────────────────────────┐     │
│   │            容器内部文件系统               │     │
│   │                                         │     │
│   │   /var/lib/mysql    /app/config          │     │
│   └─────────────────────────────────────────┘     │
└──────────────────────────────────────────────────┘

二、Named Volumes vs Bind Mounts

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

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

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

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

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

三、Compose 中配置数据卷

3.1 Named Volumes 基础用法

# docker-compose.yml
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: my-secret-pw
      MYSQL_DATABASE: myapp
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"

volumes:
  mysql_data:

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

3.2 Bind Mounts 用法

services:
  web:
    image: nginx:alpine
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./html:/usr/share/nginx/html

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

3.3 混合使用

实际项目中,两种方式经常一起用:

services:
  app:
    image: node:20-alpine
    volumes:
      - ./src:/app/src          # Bind Mount: 开发时代码热更新
      - node_modules:/app/node_modules  # Named Volume: 依赖包持久化
      - app_logs:/app/logs      # Named Volume: 日志持久化

volumes:
  node_modules:
  app_logs:

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

四、MySQL 数据持久化实操

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

4.1 创建项目

mkdir -p ~/docker-mysql-demo && cd ~/docker-mysql-demo

创建 docker-compose.yml

services:
  mysql:
    image: mysql:8.0
    container_name: demo-mysql
    environment:
      MYSQL_ROOT_PASSWORD: rootpass123
      MYSQL_DATABASE: demo_db
      MYSQL_USER: demo_user
      MYSQL_PASSWORD: demopass123
    volumes:
      - mysql_data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    ports:
      - "3307:3306"
    restart: unless-stopped

volumes:
  mysql_data:
    name: demo-mysql-data

创建初始化脚本 init.sql

USE demo_db;

CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO users (name, email) VALUES
('张三', 'zhangsan@example.com'),
('李四', 'lisi@example.com'),
('王五', 'wangwu@example.com');

4.2 启动并验证

# 启动服务
docker compose up -d

# 预期输出:
# [+] Running 2/2
#  ✔ Volume "demo-mysql-data"  Created
#  ✔ Container demo-mysql      Started

等待 MySQL 完全启动(大约 10-15 秒),然后验证数据:

# 连接数据库查询数据
docker exec demo-mysql mysql -udemo_user -pdemopass123 demo_db \
  -e "SELECT * FROM users;"

# 预期输出:
# +----+--------+----------------------+---------------------+
# | id | name   | email                | created_at          |
# +----+--------+----------------------+---------------------+
# |  1 | 张三   | zhangsan@example.com | 2026-03-06 10:00:00 |
# |  2 | 李四   | lisi@example.com     | 2026-03-06 10:00:00 |
# |  3 | 王五   | wangwu@example.com   | 2026-03-06 10:00:00 |
# +----+--------+----------------------+---------------------+

4.3 验证持久化

关键时刻到了——删掉容器,数据还在不在?

# 插入一条新数据
docker exec demo-mysql mysql -udemo_user -pdemopass123 demo_db \
  -e "INSERT INTO users (name, email) VALUES (' 赵六 ','zhaoliu@example.com');"

# 停止并删除容器(注意:不要加 -v 参数!)docker compose down

# 重新启动
docker compose up -d

# 等待 MySQL 启动后查询
docker exec demo-mysql mysql -udemo_user -pdemopass123 demo_db \
  -e "SELECT * FROM users;"

# 预期输出: 4 条数据都在,包括后来插入的 "赵六"

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

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

五、数据卷的备份与恢复

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

5.1 备份

# 创建备份目录
mkdir -p ~/backups

# 方法一:使用 mysqldump(推荐,跨版本兼容性好)docker exec demo-mysql mysqldump -udemo_user -pdemopass123 demo_db \
  > ~/backups/demo_db_backup.sql

# 方法二:直接备份数据卷(适合整体迁移)docker run --rm \
  -v demo-mysql-data:/source:ro \
  -v ~/backups:/backup \
  alpine tar czf /backup/mysql_volume_backup.tar.gz -C /source .

# 预期输出(方法二):
#(无输出表示成功,可以 ls 验证)ls -lh ~/backups/
# -rw-r--r-- 1 root root  1.2K ... demo_db_backup.sql
# -rw-r--r-- 1 root root  5.8M ... mysql_volume_backup.tar.gz

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

5.2 恢复

# 方法一:从 SQL 文件恢复
docker exec -i demo-mysql mysql -udemo_user -pdemopass123 demo_db \
  < ~/backups/demo_db_backup.sql

# 方法二:从卷备份恢复
docker compose down
docker volume rm demo-mysql-data
docker volume create demo-mysql-data

docker run --rm \
  -v demo-mysql-data:/target \
  -v ~/backups:/backup:ro \
  alpine tar xzf /backup/mysql_volume_backup.tar.gz -C /target

docker compose up -d

5.3 定时备份脚本

生产环境建议用 crontab 定时备份:

#!/bin/bash
# backup-mysql.sh
BACKUP_DIR=~/backups/mysql
DATE=$(date +%Y%m%d_%H%M%S)
KEEP_DAYS=7

mkdir -p "$BACKUP_DIR"

docker exec demo-mysql mysqldump -udemo_user -pdemopass123 \
  --all-databases --single-transaction \
  > "$BACKUP_DIR/all_db_$DATE.sql"

# 清理过期备份
find "$BACKUP_DIR" -name "*.sql" -mtime +$KEEP_DAYS -delete

echo "Backup completed: all_db_$DATE.sql"
# 添加定时任务:每天凌晨 3 点执行
crontab -e
# 添加这一行:# 0 3 * * * /path/to/backup-mysql.sh >> /var/log/mysql-backup.log 2>&1

六、数据卷管理常用命令

日常运维中,这几个命令用得最多:

# 查看所有数据卷
docker volume ls

# 查看数据卷详情(存储路径、创建时间等)docker volume inspect demo-mysql-data

# 删除未使用的数据卷(危险操作,先确认!)docker volume prune

# 删除指定数据卷
docker volume rm demo-mysql-data

docker volume prune 会删掉所有没被容器引用的卷。听起来很智能,但如果你刚好 docker compose down 了一个项目,它的卷就变成 " 未使用 " 了。所以执行前一定要三思。

七、常见问题 Q&A

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

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

Q2: docker compose down 和 docker compose down -v 有什么区别?

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

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

这是 Linux 上最常见的坑。容器内进程的 UID/GID 和宿主机不一致,会导致 "Permission denied"。解决方案有三个:

  1. 在 Dockerfile 中用 RUN chown 设置正确权限
  2. 在 Compose 中用 user: "1000:1000" 指定运行用户
  3. 对于 MySQL 等官方镜像,它们通常会自动处理权限,不需要额外配置

八、小结

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

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

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

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

正文完
创作不易,扫码加点动力
post-qrcode
 0
果较瘦
版权声明:本站原创文章,由 果较瘦 于2026-03-29发表,共计6302字。
转载说明:除特殊说明外本站文章皆由果较瘦原创发布,转载请注明出处。