docker安装mysql怎么配端口?挂载与密码一次设好
- 2026年11月11日
- 2 次
机房那台测试机到期不续了,上面只剩一个跑了三年的 MySQL 5.7,配置散在 /etc/my.cnf 和 /etc/my.cnf.d 两处,改一次要翻两个文件。新机器上不打算再来一遍,改成docker安装mysql,同事那边等着要连接串。要定的只有三样:端口往哪绑、数据目录挂在哪、root 密码和字符集怎么给。
端口映射与数据挂载:3306 只绑回环地址
端口这一项,结论是先写 -p 127.0.0.1:3306:3306。前面加地址不是语法讲究,docker安装mysql只在这个地址上开口子:本机的 php、脚本、客户端连 127.0.0.1:3306 照旧,外面的扫描器连不上。写成 -p 3306:3306 等价于绑 0.0.0.0,宿主机有公网地址又没防火墙,库就摆在互联网上了。

区分这两种写法不用猜,docker port mysql 的输出会直接写成 3306/tcp -> 127.0.0.1:3306 或者 3306/tcp -> 0.0.0.0:3306,箭头右边就是实际监听地址。
挂载那半边,第一次做这类迁移最容易写错的是 -v 左边。它必须是绝对路径,写 /data/mysql;写成 mysql-data 这种,docker 会当成 named volume 的名字,数据落到 /var/lib/docker/volumes 下面,备份脚本按路径去找就扑空。右边固定 /var/lib/mysql,那是镜像里的 datadir。
属主比权限位重要。容器里的 mysqld 以 mysql 用户运行,镜像里这个 uid 是 999,宿主目录属主对不上又没有写权限,初始化会直接失败,docker logs 里是 Errcode 13 的 Permission denied。chown -R 999:999 /data/mysql 比 chmod 777 干净,也不用把目录开放给所有人。
拼起来是下面这条,端口、挂载、密码、字符集都在里面,跑一次不用再进容器改。
docker run -d --name mysql \
--restart unless-stopped \
-p 127.0.0.1:3306:3306 \
-v /data/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD='Changeme_2026' \
-e MYSQL_DATABASE=app \
mysql:8.0 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_0900_ai_ci
root密码与字符集:参数只在数据目录为空时生效
MYSQL_ROOT_PASSWORD 有个前提:它只在数据目录为空、镜像执行初始化的那一次起作用。/data/mysql 里已经有数据,再改这个变量重启,密码不会跟着变,得进容器用 ALTER USER 改。所以第一次跑之前把密码定下来,后面换 -e 不起作用。

字符集同理。--character-set-server 和 --collation-server 是 mysqld 的启动参数,写在镜像名之后。8.0 的镜像默认就是 utf8mb4,排序规则 utf8mb4_0900_ai_ci;5.7 的服务端默认还是 latin1,不显式给,建库建表时中文就按 latin1 存。默认值跟着版本走,以官方文档为准。
不写进 my.cnf 挂进去的理由很实在:多一个文件要对齐,换台机器还得跟着拷。参数只有三个的时候,跟在命令行后面更直接。
MYSQL_DATABASE=app 建出来的库跟着服务端字符集走,命令里给了 utf8mb4 就不会跑偏。麻烦在导入旧数据:dump 文件里如果写死了 latin1 的建表语句,导进去还是 latin1,得用 ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4 逐表转,大表这一下要锁,耗时按行数算。所以迁移前先把 dump 里的字符集声明改掉,比事后转表便宜得多。
起来之后按输出核三件事
容器 Up 不等于配对了,起来之后至少要核三样:状态、端口落在哪个地址、字符集到底是什么。
docker port mysql
docker exec mysql mysql -uroot -p'Changeme_2026' \
-e "show variables like 'character_set_server'"
第一行如果显示 -> 0.0.0.0:3306,说明 -p 前面的地址漏了。Value 那一行不是 utf8mb4,说明参数没跟到镜像名后面,或者被镜像自带的 my.cnf 覆盖掉了。想一次看全,把条件换成 like 'character_set%',8.0 上会列出 client、connection、database、filesystem、results、server、system 七个变量,再补一条 like 'collation%' 一起看——字符集对了、排序规则还是旧的,中文比较照样出问题。
端口撞车是最常见的一种。容器起不来,报 Bind for 127.0.0.1:3306 failed: port is already allocated,报错里不说谁占着,得自己查:
ss -lntp | grep 3306
pid 对应的就是老的那个 mysqld,这台机器上原来 yum 装的 5.7 还在跑。systemctl stop mysqld 之后容器能起来,端口不用改。
版本和客户端对不上的地方
8.0 默认认证插件是 caching_sha2_password。拿它去接老系统的,多半会先撞上这一条:PHP 5.6 的 mysql 扩展、几年前的 Navicat 连上来直接报 Authentication plugin 'caching_sha2_password' cannot be loaded。临时解法是启动参数加 --default-authentication-plugin=mysql_native_password,代价是新密码走老算法,安全性弱一档;8.4 之后这个插件默认不加载,参数不一定还认,升级前看一遍弃用说明。
另一处是 datadir 非空。/data/mysql 里留着别的版本写过的文件,容器不覆盖也不初始化,直接拿 8.0 去开旧文件,几秒后退出。docker logs --tail 50 mysql 翻到最后,是 InnoDB 报 redo log 版本不匹配;这种情况删目录重建也没用,得先把旧数据导出来。跨大版本迁移走 mysqldump 或者 mysqlsh 的升级检查,换镜像 tag 解决不了。
什么时候别照这套配
单机、内部工具、数据量几十 G、停机窗口自己定,docker安装mysql按这套参数走就行。场景一变,先改这三处:应用和库在同一台宿主机上,3306 不用发布到宿主,建个 docker network 让应用用服务名连,-p 整行可以删掉;代理的情况要分开看:haproxy 装在宿主机上,走 127.0.0.1 一样连得通,-p 不用动;代理自己也在容器里,就别发布端口了,两端加进同一个 docker network 用服务名连。改绑 docker0 那种写法把地址写死在两层网络之间,换台机器就失效,不建议;密码写在 -e 里会进 docker inspect 和 shell history,多人维护的机器改成挂配置文件或者用 secret。

团队已经在用 compose 管容器的,别混着 docker run 再加一个。docker compose down 按项目标签走,手工起的容器不在它的账上,出问题时两边状态对不上,排查要多花一轮。
判断这套参数对不对,docker安装mysql只有一条依据:谁要连这个库。只有本机进程连,127.0.0.1 加 999 属主这套就够用;一旦有第二台机器、或者别的团队的人要连,绑定地址、账号的 host、防火墙规则得一起改。单改一个 -p,只是把门挪了个位置,门锁还是原来那把。
若非作者标注皆为李涛自留地原创文章,转载或复制请以超链接形式并注明出处。
- PHP array_filter() 函数详解:按条件过滤数组元素 2026-07-02
- PHP数据类型转换:字符串数据类型强制转换为整数数据类型 2022-06-10
- PHP array_diff() 和 array_intersect() 详解:数组集合运算 2026-07-02
- PHP日期函数mktime()如何使用 PHP时间函数mktime()实例代码 2022-10-15
- 如何给zblogphp后台登录地址加密? 2023-01-13
- PHP中if判断语句的各种方式 PHP中if判断语句如何嵌套 2022-06-23
