最近一段时间,我连续被好几个朋友问同一个问题:Docker 和 MySQL 到底怎么搭配?Docker Desktop 明明装好了,docker pull mysql也执行了,结果启动报错、远程连不上、进入容器不会执行 SQL,整个过程卡得死死的。这其实不是个别现象,Docker 跑 MySQL 跟直接在 Windows 或 Linux 上装 MySQL 是两套完全不同的玩法,很多人拿裸装的思路去套容器,自然处处碰壁。
这篇文章我想从零开始,把整套链路完整走一遍:Docker Desktop 的准备、MySQL 镜像拉取、正式容器的启动参数、进入容器执行 SQL 文件、远程客户端连接,以及常用的配置调优和排错。目标很直接,就是让你照着做能跑通,并且理解每一步为什么这样做,而不是死记命令。
1. Docker 跑 MySQL 前,先搞清这套玩法跟裸装到底差在哪
1.1 容器里那个 MySQL 不是"在 Windows 上装的 MySQL"
很多人第一次用 Docker 装 MySQL,脑子里还停留在"下载安装包,下一步下一步,然后连 3306 端口"的惯性里。实际上,Docker 里的 MySQL 是一个一次性可替换的运行环境。你docker run创建容器,本质上是基于镜像启动了一个隔离的进程,这个进程里运行着 Linux 版的 mysqld,它的文件系统、配置文件、数据目录都是独立的一套,跟你宿主机上的 Windows 或者 macOS 没有直接关系。
这就引出了几个关键差异:
- 数据目录:容器内的
/var/lib/mysql是 MySQL 存放数据的地方,但容器一旦被删除,里面的数据默认也会跟着消失。所以必须把宿主机目录或数据卷挂载进去,数据才安全。 - 配置文件:裸装 MySQL 读的是
my.ini或my.cnf,容器内也有这个文件,但你需要通过挂载方式覆盖它,或者用环境变量控制一部分参数。 - 端口:容器内 MySQL 默认监听 3306,但这个端口默认不直接暴露给宿主机。你得通过
-p 3306:3306做端口映射,宿主机才能访问。 - 进程生命周期:容器不是后台常驻的"服务",它更像一个被
docker start/stop控制的独立进程单元。docker restart mysql8不会像 Windows 服务管理器那样弹出一堆权限提示。
我见过一个典型误区:有人在容器里执行了service mysql start,然后问我"为什么还是连不上"。因为容器启动时真正执行的命令是mysqld,也就是启动脚本里那个入口点(entrypoint)。容器生命周期绑定的是这个 mysqld 进程,而不是 systemd 或者 service 体系。你在容器里手动service mysql start通常毫无意义,甚至会出现"进程已经跑着,但容器认为主进程死了"的错乱现象。
1.2 什么场景适合 Docker 版 MySQL,什么场景别硬上
Docker 版 MySQL 最大的价值是环境隔离和快速重建。比如你同时维护多个项目的数据库,项目 A 用 MySQL 5.7,项目 B 用 MySQL 8.0,裸装就得处理版本冲突;用 Docker 就很简单,两个容器各跑各的版本,端口分别映射到 3306 和 3307。再比如你写完一个项目的部署文档,新同事要复现环境,给一条docker run命令就够了,不用在对方机器上安装、初始化、改配置反复折腾。
还有一类高频场景是本地开发。我经常在本地起一个临时 MySQL 容器,用来验证某条 SQL 的执行计划、测试慢查询优化效果,或者跑自动化测试的数据库初始化脚本。测试完直接docker rm -f,干净利落,不影响本机已有的数据库。
但有些场景我不建议硬上 Docker。如果你要跑一个在线高并发生产库,并且团队对容器编排、网络、存储性能没有足够经验,裸装或云数据库往往是更稳的选择。Docker 部署 MySQL 在生产环境不是不行,但需要额外处理数据卷性能、备份恢复策略、容器 CPU 内存限制等一堆问题,很多团队折腾半天,最后发现还不如买一个云数据库省心。另外,如果你在低配 Windows 上跑 Docker Desktop,本身就要吃不少内存,再跑一个 MySQL 容器,电脑可能会明显变卡。这一点事先要有预期。
2. Docker Desktop 和镜像准备:装好不等于能用
2.1 Windows 下装 Docker Desktop 的两个后端与一个常见报错
Windows 上装 Docker Desktop,最关键的决策是后端选 WSL 2 还是 Hyper-V。WSL 2 是目前的主流推荐,启动快、内存管理更聪明、跟 Windows 文件系统交互也方便。如果你用的是 Windows 10/11 专业版、企业版或教育版,也可以选 Hyper-V;但如果你是 Windows Home,没得选,老老实实用 WSL 2。
安装过程本身不复杂,去 Docker 官网下载 Docker Desktop Installer,双击安装,安装器会让你选是否使用 WSL 2。这里有个容易忽略的点:如果之前没装过 WSL 内核,最好先打开 PowerShell 执行wsl --install,然后重启电脑再装 Docker Desktop。否则 Docker Desktop 启动时会提示找不到 WSL 内核,或者报WSL kernel version too low。
很多人卡在启动这一步:打开 Docker Desktop,右下角那个鲸鱼图标转半天,然后弹出一个类似Virtualization support not detected或者Docker Desktop failed to start because...的报错。这个问题十有八九是 BIOS 里的虚拟化开关没开。解决方法:重启电脑进 BIOS,找到Intel Virtualization Technology(Intel 平台)或SVM Mode(AMD 平台),设为 Enabled,保存退出再启动。装完之后在 PowerShell 里执行下面命令验证:
wsl --status wsl --set-default-version 2 docker versiondocker version能正常打印 Client 和 Server 两段信息,才说明 Docker 引擎真的跑起来了。注意,如果只显示了 Client 段,Server 段报错,通常就是 Docker Desktop 里的引擎没启动。先把 WSL 2 后端跑通,再继续下一步。
还有一个容易被忽略的点:Docker Desktop 默认会占 2GB 左右内存,在Settings -> Resources里可以调 WSL 2 的内存上限。我个人习惯给 Docker 分配 4GB 左右,因为后面还要跑 MySQL 和可能的数据集,太抠会卡。
2.2 拉取 MySQL 镜像:版本选择有讲究
镜像版本这块,我不建议直接写latest。latest标签虽然方便,但 MySQL 大版本升级时行为变化很大,尤其是认证插件、字符集默认值这些,很可能让你昨天还能连的客户端今天突然连不上。更稳妥的做法是固定大版本,比如:
docker pull mysql:8.0这样拉取的是 8.0 系列的最新补丁版,不会突然跳到 9.x。如果你需要完全锁定,也可以指定具体小版本,比如mysql:8.0.36。对于老项目兼容需求,可能还要用mysql:5.7,但要注意 5.7 已经停止官方更新,新项目不建议再选。
mysql:8.0镜像默认的认证插件是caching_sha2_password,这跟 MySQL 5.7 时代的mysql_native_password不一样。某些老版本的 Navicat、SQLyog、JDBC 驱动在连接时会出现Authentication plugin 'caching_sha2_password' cannot be loaded或类似报错。这通常不是配置问题,而是客户端太旧不认识新认证协议。遇到这种情况,要么升级客户端驱动,要么在创建用户时显式指定mysql_native_password,后面授权部分我会详细讲。
2.3 用临时容器验证镜像是否正常
镜像拉下来之后,先别急着配一堆参数,我习惯先跑一个临时容器验证镜像能不能正常初始化。这个步骤能帮你区分"镜像问题"和"配置问题":
docker run --name mysql-test \ -e MYSQL_ROOT_PASSWORD=Root@123 \ -d mysql:8.0执行完等十几秒,然后看日志:
docker logs mysql-test如果看到类似ready for connections或者mysqld: ready for connections的日志,说明镜像本身没问题。MySQL 官方镜像首次初始化会做几件事:创建数据目录、初始化系统表、设置 root 密码。这些步骤执行完需要一个过程,在这个过程中如果你急着连接,会报Access denied或连接拒绝。看到日志里出现ready for connections才算真正初始化完成。
验证完这个临时容器,直接删掉:
docker rm -f mysql-test这里已经能看出容器的一个特性:没有挂载数据卷时,删除容器等于连数据一起删。刚才只是在测试,数据无所谓,真正使用时必须挂载。
3. 正式启动:一条 docker run 把目录挂载、密码、端口一次配齐
3.1 推荐的生产型启动命令逐参数拆解
临时容器验证通过后,就可以正式创建 MySQL 容器了。下面这条命令是我在本地开发和轻量测试环境里最常用的模板:
docker run -d \ --name mysql8 \ --restart unless-stopped \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=Root@123 \ -e TZ=Asia/Shanghai \ -v /d/docker/mysql/data:/var/lib/mysql \ -v /d/docker/mysql/config/my.cnf:/etc/mysql/conf.d/my.cnf \ mysql:8.0逐个拆解一下:
-d:后台运行容器,不加的话终端会被 mysqld 日志占住,一关终端容器就停。--name mysql8:给容器起个固定名称,之后docker exec、docker start、docker logs都靠这个名称引用。不命名的话容器会随机生成一个很别扭的名字。--restart unless-stopped:容器异常退出时自动重启,但如果你手动docker stop它,就不会自动拉起来。这个策略对本地开发很友好,重启 Docker Desktop 后 MySQL 会自动恢复。-p 3306:3306:宿主机 3306 端口映射到容器 3306 端口。左侧是宿主机端口,右侧是容器端口,改左侧就能换对外端口。-e MYSQL_ROOT_PASSWORD=Root@123:设置 root 密码。官方镜像首次初始化时会读取这个环境变量。注意,这个密码只在首次初始化数据目录时生效。数据目录已经初始化之后,你再改这个环境变量重启容器,密码不会跟着变。-e TZ=Asia/Shanghai:不设置时区,MySQL 的CURRENT_TIMESTAMP可能跟你本地时间差 8 小时。-v /d/docker/mysql/data:/var/lib/mysql:把宿主机目录挂到容器数据目录,这是数据持久化的关键。-v /d/docker/mysql/config/my.cnf:/etc/mysql/conf.d/my.cnf:把自定义配置文件挂进容器的配置目录。/etc/mysql/conf.d/下所有.cnf文件都会被 mysqld 自动读取。
启动后执行docker ps看状态,如果容器一直在restarting,先别慌,用docker logs mysql8看日志。最常见的失败原因就是 data 目录挂载后权限不对,或者 my.cnf 里写了 MySQL 8.0 不认识或者不允许动态修改的参数。
3.2 目录挂载和数据卷到底怎么选
数据持久化有两种方式:bind mount(绑定挂载)和 named volume(命名卷)。
- bind mount:直接指定
/宿主机路径:/容器路径。路径直观,方便你把数据目录拷走备份,也能直接用编辑器改配置文件。缺点是 Windows 下路径写法要注意,/d/docker/mysql/data这种是 Git Bash 里的写法,PowerShell 下写D:\docker\mysql\data:/var/lib/mysql会有坑,需要写成/d/docker或D:/docker/mysql/data,建议统一用正斜杠加绝对路径。 - named volume:写法是
-v mysql_data:/var/lib/mysql,Docker 会自动在它管理的目录下创建卷,好处是不用关心具体路径,docker volume inspect mysql_data可以查看真实位置。缺点是你不太方便直接打开目录手动拷贝文件。
我个人在本地开发中用 bind mount 偏多,因为出了问题能直接进目录翻数据文件,还能顺带备份。如果你用 Docker Compose 管理多容器,用 named volume 更整洁。但无论哪种,必须保证宿主机目录有可写权限。Windows 下相对宽松,Linux 上经常遇到mysqld: Can't create/write to file '/var/lib/mysql/'这类权限错误,排查思路就是看目录属主是不是 uid 999(MySQL 官方镜像内的 mysql 用户 uid),不行就chown -R 999:999或改用命名卷自动处理权限。
3.3 端口映射冲突怎么办
-p 3306:3306最常遇到的问题就是宿主机 3306 端口已经被占用。比如你电脑上本来装了 MySQL 服务,监听 3306,Docker 再映射就会报port is already allocated。
排查方法:
netstat -ano | findstr :3306看到 PID 后,去任务管理器找到对应进程。如果是本机 MySQL 服务,可以把它停掉或者改成另一个端口;如果不想动本机 MySQL,Docker 这边就改端口映射:
docker run -d \ --name mysql8 \ -p 3307:3306 \ ...这样宿主机 3307 映射到容器 3306,客户端连接时用localhost:3307即可。这里有个常见误会:有人以为改了容器内 MySQL 的端口,实际上你只需要改左侧映射,容器内还是 3306,不需要进容器改port=3307。
如果你把-p 3307:3306中的右侧也改成 3307,那反而更麻烦:容器内部 mysqld 默认监听 3306,如果配置没改,端口映射目标不存在,就连不上了。新手很容易在这一步绕晕,记住一句:左侧是宿主机要用的端口,右侧是容器内 MySQL 实际监听的端口,右侧别乱动。
4. 进入容器执行 SQL:docker exec 的一百种打开方式
4.1 交互式进入容器操作 MySQL
正式容器的核心操作之一就是进容器执行 SQL。很多人连不上网络客户端,第一反应是"镜像坏了",其实先进容器用命令行验证最直接。
最常规的交互方式:
docker exec -it mysql8 bash进入容器 shell 后,再执行 mysql 客户端:
mysql -uroot -p输入密码后进入 MySQL 交互界面,这时可以执行任意 SQL:
SELECT VERSION(); SHOW DATABASES; USE mysql; SELECT user, host FROM user;可以一箭双雕,直接用镜像里的 mysql 客户端进入:
docker exec -it mysql8 mysql -uroot -p它会提示输入密码。如果你想避免交互,或者要在脚本里自动执行,可以写成:
docker exec -it mysql8 mysql -uroot -p'Root@123' -e "SELECT NOW();"但我个人不建议在命令行直接暴露密码,-p后面留空会让客户端交互提示输入,更安全。
如果你只想快速验证,可以用:
docker exec mysql8 mysql -uroot -p'Root@123' -e "SELECT 1;"注意,不用-it也可以执行一次性命令。区别在于-t会分配伪终端,输出格式化更漂亮;不带-t适合做管道重定向。比如把 SQL 文件灌进 MySQL,就用不带-t的模式更合适。
4.2 不进入容器直接执行 SQL 文件
实际开发里更多场景是"本地有个 test.sql,要把它导入容器里的 MySQL"。你不需要先进容器再想办法把文件传进去,直接用标准输入重定向就行:
docker exec -i mysql8 mysql -uroot -p'Root@123' < test.sql注意这里是-i而不是-it。-i保持标准输入打开,-t分配伪终端;重定向场景下不该有伪终端,处理\r\n和中文编码时更容易出问题。
同理,备份也是反过来的:
docker exec mysql8 mysqldump -uroot -p'Root@123' --databases mydb > mydb_backup.sql这样备份文件直接落在宿主机当前目录,非常方便。我再强调一下:mysqldump 和 mysql 都是容器内部的命令,你通过docker exec调用,不用在宿主机安装任何 MySQL 客户端。
还有一种批量初始化的玩法:官方镜像支持把.sql、.sh文件放到挂载到/docker-entrypoint-initdb.d/的目录里,首次初始化数据目录时会自动执行。这对搭建开发环境特别有用,比如:
docker run -d \ --name mysql8 \ -e MYSQL_ROOT_PASSWORD=Root@123 \ -v /d/docker/mysql/data:/var/lib/mysql \ -v /d/docker/mysql/initdb:/docker-entrypoint-initdb.d \ mysql:8.0initdb目录下的schema.sql会在 MySQL 首次启动时自动导入。这个机制很省事,但只对首次初始化生效,如果数据目录已经存在,再添加脚本不会重新执行。
4.3 容器内跑备份恢复
备份和恢复是日常绕不开的操作,容器场景下我推荐下面这套闭环:
备份到宿主机:
docker exec mysql8 mysqldump -uroot -p'Root@123' --single-transaction --routines --triggers mydb > /d/backup/mydb_$(date +%Y%m%d).sql恢复时直接灌:
docker exec -i mysql8 mysql -uroot -p'Root@123' mydb < /d/backup/mydb_20260601.sql恢复之前要先确保目标数据库存在,可以先用-e "CREATE DATABASE IF NOT EXISTS mydb"创建,或者导入--databases mydb的整库备份。
--single-transaction参数很关键,对 InnoDB 表可以在不锁表的情况下做一致性备份。如果数据量大,还可以考虑备份到容器内再拷出来,但正常本地开发用上面的管道式写法完全够。
5. 给远程客户端开绿灯:账号授权、认证插件和 SSL 连接
5.1 为什么 root 远程连不上
容器跑起来了,命令行能查询,接下来很多人会兴冲冲打开 Navicat 或 MySQL Workbench 连数据库,结果连不上。要理解这个现象,得分两层:容器网络层和 MySQL 账号层。
网络层方面,容器做了端口映射,宿主机 3306 能连到容器 3306,所以从本地客户端连接时地址写localhost或127.0.0.1就行。如果改成localhost还报错,多半不是网络问题。
账号层才是重头戏。MySQL 8.0 默认只给 root 创建了root@localhost账号,也就是仅允许从本地 socket 或回环地址连接。当你从宿主机通过 TCP 访问容器里的 MySQL 时,客户端连接来自容器的网关地址,在 MySQL 看来这是一个"远程"来源,root@localhost是不匹配的,于是报ERROR 1045 (28000): Access denied for user 'root'@'...'。
有人说"那我改 root 的 host 为 % 不就行了?"可以,但我不推荐在生产或共享环境里开放 root 远程登录。正确做法是创建专用应用账号。
5.2 新建应用账号并授权
进入容器:
docker exec -it mysql8 mysql -uroot -p然后创建账号并授权:
CREATE USER 'app'@'%' IDENTIFIED BY 'App@123456'; GRANT ALL PRIVILEGES ON mydb.* TO 'app'@'%'; FLUSH PRIVILEGES;'%'表示允许任意主机连接。如果只想让某个内网网段连接,可以写'192.168.1.%'。权限粒度看需求,ALL PRIVILEGES在本地开发够用,生产环境建议按最小权限给。
这里又要提到认证插件。MySQL 8.0 默认caching_sha2_password,老客户端支持不好。如果连接时报错或提示认证插件无法加载,可以在建账号时指定老协议:
CREATE USER 'app'@'%' IDENTIFIED WITH mysql_native_password BY 'App@123456'; GRANT ALL PRIVILEGES ON mydb.* TO 'app'@'%';或者针对已有账号修改:
ALTER USER 'app'@'%' IDENTIFIED WITH mysql_native_password BY 'App@123456';但要注意,mysql_native_password在 MySQL 8.0 里已经标记为 deprecated,未来大版本可能移除,新项目尽量用新插件并升级客户端。JDBC 8.0.x 以上驱动默认支持caching_sha2_password,多数的现代客户端也没问题。
5.3 遇到 1045、2002、SSL 错误的排查思路
远程连接常见的报错无非这几种,我一个个说:
ERROR 1045 (28000): Access denied for user
认证失败。先确认账号是否存在、密码是否正确、授权的 host 是否覆盖你当前的来源 IP。SQL 里可以这样查:
SELECT user, host, plugin FROM mysql.user;ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock'
这个报错有意思。它表示你用的 MySQL 客户端尝试通过 socket 文件连接,而不是 TCP。当你在容器里执行mysql -uroot -p,默认走 socket,如果 socket 路径不对,就会出现。从宿主机外部用 Navicat 连接时走 TCP,一般不会出这个问题。真正遇到这个报错时,用 TCP 方式指定端口即可,比如在容器里执行:
mysql -h127.0.0.1 -P3306 -uroot -pSSL 连接错误
我遇到过几次 MySQL 客户端提示SSL connection error或者 JDBC 提示Establishing SSL connection without server's key verification is not recommended。前者是客户端要求 SSL 但 MySQL 配置不支持,后者是 JDBC 默认开启 SSL 校验导致。
解决办法根据场景来:
- 用 MySQL Workbench 连接时,把
SSL选项卡设为No或If Available。 - JDBC 连接串里追加
useSSL=false&allowPublicKeyRetrieval=true。allowPublicKeyRetrieval这个参数在caching_sha2_password插件下很关键,否则可能出现公钥检索失败。 - 如果你确实需要 SSL 连接,那就要在容器里配置证书,这个属于进阶玩法,本地开发一般不必折腾。
这一步配好之后,Navicat、MySQL Workbench、DBeaver 这些工具都能正常连上容器里的 MySQL 了。
6. 配置调优与避坑:my.cnf、慢日志、资源限制
6.1 自定义 my.cnf 的正确姿势
Docker 里的 MySQL 同样支持自定义配置文件。官方镜像的默认配置比较保守,很多项目要调整字符集、连接数、SQL 模式等,就需要在宿主机写好my.cnf挂载进去。
前面启动命令里挂载的是/etc/mysql/conf.d/my.cnf,这个路径下追加一个配置文件即可。一个常见的本地常用配置长这样:
[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_0900_ai_ci max_connections=500 sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION default-time-zone=+08:00 lower_case_table_names=1有几个参数要特别提醒:
lower_case_table_names:MySQL 8.0 在 Linux 上默认值是 0,即表名区分大小写。如果你从 Windows 迁移过来,Windows 上默认不区分,需要在初始化数据目录之前就定好这个值。如果已经初始化再改,服务可能启动失败,报错日志会提示Table 'mysql.plugin' doesn't exist之类。这时候只能调整配置后重新初始化数据目录,或者干脆全小写表名写代码。sql_mode:我见过有些老的业务 SQL 不带 group by 全字段,如果沿用 MySQL 5.7 的默认严格模式,8.0 可能更严格,需要按业务调整。max_connections:连接数不是越大越好,每个连接都有内存开销,容器内存有限时设太高反而容易 OOM。
改完配置重启容器:
docker restart mysql8然后进容器验证:
docker exec mysql8 mysql -uroot -p -e "SHOW VARIABLES LIKE 'max_connections';"6.2 开启慢查询日志定位慢 SQL
容器里的 MySQL 定位慢 SQL,方式跟裸装大同小异。在自定义配置中加上:
slow_query_log=1 slow_query_log_file=/var/lib/mysql/slow.log long_query_time=1long_query_time=1表示超过 1 秒的查询记录到慢日志。注意,慢查询日志文件写在数据目录里,数据目录挂载到了宿主机,所以宿主机上直接就能打开看。
在容器内也可以实时查:
docker exec mysql8 mysql -uroot -p -e "SHOW VARIABLES LIKE 'slow_query_log';"真正分析时,我常用两种方式。一是直接打开 slow.log 看,二是用 MySQL 自带的mysqldumpslow汇总:
docker exec mysql8 mysqldumpslow -s c -t 10 /var/lib/mysql/slow.log-s c按查询次数排序,-t 10取前十名,能快速找出最频繁的慢查询。
额外的调试利器是EXPLAIN。拿到慢 SQL 后,在容器里执行:
EXPLAIN SELECT * FROM orders WHERE user_id = 123;看type和rows字段,如果出现全表扫描或者预估行数特别大,就要考虑加索引。这套排查链路在 Docker 容器里跟裸装完全一样,只是入口换成了docker exec。
6.3 容器资源限制与常见启动失败排查
容器不是免费的。MySQL 跑在容器里同样要吃 CPU 和内存。我见过太多人电脑卡到飞起,就是因为 MySQL 容器的内存完全没有限制。给 Docker 一个硬指标模板:
docker run -d \ --name mysql8 \ --memory="1g" \ --cpus="1.0" \ -p 3306:3306 \ ...--memory="1g"限制容器最多使用 1GB 内存,--cpus="1.0"限制最多一个 CPU 核心。如果是本地开发,这个配置足够常见场景使用;如果跑复杂查询,可以适当调到 2GB。限制资源的好处是 MySQL 疯了也不至于拖垮整台机器。
启动失败的排查,我按概率排序写个清单:
| 症状 | 主要原因 | 处理方法 |
|---|---|---|
| 容器一直 Restarting | 数据目录权限不对、配置文件参数非法、端口被占 | docker logs mysql8看具体日志 |
Can't connect to MySQL server | 端口映射没生效或 3306 被占用 | docker ps查看端口映射,netstat排查端口 |
Access denied | 密码错、host 不匹配、认证插件不支持 | 检查账号 host,必要时重建账号 |
Table 'mysql.plugin' doesn't exist | lower_case_table_names 初始化后变更 | 重新初始化数据目录或恢复原配置 |
docker network 不通 | 容器间网络配置问题 | 多个 MySQL 容器使用自定义网络docker network create后互联 |
容器间通信是另一个常踩的坑。比如你同时用 Docker 跑 MySQL 和 Spring Boot 应用,应用容器里写localhost:3306是连不到 MySQL 容器的,因为两个容器各自有独立网络栈。正确做法是创建一个自定义网络:
docker network create mynet启动 MySQL 时加--network mynet,启动应用容器时也加--network mynet,应用里连接地址写容器名mysql8而不是 localhost。这样 Docker 内置 DNS 会自动解析。这个细节曾经折磨过我很久,第一次遇到时我以为 MySQL 挂了,结果只是网络模式的问题。
如果你觉得docker run参数太多记不住,可以考虑用 Docker Compose,配置写在 YAML 里更直观,也方便团队共享环境。一个简版 compose 文件可以这样写:
services: mysql8: image: mysql:8.0 container_name: mysql8 restart: unless-stopped ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: "Root@123" TZ: "Asia/Shanghai" volumes: - ./mysql/data:/var/lib/mysql - ./mysql/config/my.cnf:/etc/mysql/conf.d/my.cnf然后docker compose up -d就行。
踩过几次坑之后,我现在的习惯是:凡是涉及容器的数据库环境,先把日志、数据卷、端口映射列清楚,再动手敲命令。尤其不要凭记忆写-v路径,路径写错时容器也能启动,但数据存到了你找不到的地方,恢复和备份都会很尴尬。整套跑通后你会发现,Docker 里的 MySQL 其实比裸装更好伺候——它能随时销毁重建,配置和数据分离,环境迁移成本极低。只要把挂载、端口、认证这三件事想明白,剩下的都是套路。