skip to Main Content

I used mvn clean package to get my target directory and .jar file. Then, I try to put it and mysql database into docker containers but keep failing. It can be executed perfectly outside the containers.

I try to set up the environments through docker compose file, so I guess the application.properties does not matter here. Correct me if I am wrong.

Current structure of my dir:

enter image description here

The error I got:

java.sql.SQLNonTransientConnectionException: Could not create connection to database server. Attempted reconnect 3 times. Giving up.

docker-compose.yml:

version: '3.8'
services:
  mysql-db:
    image: mysql:8.0
    container_name: mysql-container
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: inventory
      MYSQL_USER: user
      MYSQL_PASSWORD: password
    ports:
      - "3306:3306"

  spring-app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: spring-container
    ports:
      - "8082:8080"
    depends_on:
      - mysql-db
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql-db:3306/inventory?autoReconnect=true&useSSL=false
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: root

Dockerfile:

# Use a base image with OpenJDK Java 17 installed
FROM openjdk:17

# Set the working directory in the container
WORKDIR /app

# Copy the packaged Spring Boot application JAR file into the container
COPY target/smartstock-0.0.1-SNAPSHOT.jar /app

# Specify the command to run the Spring Boot application when the container starts
CMD ["java", "-jar", "smartstock-0.0.1-SNAPSHOT.jar"]

application.properties:

#information for connect to MySQL database
spring.datasource.url=jdbc:mysql://mysql-db:3306/inventory?autoReconnect=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#auto building table in database according to entity class we have
spring.jpa.hibernate.ddl-auto=update
#show what sql is jpa using
spring.jpa.show-sql=true
#reduce logging information. only shows warm
logging.level.root=warn

2

Answers


  1. Chosen as BEST ANSWER

    I ended up with the following settings. This setting is originally from this youtube video.

    application.properties:

       #information for connect to MySQL database
        spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/test
        spring.datasource.username=root
        spring.datasource.password=root
        spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
        #auto building table in database according to entity class we have
        spring.jpa.hibernate.ddl-auto=update
        #show what sql is jpa using
        spring.jpa.show-sql=true
        #reduce logging information. only shows warm
        logging.level.root=warn
        
    

    docker-compose.yml:

    version: "3"
    services: 
      springboot-app:
       # container_name: spring-app
        image: springbooot-app
        restart: always
        build: .
        ports:
          - 8082:8080 
        environment:
          MYSQL_HOST: mysqldb
          MYSQL_USER: root
          MYSQL_PASSWORD: root
          MYSQL_PORT: 3306
      mysqldb: 
        container_name: mysqldb
        image: mysql:8.0
        ports: 
         - 3307:3306   
        environment:
          MYSQL_DATABASE: test
          MYSQL_ROOT_PASSWORD: root
    

    Dockerfile:

    # Use a base image with OpenJDK Java 17 installed
    FROM openjdk:17
    
    # Set the working directory in the container
    WORKDIR /app
    
    # Copy the packaged Spring Boot application JAR file into the container
    COPY target/smartstock-0.0.1-SNAPSHOT.jar /app
    
    # Specify the command to run the Spring Boot application when the container starts
    CMD ["java", "-jar", "smartstock-0.0.1-SNAPSHOT.jar"]
    

  2. When working with Dockerized databases, it’s crucial to use the container name for the host instead of the service name in your code.

    Therefore, your connection string should be:

    jdbc:mysql://mysql-container:3306/inventory?autoReconnect=true&useSSL=false
    

    as you specified your MySQL container name on your docker-compose.yaml.

    To ensure compatibility both locally and in Docker, adjust your application.properties file as follows:

    spring.datasource.url=${MYSQL_URL:jdbc:mysql://localhost:3306/inventory?autoReconnect=true&useSSL=false}
    

    and set on your docker-compose.yaml on the spring-app service the environment variable:

    environment:
        MYSQL_URL: jdbc:mysql://mysql-container:3306/inventory?autoReconnect=true&useSSL=false
    

    or

    you can specify just the host part, like this:

    spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/inventory?autoReconnect=true&useSSL=false
    

    and set on your docker-compose.yaml on the spring-app service the env:

    environment:
        MYSQL_HOST: mysql-container
    

    This way when you run locally without the env it will use your localhost database and when you Dockerize your application you will pass an env with the correct host for your connection string.

    You can see an example on running an application with Dockerized PostgreSQL on this Baeldung article: https://www.baeldung.com/spring-boot-postgresql-docker

    UPDATE:

    Another thing that you should do to work is add an restart-policy to your spring-app. The depends_on keyword on his own is not enough.

    This is what happens when you execute the docker compose up. The docker will initialize your mysql-db, and after it initializes the depends_on policy will make your spring-app initializes in sequence, but there is the problem, your database is initialized, but not fully loaded and working. So when you load your spring-app it will not find a database and will fail.

    To prevent this, you should add an restart policy to your spring-app, so when it loads and not found a database it will fail but will start another on sequence. Add it like this:

          spring-app:
            build:
              context: .
              dockerfile: Dockerfile
            container_name: spring-container
            ports:
              - "8082:8080"
            depends_on:
              - mysql-db
            environment:
              MYSQL_URL: jdbc:mysql://mysql-container:3306/inventory?autoReconnect=true&useSSL=false
              MYSQL_USERNAME: root
              MYSQL_PASSWORD: root
            deploy:
              restart_policy:
                condition: on-failure
                max_attempts: 10
    

    with this it will try to restart your application until it runs properly, at max 10 times.

    The name of the environment variable doesn’t matter, as long as is the same as you specify on your application.properties. As a convention I recommend you to use the MYSQL_URL name for the env, because SPRING_DATASOURCE_URL gives an impression that is an Spring provided property. You should end with something like this:

        spring.datasource.url=${MYSQL_URL:jdbc:mysql://localhost:3306/inventory?autoReconnect=true&useSSL=false}
        spring.datasource.username=${MYSQL_USERNAME:root}
        spring.datasource.password=${MYSQL_PASSWORD:root}
    

    Attention: The spring.datasource.url is not an environment variable, just what is inside the ${ } is one.

    I would also recommend you to connect your app and database to the same docker network. Providing better isolation, security and communication, preventing from eventual communication issues.

    Following everything you should end with an docker-compose like this:

    version: '3.8'
    services:
      mysql-db:
        image: mysql:8.0
        container_name: mysql-container
        environment:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: inventory
          MYSQL_USER: user
          MYSQL_PASSWORD: password
        ports:
          - "3306:3306"
        networks:
          - springmysql
    
      spring-app:
        build:
          context: .
          dockerfile: Dockerfile
        container_name: spring-container
        ports:
          - "8082:8080"
        depends_on:
          - mysql-db
        environment:
          MYSQL_URL: jdbc:mysql://mysql-container:3306/inventory?autoReconnect=true&useSSL=false
          MYSQL_USERNAME: root
          MYSQL_PASSWORD: root
        deploy:
          restart_policy:
            condition: on-failure
            max_attempts: 10
        networks:
          - springmysql
    
    networks:
      springmysql:
        name: springmysql
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search