共计 4277 个字符,预计需要花费 11 分钟才能阅读完成。
一、先看配置中的两种挂载方式
# /web/docker-compose.yml
mysql:
volumes:
# 数据目录 → 命名卷(Named Volume)
- db_data:/var/lib/mysql
# 配置文件 → 绑定挂载(只读)
- /web/mysql/conf.d:/etc/mysql/conf.d:ro
# 日志目录 → 绑定挂载(Bind Mount)
- /web/mysql/log:/var/log/mysql
......
volumes:
db_data: # ← Docker 管理的命名卷
同样是”把容器内目录持久化到宿主机”,为什么数据用命名卷、日志用绑定挂载?这不是随意选择,而是基于数据特性、访问模式、权限管理、运维需求的综合判断。
二、命名卷的实际物理路径
查看命名卷的详细信息
docker volume inspect wpsite_db_data
输出
[
{
"CreatedAt": "2026-08-27T10:30:00+08:00",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "wpsite",
"com.docker.compose.version": "5.4.0",
"com.docker.compose.volume": "db_data"
},
"Mountpoint": "/var/lib/docker/volumes/wpsite_db_data/_data",
"Name": "wpsite_db_data",
"Options": {},
"Scope": "local"
}
]
物理路径:
/var/lib/docker/volumes/wpsite_db_data/_data/
命名规则:<项目名>_<卷名> → wpsite_db_data
# 查看数据文件
sudo ls -la /var/lib/docker/volumes/wpsite_db_data/_data/
# 输出(MariaDB 数据文件):
# drwx------ 999 999 aria_log.00000001
# drwx------ 999 999 ib_buffer_pool
# drwx------ 999 999 ibdata1
# drwx------ 999 999 ib_logfile0
# drwx------ 999 999 ibtmp1
# drwx------ 999 999 mysql/
# drwx------ 999 999 performance_schema/
# drwx------ 999 999 wordpress/ ← 你的博客数据
# ...
注意文件所有者是
999:999(容器内 mysql 用户),宿主机上直接用普通用户无法读取。
三、为什么数据目录用命名卷?
3.1 权限自动管理(最核心的原因)
MariaDB 容器内以 mysql 用户(UID 999)运行,数据文件必须由该用户读写。
命名卷:
Docker 首次创建卷时,自动将容器内 /var/lib/mysql 的权限
复制到卷中。容器以 UID 999 启动 → 数据文件自动归 999 所有。
无需任何手动操作。✅
绑定挂载:
宿主机 /web/mysql/ 目录默认归 root 所有。
MariaDB 容器以 UID 999 运行 → 无法写入 → 启动失败 ❌
必须手动:
chown -R 999:999 /web/mysql
chmod 750 /web/mysql
对于数据库数据文件,权限错误意味着数据损坏或容器无法启动。命名卷消除了这个风险。
3.2 数据安全性
| 维度 | 命名卷 | 绑定挂载 |
|---|---|---|
| 宿主机用户能否直接修改数据文件 | ❌ 需要 root + 知道路径 | ✅ 任何有权限的用户 |
| 误删风险 | 低(路径深,不直观) | 高(rm -rf /opt/mysql/* 就完了) |
| 备份工具误扫 | 不会(Docker 内部管理) | 可能(备份脚本扫描 /opt) |
docker compose down |
数据保留 ✅ | 数据保留 ✅ |
docker compose down -v |
数据删除 ⚠️ | 数据保留 ✅ |
命名卷将数据”藏”在 Docker 管理的深层路径中,降低了被宿主机操作误伤的概率。
3.3 I/O 性能
数据库是大量小文件 + 高频随机读写的典型场景:
| 指标 | 命名卷 | 绑定挂载 |
|---|---|---|
| Linux 上性能差异 | 几乎相同 | 几乎相同 |
| macOS/Windows(Docker Desktop) | 显著更优 | 较慢(经过虚拟化层) |
| 文件元数据操作 | Docker 有优化 | 直接透传 |
| 大量小文件创建 | 略优 | 略差 |
在 Linux 生产服务器上差异很小,但在开发环境(macOS/Windows)中差异显著。命名卷是 Docker 官方推荐的数据库存储方式。
3.4 生命周期管理
# 命名卷的生命周期独立于容器
docker compose down # 容器删除,卷保留 ✅
docker compose up -d # 新容器自动挂载旧卷,数据恢复
# 只有显式删除卷才会丢失数据
docker compose down -v # ⚠️ 删除卷,数据丢失
docker volume rm wpsite_db_data # ⚠️ 手动删除
这种”默认保留、显式删除”的行为,对数据库数据是最安全的。
四、为什么日志用绑定挂载?
4.1 运维可访问性(最核心的原因)
日志的核心需求是人能方便地看。
绑定挂载:
# 直接在宿主机上查看慢查询日志
tail -f /web/mysql/log/slow.log
# 用 grep 搜索
grep "SELECT" /web/mysql/log/slow.log | tail -20
# 用外部工具分析
mysqldumpslow /web/mysql/log/slow.log
# 用日志采集工具(如 Filebeat、Promtail)直接采集
# 只需配置路径 /web/mysql/log/*.log
如果用命名卷:
# 必须进容器才能看
docker exec wpdb tail -f /var/log/mysql/slow.log
# 或者用 docker cp 复制出来
docker cp wpdb:/var/log/mysql/slow.log ./slow.log
# 外部日志采集工具无法直接访问
# 需要额外的 sidecar 容器或 volume 挂载
4.2 日志不是关键数据
| 特性 | 数据库数据 | 日志 |
|---|---|---|
| 丢失后果 | 灾难性(博客没了) | 可接受(重新生成) |
| 写入模式 | 随机读写、事务性 | 顺序追加 |
| 文件数量 | 大量小文件 | 少量大文件 |
| 权限敏感度 | 极高(不能被人篡改) | 低(只读查看即可) |
| 外部工具需求 | 无(通过 SQL 访问) | 高(tail、grep、采集器) |
日志不需要命名卷提供的权限保护和性能优化,但非常需要绑定挂载提供的直接可访问性。
4.3 日志轮转与清理
绑定挂载下,可以直接在宿主机上配置日志轮转:
# 宿主机用 logrotate
# nano /etc/logrotate.d/mysql-docker
/web/mysql/log/*.log {
weekly
rotate 7
missingok
notifempty
compress
delaycompress
dateext
copytruncate
}
命名卷下,日志轮转只能在容器内部处理,灵活性降低。
4.4 权限可控
日志目录的权限需求与数据目录不同:
# 日志目录:允许运维用户读取
chown -R 999:999 /web/mysql/log # mysql 用户写入
chmod 750 /web/mysql/log # 同组可读
# 可以额外授权给运维用户
usermod -aG 999 ops_user
# 或
setfacl -m u:ops_user:r /web/mysql/log/slow.log
命名卷的权限完全由容器内进程决定,宿主机上无法灵活调整。
五、对比总结
| 考量维度 | 数据目录(命名卷) | 日志目录(绑定挂载) |
|---|---|---|
| 权限管理 | 自动 ✅ | 需手动,但灵活 |
| 误操作防护 | 高(路径深) ✅ | 低(路径直观) |
| 宿主机直接查看 | 不便 | 方便 ✅ |
| 外部工具集成 | 不便 | 方便 ✅ |
| I/O 性能 | 略优 ✅ | 标准 |
| 数据重要性 | 极高 | 低 |
| 备份方式 | mariadb-dump(逻辑备份) |
直接复制文件 |
| 日志轮转 | 容器内处理 | 宿主机 logrotate ✅ |
| Docker 官方推荐 | ✅ 数据库数据用命名卷 | — |
六、备份策略的差异
数据备份(命名卷)
命名卷不适合直接复制文件做备份(InnoDB 有热数据、WAL 等一致性问题),应使用逻辑备份:
# ✅ 正确:逻辑备份
cd /web && set -a && source .env && set +a && \
docker exec wpdb mariadb-dump -u root -p"$DB_ROOT_PASSWORD" \
--single-transaction --quick --lock-tables=false \
--routines --triggers --events wordpress \
| gzip > /web/backup/wp-db-backup-$(date +%Y%m%d-%H%M%S).sql.gz
# ❌ 错误:直接复制卷目录(可能不一致)
cp -r /var/lib/docker/volumes/wpsite_db_data/_data/ /web/backup/
如果一定要物理备份命名卷:
# 停止数据库 → 复制卷 → 重启
docker compose stop mysql
docker run --rm -v wpsite_db_data:/data -v $(pwd):/backup \
alpine tar czf /backup/db_data.tar.gz -C /data .
docker compose start mysql
日志备份(绑定挂载)
# 直接复制即可
cp /web/mysql/log/slow.log /web/backup/slow_$(date +%F).log
# 或者压缩归档
tar czf /web/backup/mysql_logs_$(date +%F).tar.gz /web/mysql/log/
七、命名卷的管理命令
# 列出所有卷
docker volume ls
# 查看卷详情(含物理路径)
docker volume inspect wpsite_db_data
# 查看卷占用空间
du -sh /var/lib/docker/volumes/wpsite_db_data/_data/
# 删除卷(⚠️ 数据丢失)
docker volume rm wpsite_db_data
# 清理所有未使用的卷(⚠️ 危险操作)
docker volume prune
八、总结
数据是”命”,日志是”日记”。
命要锁在保险柜里(命名卷:权限自动、路径深、防误操作); 日记要放在书桌上随时翻(绑定挂载:直接可见、可 grep、可轮转)。
两者不是”哪个更好”的问题,而是不同数据特性对应不同存储策略的问题。Docker 官方文档中也明确建议:数据库数据使用命名卷,需要宿主机访问的文件使用绑定挂载。
本文内容在完整版教程 Docker WordPress 建站指南:从零到生产级部署 基础上进行讨论