docker compose problem while using spring boot mysql docker

Viewed 1141

I'm getting this exception while trying to run 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.

My docker-compose.yml looks like this

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

Have been struggling for a while and seen people with similar problems, but still didn't found the solution. Is there a problem with my docker-compose.yml?

2 Answers

Adding to @Shawrup's excellent description of what's happening, another solution is to add a healtcheck to the MySQL container. This will cause Docker Compose to wait for the healthcheck to be successful before starting any dependent containers.

Your MySQL container configuration could look something like this:

  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

The problem here is, your application is trying to connect to mysql before it's ready. Accordinfg to official documentation

You can control the order of service startup and shutdown with the depends_on option. Compose always starts and stops containers in dependency order, where dependencies are determined by depends_on, links, volumes_from, and network_mode: "service:...".

However, for startup Compose does not wait until a container is “ready” (whatever that means for your particular application) - only until it’s running. There’s a good reason for this.

The problem of waiting for a database (for example) to be ready is really just a subset of a much larger problem of distributed systems. In production, your database could become unavailable or move hosts at any time. Your application needs to be resilient to these types of failures.

To handle this, design your application to attempt to re-establish a connection to the database after a failure. If the application retries the connection, it can eventually connect to the database.

Use a tool such as wait-for-it, dockerize, sh-compatible wait-for, or RelayAndContainers template. These are small wrapper scripts which you can include in your application’s image to poll a given host and port until it’s accepting TCP connections.

From here.

I found that, spring boot 2.3.4 does not stop your app if db connection fails. It will retry if the app tries to access db and connection will be established if db is up.

Another workaround is, start your db first and then start your app.

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