必须挂载/var/lib/mysql,否则容器删除或重启后数据必然丢失;推荐使用docker volume(如mysql-data卷),开发可用bind mount但需chown -r 999:999并加:z标签(selinux启用时)。

挂载 /var/lib/mysql 是硬性要求,不挂就丢数据
MySQL 容器一旦停止并删除,默认写入 /var/lib/mysql 的数据会随容器层一起销毁。这不是“可能丢失”,而是必然丢失——因为该路径在镜像层是只读的,运行时写入全在可写容器层。所以只要没挂载,哪怕只重启一次,初始化后的数据库、用户、表结构全都没了。
必须显式挂载这个路径,且目标必须是空目录或 Docker 管理的卷。常见错误包括:
- 漏写
-v或volumes:配置,只写了端口和密码 - 挂载了
/etc/mysql/conf.d却忘了挂/var/lib/mysql - 用
docker commit保存容器状态来“持久化”——这不可靠,且无法增量备份
docker run -v 绑定宿主机目录:权限和 SELinux 是两大拦路虎
直接挂载宿主机路径(比如 /my/mysql/data)最直观,但 Linux 下极易失败。根本原因不是 Docker 不支持,而是 MySQL 进程(UID 999)需要对该目录有完整读写权限,且 SELinux 策略默认禁止容器访问任意宿主机路径。
实操要点:
- 创建目录后立即执行:
chown -R 999:999 /my/mysql/data(MySQL 8.0 默认以 UID 999 运行) - 若启用了 SELinux(
getenforce返回Enforcing),必须加:z标签:-v /my/mysql/data:/var/lib/mysql:z - 不要用 root 用户创建目录再 chown —— 某些发行版(如 CentOS Stream)会保留 root 的 capabilites,导致 MySQL 启动报错
mysqld: Can't create/write to file - 首次启动前确保目录为空;若已有旧数据,需确认文件属主已是 999
docker volume 命名卷:适合大多数场景,但调试不便
命名卷由 Docker 自动管理位置和权限,规避了绑定挂载的权限问题,也更符合容器设计哲学。它适合开发、CI/CD 和多数生产环境,但不适合需要直接编辑或备份数据文件的场景。
关键行为:
- 运行
docker run -v mysql-data:/var/lib/mysql时,Docker 自动创建卷mysql-data(如果不存在) - 卷物理位置在
/var/lib/docker/volumes/mysql-data/_data,普通用户不可见也不应直接操作 - 删除容器不影响卷;必须显式执行
docker volume rm mysql-data才会清空数据 - 不能用
ls或cp直接查看内容——得先docker run --rm -v mysql-data:/target alpine ls /target
配置文件映射要避开 my.cnf 覆盖陷阱
MySQL 官方镜像启动时会按顺序加载配置:内置 /etc/mysql/my.cnf → /etc/mysql/conf.d/*.cnf → /etc/mysql/mysql.conf.d/*.cnf。如果你把自定义 my.cnf 挂到 /etc/mysql/my.cnf,会完全覆盖默认配置,极可能导致启动失败(例如跳过 socket 文件、禁用 bind-address)。
安全做法是只挂载片段:
- 新建
/my/mysql/conf.d/custom.cnf,只写你需要改的项,如:[mysqld] default_authentication_plugin=mysql_native_password character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci
- 挂载命令中指定:
-v /my/mysql/conf.d:/etc/mysql/conf.d:ro - 避免挂载整个
/etc/mysql目录——镜像后续更新可能新增必要配置文件
配置生效后,进容器执行 mysql --help | grep "Default options" 可确认加载顺序,mysql -e "SHOW VARIABLES LIKE 'character_set%';" 验证是否生效。
真正容易被忽略的是:配置文件挂载成功 ≠ MySQL 会重读它。修改后必须重启容器,且每次重启都会重新初始化数据目录(如果它是空的)。所以务必先确保 /var/lib/mysql 已挂载且非空,再调配置。











