mysql容器必须挂载/var/lib/mysql到命名卷以持久化数据,root密码须通过mysql_root_password设置,配置应挂载到/etc/mysql/conf.d/,并显式声明端口与时区。

docker-compose.yml 里 mysql 服务必须显式挂载 /var/lib/mysql
不挂载会导致容器重启后数据全丢——因为 MySQL 默认把数据写在容器内文件系统,而 Docker 容器销毁即清空。哪怕只跑测试,也建议用命名卷,别依赖默认匿名卷。
实操建议:
- 用
docker volume create mysql-data提前建好卷,再在docker-compose.yml中通过volumes映射到/var/lib/mysql - 不要直接映射宿主机目录(比如
./mysql-data:/var/lib/mysql),容易因文件权限问题导致 MySQL 启动失败:容器内mysql用户(UID 999)可能没权限读写宿主机目录 - 如果非要用宿主机路径,得提前
chown -R 999:999 ./mysql-data,且该路径不能是 root 用户的家目录(某些 Linux 发行版会拒绝挂载)
root 密码必须通过 MYSQL_ROOT_PASSWORD 环境变量传入
MySQL 官方镜像完全忽略 my.cnf 里的 password 配置,也不接受 mysql_secure_installation 交互式设置。不设这个变量,容器会卡在初始化阶段并报错:ERROR 1045 (28000): Access denied for user 'root'@'localhost'。
常见错误现象:
- 容器反复重启,日志里出现
mysqld: Can't read dir of '/etc/mysql/conf.d/'—— 实际是初始化失败的副产物,根源仍是密码没配 - 用
MYSQL_PASSWORD或MYSQL_USER单独配置但漏了MYSQL_ROOT_PASSWORD,一样起不来 - 密码含特殊字符(如
@、/、:)时,必须用单引号包裹整个值,否则 YAML 解析失败
端口冲突和时区要手动指定 ports 与 environment
Docker 默认不暴露端口,不显式写 ports 就连不上;MySQL 容器默认用 UTC 时区,跟业务应用时区不一致会导致 NOW()、时间字段插入出错。
实操建议:
-
ports写成"3306:3306",别省略引号,避免 YAML 把数字当整型解析 - 加
environment块:TZ: "Asia/Shanghai"和MYSQL_TIME_ZONE: "+08:00",后者确保 MySQL 内部函数也对齐 - 如果宿主机 3306 已被占用,改左边端口(如
"3307:3306"),但应用连接字符串必须同步改成:3307,不然连的是宿主机原 MySQL
首次启动后别直接 exec 进去改配置文件
MySQL 官方镜像启动逻辑是:检测 /var/lib/mysql 是否为空 → 空则执行初始化(生成系统库、设置 root 密码)→ 启动 mysqld。一旦初始化完成,再手动改 /etc/mysql/my.cnf 并重启,大概率失败——因为配置加载顺序和权限校验会跳变。
正确做法:
- 需要自定义配置(如
max_connections、character_set_server=utf8mb4),应在docker-compose.yml中挂载一个my.cnf到/etc/mysql/conf.d/(注意不是覆盖主配置,而是追加) - 挂载路径必须是
/etc/mysql/conf.d/xxx.cnf,且文件权限为 644,否则 MySQL 启动时会静默忽略并报 warning - 改完配置必须删掉已有数据卷重建容器,或者用
docker-compose down -v清除卷,否则旧数据和新配置不兼容
复杂点在于:数据卷、配置挂载、环境变量三者耦合紧密,动其中任何一个,都得确认另外两个是否还匹配。尤其上线前,一定要用 docker-compose logs mysql 看初始化日志有没有 MySQL init process done. Ready for start up. 这句,没有就说明卡在某步了。











