Docker 容器连不上 MySQL?90% 是这几个问题

说实话,Docker 容器连不上数据库这个问题,我至少帮同事解决了十几次。每次都是那几个坑,写篇文章记录一下,下次直接甩链接。


背景

上周五下午,同事跑过来问我:“项目在本机跑得好好的,用 Docker 启动后连不上数据库,什么情况?”

我一看日志,Communications link failure,熟悉的味道。排查了十分钟,发现又是 localhost 的问题。

这类问题占了容器网络问题的 90%以上,今天一次性说清楚。


问题一:连接地址用了 localhost

这是最常见的坑。

在本地开发时,数据库配置通常这样写:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: 123456

本地没问题,但容器里 localhost 指代的是容器自己,不是宿主机。

正确做法:

spring:
  datasource:
    url: jdbc:mysql://host.docker.internal:3306/mydb
    username: root
    password: 123456

host.docker.internal 是 Docker 提供的特殊 DNS,指向宿主机地址。Windows 和 Mac 都支持,Linux 需要加 --add-host 参数。

如果是在 Docker Compose 里,更简单:

services:
  app:
    build: .
    extra_hosts:
      - "host.docker.internal:host-gateway"

问题二:3306 端口没映射

有些同学会这样启动 MySQL 容器:

docker run -d --name mysql mysql:8.0

然后配置连接 localhost:3306,结果连不上。

问题在哪? 没做端口映射啊!

容器的端口默认是隔离的,不做映射外部访问不了。

正确做法:

docker run -d --name mysql -p 3306:3306 mysql:8.0

-p 3306:3306 的意思是,把容器的 3306 端口映射到宿主的 3306 端口。

也可以指定宿主机端口:

docker run -d --name mysql -p 3307:3306 mysql:8.0

这样就连 localhost:3307


问题三:防火墙和安全组

这个坑我踩过,印象深刻。

在云服务器上部署,端口映射也做了,本地就是连不上。查了两小时,最后发现是安全组没开放 3306 端口。

排查思路:

  1. 本地测试端口是否通

    telnet <服务器IP> 3306
    
  2. 在服务器上测试

    curl <服务器IP>:3306
    
  3. 检查防火墙

    # CentOS
    firewall-cmd --list-ports
    # Ubuntu
    ufw status
    
  4. 云服务器检查安全组
    去云平台控制台,确认入方向规则里开放了 3306 端口。


问题四:容器网络不通

如果你的应用和数据库在不同的容器里,需要确保它们在同一个网络。

# 创建网络
docker network create my-network

# 启动 MySQL 并加入网络
docker run -d --name mysql \
  --network my-network \
  -p 3306:3306 \
  mysql:8.0

# 启动应用并加入同一网络
docker run -d --name app \
  --network my-network \
  myapp:latest

在同一个网络里,容器可以通过容器名互相访问:

spring:
  datasource:
    url: jdbc:mysql://mysql:3306/mydb

这里 mysql 是容器的名称,Docker 自带 DNS 会解析到对应的容器 IP。


问题五:认证插件不兼容

MySQL 8.0 默认用了 caching_sha2_password 插件,有些老版本的客户端不支持。

错误信息类似:

Authentication plugin 'caching_sha2_password' cannot be loaded

解决方案一:

启动容器时指定认证插件:

docker run -d --name mysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  mysql:8.0 \
  --default-authentication-plugin=mysql_native_password

解决方案二:

进入容器修改用户认证方式:

docker exec -it mysql mysql

ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;

写在最后

Docker 网络问题看起来复杂,其实套路很固定。

排查顺序:

  1. 先用 telnetnc 测试端口通不通
  2. 检查连接地址是否正确
  3. 确认容器网络配置
  4. 最后查防火墙和安全组

记住这几点,90% 的容器连不上数据库的问题都能快速定位。

有问题评论区交流。

更多推荐