必须挂载/var/lib/mysql,否则容器删除后数据全丢;推荐用命名卷docker volume create mysql-data并-v mysql-data:/var/lib/mysql挂载,避免匿名卷导致重启丢数据。

必须挂载 /var/lib/mysql,否则容器一删,数据全丢——这不是警告,是事实。
容器启动就退出?先查 docker logs 里的权限错误
常见现象:执行 docker run 后容器状态立刻变成 Exited (1),docker logs mysql-container 显示类似 mysqld: Can't read dir of '/etc/mysql/conf.d/' 或 Permission denied。
根本原因不是配置写错了,而是宿主机目录权限或属主不匹配 MySQL 容器内用户(UID/GID 999)。
- 不要直接复用已有目录(比如之前跑过其他服务的
/opt/mysql/data),先清空或新建干净路径:mkdir -p /opt/mysql/{data,conf} - 配置文件必须放在
/etc/mysql/conf.d/下,且命名以.cnf结尾;别挂到/etc/mysql/my.cnf,否则会跳过默认配置,导致bind-address缺失、外部连不上 - 加
--user 999:999参数可绕过多数权限问题,尤其在 CentOS/RHEL 等 SELinux 启用的系统上很关键
挂载命令里 -v 的顺序和路径不能错
一条能跑通的最小命令长这样(以 MySQL 8.0 为例):
docker run \ --name mysql-8 \ -e MYSQL_ROOT_PASSWORD=123456 \ -p 3306:3306 \ -v /opt/mysql/data:/var/lib/mysql \ -v /opt/mysql/conf:/etc/mysql/conf.d:ro \ --user 999:999 \ -d mysql:8.0
注意几个硬性约束:
-
/var/lib/mysql必须是第一个-v,且必须映射到宿主机一个**空目录**(首次初始化时若非空,MySQL 会拒绝启动) -
/etc/mysql/conf.d挂载要用:ro只读,防止容器内进程意外改写宿主机配置 - 宿主机配置目录(如
/opt/mysql/conf)下只放.cnf文件,别放空文件或隐藏文件,否则 MySQL 启动时加载失败 - 如果要用自定义
my.cnf,内容至少保留[mysqld]段,加上character-set-server=utf8mb4这类基础项,避免因语法错误整个配置加载中断
初始化 SQL 怎么自动执行?靠 /docker-entrypoint-initdb.d/
你不需要进容器手动 source 建库脚本。只要把 .sql 或 .sh 文件挂到这个路径,MySQL 首次初始化数据目录时就会自动执行。
- 挂载方式:
-v /path/to/init.sql:/docker-entrypoint-initdb.d/init.sql - 文件必须是 UTF-8 编码,BOM 头会导致执行失败
- 只在
/var/lib/mysql是空目录时触发;如果目录已有数据(比如你复用了旧卷),这个机制完全不生效 - SQL 文件里别写
CREATE DATABASE如果数据库名已存在,会报错中断后续语句;建议加CREATE DATABASE IF NOT EXISTS xxx;
命名卷 vs 宿主机目录:选哪个取决于你要不要直接碰文件
用 docker volume create mysql-data 再 -v mysql-data:/var/lib/mysql 是更“Docker 原生”的做法,适合生产环境。
但它有个实际限制:你没法直接编辑 my.cnf 或查看 mysql-data 卷里的二进制日志、慢查询日志——因为 Docker 把它藏在 /var/lib/docker/volumes/ 底下,路径不固定,且可能受 SELinux 或 rootless Docker 权限限制。
所以真实选择逻辑很简单:
- 开发/测试/单机部署 → 直接
-v /host/path:/var/lib/mysql,省事、可调试、路径透明 - CI/CD 流水线或需要跨主机一致行为 → 用命名卷,靠
docker volume inspect查Mountpoint,再配合 bind mount 单独挂配置 - 生产环境多容器协作 → 别单用
docker run,该上docker-compose.yml了,不然配置分散、不可追溯
最常被忽略的一点:挂载前没确认宿主机目录是否为空,或者误把非空目录当空目录用,结果 MySQL 初始化失败又看不出原因——这时候别猜,ls -la /opt/mysql/data 看一眼就知道。











