使用spring boot mysql docker时docker撰写问题

Posted

技术标签:

【中文标题】使用spring boot mysql docker时docker撰写问题【英文标题】:docker compose problem while using spring boot mysql docker 【发布时间】:2021-03-04 10:18:09 【问题描述】:

我在尝试运行 docker compose 时遇到此异常

app-server_1  | com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
app-server_1  | 
app-server_1  | The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.

我的 docker-compose.yml 看起来像这样

version: "3.7"

services:
  db:
    image: mysql:5.7
    ports:
      - "3306:3306"
    restart: always
    environment:
      MYSQL_DATABASE: ppmt
      MYSQL_USER: vilius
      MYSQL_PASSWORD: vilius123
      MYSQL_ROOT_PASSWORD: root
    networks:
      - backend

  app-server:
    build:
      context: simple-fullstack
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    restart: always
    depends_on:
      - db
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/ppmt?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
      SPRING_DATASOURCE_USERNAME: vilius
      SPRING_DATASOURCE_PASSWORD: vilius123
    networks:
      - backend

networks:
  backend:

application.properties

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url =jdbc:mysql://db:3306/ppmt?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username=vilius
spring.datasource.password=vilius123

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.datasource.initialize=true

苦苦挣扎了一段时间,看到有人遇到类似问题,但还是没有找到解决办法。我的 docker-compose.yml 有问题吗?

【问题讨论】:

检查JDBC URL中的IP地址或主机名是否正确 我的建议是将 jdbc:mysql... 字符串用双引号括起来,因为 : yaml 中的字符 【参考方案1】:

除了@Shawrup 对正在发生的事情的出色描述外,另一种解决方案是将healtcheck 添加到MySQL 容器中。这将导致 Docker Compose 在启动任何依赖容器之前等待运行状况检查成功。

您的 MySQL 容器配置可能如下所示:

  db:
    image: mysql:5.7
    ports:
      - "3306:3306"
    restart: always
    environment:
      MYSQL_DATABASE: ppmt
      MYSQL_USER: vilius
      MYSQL_PASSWORD: vilius123
      MYSQL_ROOT_PASSWORD: root
    networks:
      - backend
    healthcheck:
      test: "/usr/bin/mysql --user=root --password=root --execute \"SHOW DATABASES;\""
      interval: 2s
      timeout: 20s
      retries: 10

【讨论】:

我还更改了 spring 容器以在失败时重新启动它,因为depends_on 仅确保 db 将启动,但大多数情况下 db 加载速度没有 spring 项目那么快,它会导致 link_failure。 【参考方案2】:

这里的问题是,您的应用程序在准备好之前尝试连接到 mysql。根据官方文档

您可以使用 depends_on 选项控制服务启动和关闭的顺序。 Compose 总是按依赖顺序启动和停止容器,其中依赖由depends_on、links、volumes_from 和network_mode 确定:“service:...”。

但是,对于启动,Compose 不会等到容器“准备好”(无论这对您的特定应用程序意味着什么)——只等到它运行。这是有充分理由的。

等待数据库(例如)准备就绪的问题实际上只是分布式系统的一个更大问题的一个子集。在生产中,您的数据库可能随时变得不可用或移动主机。您的应用程序需要能够适应这些类型的故障。

要处理此问题,请将您的应用程序设计为在发生故障后尝试重新建立与数据库的连接。如果应用程序重试连接,它最终可以连接到数据库。

使用 wait-for-it、dockerize、sh-compatible wait-for 或 RelayAndContainers 模板等工具。这些是小型包装脚本,您可以将它们包含在应用程序的映像中,以轮询给定的主机和端口,直到它接受 TCP 连接。

来自here。

我发现,如果数据库连接失败,spring boot 2.3.4 不会停止您的应用程序。如果应用程序尝试访问 db,它将重试,如果 db 启动,将建立连接。

另一种解决方法是,先启动您的数据库,然后再启动您的应用程序。

docker-compose up db
docker-compose up app-server

【讨论】:

我觉得这个答案被接受很奇怪,因为depends_on: db 已经在应用服务器定义中指定了。 depends_on 只是确保 db 在 app-server 之前运行,并不意味着 db 会在您的 app-server 启动之前准备好接受请求。 明白了。一个更好的解决方案可能是使用等待脚本 (github.com/Eficode/wait-for)。否则,在我看来,使用 docker-compose 并没有什么意义。 是的,答案中也提到了它,因为 docker docs 推荐它。我还添加了我的发现作为另一种选择。

以上是关于使用spring boot mysql docker时docker撰写问题的主要内容,如果未能解决你的问题,请参考以下文章

Docker 容器整合 Spring Boot 应用

IDEA快速部署Spring Boot 项目到Docker

Vaadin Flow 是不是支持 Spring-Boot Native

Spring Boot:在Spring Boot中使用Mysql和JPA

无法加载在 Docker 中运行的 Spring Boot 应用程序

Spring Boot 2.0:Docker Compose + Spring Boot + Nginx + Mysql 实践