Rafael A P Nascimento
Rafael A P Nascimento

Reputation: 475

Spring Boot + docker-compose + MySQL: Connection refused

I'm trying to set up a Spring Boot application that depends on a MySQL database called teste in docker-compose. After issuing docker-compose up, I'm getting:

Caused by: java.net.ConnectException: Connection refused (Connection refused)

I'm running on Linux Mint, my docker-compose version is 1.23.2, my Docker version is 18.09.0.

application.properties

# JPA PROPS
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy

spring.datasource.url=jdbc:mysql://db:3306/teste?useSSL=false&serverTimezone=UTC
spring.datasource.username=rafael
spring.datasource.password=password

spring.database.driverClassName =com.mysql.cj.jdbc.Driver

docker-compose.yml

version: '3.5'
services:
  db:
    image: mysql:latest
    environment:
      - MYSQL_ROOT_PASSWORD=rootpass
      - MYSQL_DATABASE=teste      
      - MYSQL_USER=rafael
      - MYSQL_PASSWORD=password
    ports:
      - 3306:3306
  web:
    image: spring-mysql
    depends_on:
      - db
    links:
      - db
    ports:
      - 8080:8080
    environment:
      - DATABASE_HOST=db
      - DATABASE_USER=rafael
      - DATABASE_NAME=teste
      - DATABASE_PORT=3306

and the Dockerfile

FROM openjdk:8
ADD target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Upvotes: 12

Views: 12438

Answers (5)

Rostislav Matl
Rostislav Matl

Reputation: 4543

You need to declare networks for both the services and and make them use the same to communicate.

If the dependency (here db) takes long to start you need some mechanism to wait for it - I would say doing it (re-connecting) on application level would make your app more resilient. I think for a database a standard connection pool like Hikari takes care of it already.

Upvotes: 0

Supun Sandaruwan
Supun Sandaruwan

Reputation: 2418

For the connection refuse issue, we need to consider a few things

  1. When providing DB URL for Spring application. you need to specify the MySQL service name as host

spring.datasource.url=jdbc:mysql://<mysql_service_name_here>:3306/<db_here>

  1. Spring application can be looking for MySQL DB connection before properly starting the MySQL container. To eliminate that issue should spring-app container make wait using healthcheck and depends_on properties on compose file.

  2. All two containers should be in the same docker network.

Perfect sample compose file that includes those solutions.

version: '3'
services:
  mysql:
    container_name: mysql
    image: mysql:5.7
    healthcheck:
      test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ]
      interval: 10s
      timeout: 5s
      retries: 5
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: http_logger
      MYSQL_USER: supun
      MYSQL_PASSWORD: password
    ports:
      - "3307:3306"
    networks:
      - logger-network
    volumes:
      - mysql-data:/var/lib/mysql

  http-logger:
    image: http-logger:v1.8.0
    container_name: http-logger
    ports:
      - "8080:8080"
    build: .
    depends_on:
      mysql :
        condition: service_healthy
    networks:
      - logger-network
    environment:
      MYSQL_HOST: mysql
      MYSQL_PORT: 3306
      MYSQL_DB: http_logger
      MYSQL_USER: supun
      MYSQL_PASSWORD: password

networks:
  logger-network:
    driver: bridge

volumes:
  mysql-data:

Here is perfectly working spring boot + MySQL volume + docker-compose example. https://github.com/supunUOM/spring_mysql_docker_volume

Upvotes: 0

Vikas Kumar
Vikas Kumar

Reputation: 157

I was facing the same issue and in case you do not want to use any custom scripts, this can easily be resolved using health checks along with depends on. A sample using these is as follows:

services:
  mysql-db:
    image: mysql
    environment:
      - MYSQL_ROOT_PASSWORD=vikas1234
      - MYSQL_USER=vikas
    ports:
      - 3306:3306
    restart: always
    healthcheck:
      test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
      timeout: 20s
      retries: 10

  app:
    image: shop-keeper
    container_name: shop-keeper-app
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 8080:8080
    depends_on:
      mysql-db:
        condition: service_healthy
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql-db:3306/shopkeeper?createDatabaseIfNotExist=true
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: vikas1234

Upvotes: 6

Hansika Weerasena
Hansika Weerasena

Reputation: 3364

Docker compose always starts and stops containers in dependency order, or sequential order in the file if not given. But docker-compose does not guarantee that it will wait till the dependency container is running. You can refer here for further details. So the problem here is that your database is not ready when your spring-mysql container tries to access the database. So, the recommended solution is you could use wait-for-it.sh or similar script to wrap your spring-mysql app starting ENTRYPOINT.

As example if you use wait-for-it.sh your ENTRYPOINT in your Dockerfile should change to following after copying above script to your project root:

ENTRYPOINT ["./wait-for-it.sh", "db:3306", "--", "java", "-jar", "app.jar"]

And two other important thing to consider here is:

  • Do not use links they are deprecated you should use user-defined network instead. All services in docker-compose file will be in single user-defined network if you don't explicitly define any network. So you just have to remove the links from compose file.
  • You don't need to publish the port for docker container if you only use it inside the user-defined network.

Upvotes: 5

grapes
grapes

Reputation: 8636

Your config looks nice, I would just recommend:

  • Remove links: db. It has no value in user-defined bridge networking
  • Remove port exposing for db unless you want to connect from outside docker-compose - all ports are exposed automatically inside user-defined bridge network.

I think the problem is that database container takes more time to start than web. depends_on just controls the order, but does not guarantee you database readiness. If possible, set several connection attempts or put socket-wait procedure in your web container.

Upvotes: 1

Related Questions