📘 Docker 数据目录与权限问题复盘(最终版)
日期:2026-01-09
作者:我
主题:Docker + Linux 文件系统 + MySQL / Redis / Elasticsearch
一、问题背景
今天在为项目部署 MySQL、Redis、Elasticsearch 时,我遇到了大量容器启动失败的问题,典型错误集中在:
Permission denied (errno 13)- MySQL 初始化失败
- Elasticsearch 拒绝写入数据目录
一开始我尝试从权限、镜像、配置层面排查,但最终发现:
这些问题本质上都不是服务本身的问题,而是数据目录使用方式不正确。
二、踩坑复盘(问题是怎么来的)
1. 在不合适的文件系统上存放 Docker 数据
我曾把 Docker 数据目录放在 NTFS 等非 Linux 原生文件系统上,结果导致:
- 权限看起来正确,但实际不可用
- MySQL 因大小写问题被迫降级
- Elasticsearch / Redis 随机写失败
这一步直接埋下了后续所有问题的根源。
2. 误以为 Docker Volume 会处理权限
我使用了 Docker volume + bind 的方式,但一度误以为 Docker 会自动处理目录权限。
事实是:
Docker 只负责挂载路径,不负责修复权限或文件系统问题。
3. 迷信 chmod 777
我多次尝试通过 chmod 777 解决问题,但事实证明:
- 权限位不是关键
- 目录的真实拥有者(UID / GID)才是关键
- 文件系统能力决定了一切
三、最终结论(只保留一个)
**唯一长期可行、可维护、可复用的方案:
在宿主机提前创建好目录 → 使用 Linux 原生文件系统 → 通过 bind mount 挂载到容器。**
其他尝试全部属于踩坑,不再作为方案考虑。
四、最终有效目录结构(宿主机)
/media/local/data/gamebox/
├── mysql
├── redis
├── elasticsearch
└── elasticsearch-plugins以上目录均由我 手动提前创建,不交给 Docker 自动生成。
五、最终验证可用的 Docker Compose 配置(已脱敏)
services:
mysql:
image: mysql:8.0
container_name: gamebox-mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_PASSWORD:-******}
MYSQL_DATABASE: gamebox
TZ: Asia/Shanghai
volumes:
- mysql_data:/var/lib/mysql
- ../sql:/docker-entrypoint-initdb.d:ro
ports:
- "23306:3306"
command:
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
--default-authentication-plugin=mysql_native_password
--skip-name-resolve
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: gamebox-redis
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD:-******}
volumes:
- redis_data:/data
ports:
- "26379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
elasticsearch:
image: elasticsearch:7.17.25
container_name: gamebox-es
restart: unless-stopped
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- ES_JAVA_OPTS=-Xms512m -Xmx512m
- TZ=Asia/Shanghai
- cluster.routing.allocation.disk.watermark.low=95%
- cluster.routing.allocation.disk.watermark.high=97%
- cluster.routing.allocation.disk.watermark.flood_stage=99%
- http_proxy=http://***:***@proxy.example.com:PORT
- https_proxy=http://***:***@proxy.example.com:PORT
- HTTP_PROXY=http://***:***@proxy.example.com:PORT
- HTTPS_PROXY=http://***:***@proxy.example.com:PORT
volumes:
- es_data:/usr/share/elasticsearch/data
- es_plugins:/usr/share/elasticsearch/plugins
ports:
- "29200:9200"
- "29300:9300"
entrypoint: /bin/bash
command: >
-c "
if [ ! -f /usr/share/elasticsearch/plugins/analysis-ik/plugin-descriptor.properties ]; then
elasticsearch-plugin install --batch https://get.infini.cloud/elasticsearch/analysis-ik/7.17.25;
fi;
/usr/local/bin/docker-entrypoint.sh eswrapper
"
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"]
interval: 30s
timeout: 10s
retries: 5
volumes:
mysql_data:
driver: local
driver_opts:
type: none
o: bind
device: /media/local/data/gamebox/mysql
redis_data:
driver: local
driver_opts:
type: none
o: bind
device: /media/local/data/gamebox/redis
es_data:
driver: local
driver_opts:
type: none
o: bind
device: /media/local/data/gamebox/elasticsearch
es_plugins:
driver: local
driver_opts:
type: none
o: bind
device: /media/local/data/gamebox/elasticsearch-plugins
networks:
default:
name: gamebox-network六、我真正学到的东西
- Docker 不会帮我兜底权限问题
- Volume 只是路径映射,不是权限隔离
- 数据库对文件系统极度敏感
chmod 777不是解决方案- 提前建目录 + bind mount 是唯一正解
七、最终一句话总结
**这次问题让我真正理解了:
Docker 并没有让系统更简单,它只是把底层问题暴露得更清楚。**尊重文件系统、尊重权限模型,容器才能稳定运行。