推荐使用mysql_root_password_file从文件读取密码,避免明文密码泄露;需将密码存于权限600的文件并挂载为绝对路径,配合docker-compose secrets或env_file安全注入,同时清理多余用户、禁用skip-grant-tables、限制bind-address与网络暴露。

用 MYSQL_ROOT_PASSWORD_FILE 替代明文环境变量
直接写 -e MYSQL_ROOT_PASSWORD=xxx 会让密码出现在 docker inspect、ps aux、Shell 历史和 CI/CD 日志里,生产环境审计过不了。MySQL 5.7.20+ 和 8.0+ 支持从文件读取密码,这才是合规做法。
操作要点:
- 把密码写进宿主机一个权限为
600的文件(如/run/secrets/mysql_root_pw),确保只有 root 可读 - 启动容器时用
-e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_pw - 配合
--secret(Docker Swarm)或--mount type=bind,src=...,dst=/run/secrets/mysql_root_pw,readonly挂载,避免挂载整个目录 - 该文件路径必须是容器内绝对路径,且不能是符号链接(MySQL 启动时会
stat()校验)
在 docker-compose.yml 中避免硬编码密码
MYSQL_ROOT_PASSWORD 出现在 docker-compose.yml 里等于把密钥提交进 Git——哪怕加了 .gitignore,也常被误提。正确方式是让 Compose 从外部注入。
推荐组合:
- 使用
env_file:在docker-compose.yml中写env_file: .env,而.env文件不纳入版本控制,并设chmod 600 .env - 更安全的替代:用
MYSQL_ROOT_PASSWORD_FILE+secrets块(仅限 Docker Compose v3.5+),例如:services: mysql: image: mysql:8.0 environment: - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/root_pw secrets: - root_pw secrets: root_pw: file: ./prod-secrets/mysql-root-pw.txt - 注意:
secrets在非 Swarm 模式下只挂载为文件,不会暴露为环境变量,且默认只读、仅对服务容器可见
初始化后立即禁用本地无密码登录路径
MySQL 官方镜像在首次初始化时,若未设 MYSQL_ROOT_PASSWORD,会生成临时密码并输出到日志;但若设了,又没配好用户权限,仍可能留有 'root'@'localhost' 以外的弱授权入口,比如 'root'@'127.0.0.1' 或通配符主机未显式禁用。
必须检查并清理:
- 进容器执行:
mysql -uroot -p -e "SELECT host,user FROM mysql.user;",确认只有明确需要的 host 条目(如'root'@'localhost'和'root'@'172.18.0.%') - 删掉多余条目:
DROP USER 'root'@'%';(如果不需要远程 root) - 确保
my.cnf中没有skip-grant-tables或skip-networking等调试配置残留 - 若用初始化脚本(
/docker-entrypoint-initdb.d/),脚本末尾务必加FLUSH PRIVILEGES;,否则权限变更不生效
别忽略 bind-address 和网络策略的协同
密码再安全,端口裸露在公网也白搭。很多人以为“设了密码就安全了”,却忘了 MySQL 容器默认监听 0.0.0.0:3306,只要端口映射出去,爆破工具一扫一个准。
实际防护要分三层:
- 容器内:通过挂载自定义
my.cnf,设bind-address = 127.0.0.1(仅限本地访问)或留空(默认0.0.0.0,但需配合下一层) - Docker 网络:用
--network mysql-net创建专用桥接网,不连default网络,应用容器走同一网络直连,不映射宿主机端口 - 宿主机防火墙:用
ufw或iptables显式deny 3306,只对应用服务器 IP 白名单放行(不是容器 IP,是发起连接的源 IP)
最容易被跳过的点是:改了 my.cnf 却没重启容器,或者防火墙规则写了但没 reload;docker exec -it mysql-container ss -tln | grep :3306 输出的监听地址,必须和你的网络设计一致。











