MySQL 容器 Compose 启动报错 2003 Can't connect 如何排查

文章导读
2003 表示连接请求没有到达 MySQL,或者到达后被拒绝。这个错误码和账号密码错误不同,账号或密码错误时客户端通常会报 1045 access denied。看到 2003 时先不要重设密码,按照“容器状态、端口映射、网络模式、MySQL 监听地址、启动顺序”的顺序逐项排查。每一步都要有操作和验证,不要连续改多个地方再回头看结果。
📋 目录
  1. 先确认 MySQL 容器是否真的就绪
  2. 端口映射是否真的落在宿主机上
  3. 网络模式和连接地址要分对象
  4. MySQL 的监听地址和 skip_networking
  5. 启动顺序和健康检查
A A

先确认 MySQL 容器是否真的就绪

2003 表示连接请求没有到达 MySQL,或者到达后被拒绝。这个错误码和账号密码错误不同,账号或密码错误时客户端通常会报 1045 access denied。看到 2003 时先不要重设密码,按照“容器状态、端口映射、网络模式、MySQL 监听地址、启动顺序”的顺序逐项排查。每一步都要有操作和验证,不要连续改多个地方再回头看结果。

2003错误最常见的原因是容器尚未就绪或启动后崩溃退出。执行 docker compose ps 查看容器状态,当状态显示 Up(healthy)或 Up(正常退出除外)时再尝试连接。如果容器反复重启,可以借助 docker compose logs mysql 观察日志,MySQL初始化时会在日志中输出“ready for connections”字样,只有在这一行出现后连接才会成功。如果日志中出现初始化完成但容器立即退出,通常是因为数据目录权限或配置文件中的命令问题,需要逐行核对error日志,而不要急着改连接参数。

这里有个实际操作上的判断:容器显示 Up,不代表 MySQL 已经在监听 3306。第一次启动如果数据目录是空的,MySQL 会先做初始化,期间连接必然失败。建议用 docker compose logs -f mysql 单独开一个窗口看日志,等 ready for connections 出现后,再执行 mysql 客户端测试。

端口映射是否真的落在宿主机上

先确认宿主机端口是否真正映射到容器内的3306端口。执行 docker compose ps 观察容器的端口映射列,应看到类似 0.0.0.0:3306->3306/tcp 的记录。如果端口没有出现在映射列表里,多半是 docker-compose.yml 中 ports 配置有格式错误,或者容器启动失败导致没有绑定端口。可以用 docker port 单独查看映射关系,若完全没有输出,说明容器未正常运行或端口配置未生效。注意,即便容器内MySQL进程在跑,只要宿主机端口没有被占用,客户端从宿主机去连接仍会报2003。

MySQL 容器 Compose 启动报错 2003 Can't connect 如何排查

如果 docker port 返回了 0.0.0.0:3306,但客户端依然 2003,那问题就不在端口映射这一层。需要继续确认 MySQL 的监听地址,以及宿主机是否有防火墙规则拦截了 3306。注意,firewall-cmd 或 iptables 的检查要和容器网络分开看,因为映射走的是 Docker 的 nat 表。

网络模式和连接地址要分对象

当使用docker compose时,默认会创建自定义bridge网络,服务名就是容器的主机名。此时如果在宿主机上用127.0.0.1去连接,端口映射正常的话可以通;但如果容器使用了 host 网络模式,则必须通过宿主机IP或127.0.0.1访问,此时不再有端口映射,却需要MySQL监听在非loopback地址。另一个常见坑是:当你在应用容器内去连接数据库容器时,不能写127.0.0.1,而要写compose服务名,例如 mysql:3306。如果连接地址写错,即使端口映射正常,也会出现2003,因为网络栈不在同一个namespace中。

判断当前网络模式可以执行 docker inspect -f '{{.HostConfig.NetworkMode}}' <容器名>。如果是 host,端口映射那一列通常不会显示,即使 compose 文件里写了 ports 也会被忽略。如果应用容器和数据库容器在同一个 compose 项目里,优先用服务名连接;如果跨宿主机,才需要考虑宿主机 IP 和端口映射。

MySQL 容器 Compose 启动报错 2003 Can't connect 如何排查

MySQL 的监听地址和 skip_networking

另一个经常被忽略的点是 bind-address。自定义镜像或挂载了自定义 my.cnf 后,可能把 bind-address 写成 127.0.0.1。这样容器内的 MySQL 只监听回环地址,宿主机通过端口映射访问时,流量进入的是容器网卡,连接会被拒绝。检查时可以进入容器执行 mysql -e "show variables like 'bind_address'",注意不要只信镜像默认值。如果结果是 127.0.0.1,就把配置改成 0.0.0.0,然后重新创建容器,通常需要 docker compose up -d --force-recreate mysql,而不是 docker compose restart mysql。

还需要确认 skip_networking 是否为 ON。这个选项开启后,MySQL 会拒绝所有 TCP 连接,端口映射和 bind-address 都没有意义。检查方式同样是进入容器执行 mysql -e "show variables like 'skip_networking'",如果返回 ON,就在配置文件里注释掉 skip-networking,再重建容器。2003 是网络层错误,但 skip_networking 会导致网络层直接拒绝,所以这一步不能省。

MySQL 容器 Compose 启动报错 2003 Can't connect 如何排查

启动顺序和健康检查

在 Compose 编排里,depends_on 只控制容器启动顺序,不能保证 MySQL 进程已经 ready。如果应用容器启动时数据库还在初始化,2003 就会随机出现。建议在应用启动脚本里加入重试循环,每 2 秒尝试一次连接,最多等 30 秒。也可以给 MySQL 服务配置 healthcheck,比如用 mysqladmin ping 判断端口是否可连,这样依赖服务可以通过 condition: service_healthy 延迟启动。

healthcheck 的写法和镜像提供的工具有关,使用 mysql 官方镜像时,可以在 compose 文件里配置:

healthcheck:
  test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-u", "root"]
  interval: 5s
  timeout: 3s
  retries: 10

这段配置只适合本地 root 免密或环境变量允许的情况,如果你给 root 设置了密码,需要在 mysqladmin ping 参数里带上 -p 对应的密码参数,或者用 MYSQL_PWD 环境变量。配置后执行 docker compose ps 查看状态,看到 healthy 再启动依赖服务。