PAYATHON 2026

DockerHub 镜像启动后 Spring Boot 无法连接 MySQL,如何解决?

数据小陈

当前报错的直接原因是应用运行时无法解析数据库主机名 mysql-db。先让 Spring Boot 容器和 MySQL 服务加入同一个 Docker 网络,并传入正确的数据库连接配置。现有堆栈无法证明存在密码、Hibernate 或 JDBC 驱动兼容性问题。

为什么本地能运行,拉取镜像后却失败?

异常链最底部显示:

Caused by: java.net.UnknownHostException: mysql-db

这表示连接在主机名解析阶段就失败了,还没有进入数据库认证或 DDL 执行阶段。外层的 Unable to open JDBC Connection for DDL execution 则是 Hibernate 获取连接失败后的表现。

你的 Compose 文件为两个服务声明了相同的 spring-cloud-network。按该配置正确启动后,应用可以通过服务名 mysql-db 访问数据库。

DockerHub 保存的是镜像。拉取并运行 API 镜像时,项目中的 docker-compose.yaml 不会自动执行,MySQL 服务、数据库卷和 Compose 中的环境变量也不会自动配置。

所示 Dockerfile 只向镜像复制了应用 JAR。如果在 Docker Desktop 中单独启动这个镜像,还需要配置数据库容器、网络和环境变量。题目没有提供这次启动的具体参数,因此无法确定缺少了哪一项。

使用 Compose 运行 DockerHub 镜像

如果 MySQL 和 API 都运行在当前 Docker 环境中,可以继续用 Compose 管理两个服务,将 project-server 的 build: . 换成 DockerHub 镜像地址。健康检查时间可以根据实际初始化耗时调整。

services:
  mysql-db:
    image: mysql:8.4.7
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - db:/var/lib/mysql
    ports:
      - "3305:3306"
    networks:
      - spring-cloud-network
    healthcheck:
      test:
        - CMD-SHELL
        - >-
          mysql --protocol=TCP -h 127.0.0.1
          -u"$$MYSQL_USER" -p"$$MYSQL_PASSWORD"
          "$$MYSQL_DATABASE" -e 'SELECT 1' >/dev/null 2>&1
      interval: 10s
      timeout: 5s
      retries: 12
      start_period: 30s
    restart: unless-stopped

  project-server:
    image: "${DOCKERHUB_USERNAME}/api-project-pmt:latest"
    ports:
      - "8081:8081"
    environment:
      SPRING_DATASOURCE_URL: "jdbc:mysql://mysql-db:3306/${MYSQL_DATABASE}?${MYSQL_JDBC_PARAMS}"
      SPRING_DATASOURCE_USERNAME: ${MYSQL_USER}
      SPRING_DATASOURCE_PASSWORD: ${MYSQL_PASSWORD}
    depends_on:
      mysql-db:
        condition: service_healthy
    networks:
      - spring-cloud-network
    restart: on-failure:10

networks:
  spring-cloud-network:
    driver: bridge

volumes:
  db:

在部署目录的 .env 中保留所需的数据库配置,并新增 DOCKERHUB_USERNAME,填入实际的 DockerHub 用户名。然后在该目录执行:

docker compose --env-file .env pull
docker compose --env-file .env up -d
docker compose ps
docker compose logs --tail=100 mysql-db project-server

这里通过 image: 使用发布后的镜像。原来的 build: . 是从本地构建上下文构建应用镜像,不能据此确认运行的是 DockerHub 中的版本。

健康检查中的 $$ 会将美元符号传给容器中的 shell,以便读取 MySQL 容器自身的环境变量。

数据库地址和端口应该怎么填?

地址取决于应用运行的位置:

应用运行位置数据库地址端口
与 MySQL 位于同一 Compose 网络的容器mysql-db3306
Docker 所在宿主机上的本地应用localhost3305

3305:3306 将宿主机的 3305 端口映射到 MySQL 容器的 3306 端口。两个容器通过同一网络通信时,使用 mysql-db:3306。

不要把应用容器内的数据库地址直接改成 localhost,因为它在应用容器中指向容器自身。如果数据库部署在宿主机或远程服务器,应改用该位置可访问的地址。

检查运行时环境变量是否真正传入

原配置使用了两组凭据:

  • application.properties 使用 root。
  • Compose 的 environment 使用 .env 中的 MYSQL_USER 和 MYSQL_PASSWORD,即另一组账户。

通过 Compose 启动时,传入的 Spring 配置可以覆盖 JAR 内的配置。如果单独运行镜像且未传入这些配置,应用会继续使用 JAR 中的值。

.env 在 Compose 中主要用于 ${变量名} 插值,其中的变量不会全部自动注入所有容器。上面的配置显式传入了 SPRING_DATASOURCE_URL、SPRING_DATASOURCE_USERNAME 和 SPRING_DATASOURCE_PASSWORD。

GitHub Actions 中的 DOCKERHUB_USERNAME、DOCKERHUB_TOKEN 用于登录镜像仓库,不会自动成为部署容器的数据库配置。

仍然失败时,怎样继续定位?

先检查两个容器的实际网络:

docker inspect $(docker compose ps -q project-server) --format '{{json .NetworkSettings.Networks}}'
docker inspect $(docker compose ps -q mysql-db) --format '{{json .NetworkSettings.Networks}}'

两个结果应包含同一个网络名称。Compose 创建的实际名称可能带有项目名前缀,以检查结果为准。如果某个服务没有容器,先通过 docker compose ps -a 和日志确认原因。

如果应用镜像提供了 getent,还可以在应用容器运行期间检查解析:

docker compose exec project-server getent hosts mysql-db

没有 getent 命令只说明镜像缺少该工具,不能据此判断 DNS 失败。

接下来根据新的异常继续排查:

  • 仍为 UnknownHostException:检查服务是否存在、网络是否一致,以及实际 JDBC 主机名。
  • 出现 Connection refused:检查 MySQL 是否已监听目标端口、是否初始化完成。
  • 出现 Access denied:检查账户、密码和授权。
  • 出现连接超时:检查网络路径、防火墙和目标服务状态。

db 卷会保留数据库状态。如果 MySQL 此前已经初始化,仅修改 .env 中的初始化账户或密码,不会自动修改已有数据库中的账户。遇到认证错误时,应核对已有账户,不要为排查连接问题直接删除数据卷。

为什么增加超时或替换 Hibernate 没有解决?

连接超时、自动重连和容器重启,都无法为不存在或在当前网络中不可见的主机名补上解析记录。restart: on-failure:10 只负责失败后的重启,也不等同于数据库健康检查。

先修复 mysql-db 的解析和网络连接,再根据后续异常判断是否需要处理依赖。当前日志不足以将根因归结为 Hibernate 版本。

此外,spring.jpa.hibernate.ddl-auto=create-drop 会涉及建表和删表,适合可丢弃数据的开发环境。连接恢复前,应先确认目标数据库中的数据是否允许被重建。

备注:内容仅供参考。