0

I am trying to connect the container of my springboot application with the container of a mysql image using docker-compose, however when I run docker-compose up my terminal starts a loop where it starts the spring application, try to connect with the MySQL container, fails and keep trying. The error that I get is com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failures

docker-compose file:

version: '3.8'

services:
  mysqldb:
    image: mysql
    platform: linux/x86_64
    env_file: ./.env
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=$MYSQLDB_ROOT_PASSWORD
      - MYSQL_DATABASE=$MYSQLDB_DATABASE
    ports:
      - $MYSQLDB_LOCAL_PORT:$MYSQLDB_DOCKER_PORT
    volumes:
      - db:/var/lib/mysql
  app:
    depends_on:
     - mysqldb
    build: .
    restart: always
    env_file: ./.env
    ports:
      - $APP_LOCAL_PORT:$APP_DOCKER_PORT
    environment:
      - DB_HOST=mysqldb
      - DB_USER=$MYSQLDB_USER
      - DB_PASSWORD=$MYSQLDB_ROOT_PASSWORD
      - DB_NAME=$MYSQLDB_DATABASE
      - DB_PORT=$MYSQLDB_DOCKER_PORT
    stdin_open: true
    tty: true

volumes: 
  db:

.env:

MYSQLDB_USER=root
MYSQLDB_ROOT_PASSWORD=12345678
MYSQLDB_DATABASE=dronefeederdb
MYSQLDB_LOCAL_PORT=3306
MYSQLDB_DOCKER_PORT=3306

APP_LOCAL_PORT=8080
APP_DOCKER_PORT=8080

Application.yaml:

server:
  port: 8080
spring:
    datasource:
        username: ${DB_USER}
        password: ${DB_PASSWORD}
        url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}
    jpa:
        hibernate:
            ddl-auto: update
        show-sql: true
        open-in-view: false
        #https://ia-tec-development.medium.com/lombok-e-spring-data-jpa-142398897733
    security.user:
        name: dronefeeder
        password: dronefeeder
        #https://www.baeldung.com/spring-boot-security-autoconfiguration


resilience4j.circuitbreaker:
  configs:
    default:
      waitDurationInOpenState: 10s
      failureRateThreshold: 10
  #instances:
    #estudantes:
      #baseConfig: default

Dockerfile:

FROM openjdk:11.0-jdk as build-image
WORKDIR /app
COPY . .
RUN ./mvnw clean package -DskipTests

FROM openjdk:11.0-jre
COPY --from=build-image /app/target/*.jar /app/app.jar
EXPOSE 8080
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom", "-jar", "/app/app.jar"]

Repository link: https://github.com/julia-baptista/dronefeeder/tree/docker-configuration

antonkronaj
  • 1,237
  • 12
  • 13
  • Your link is pointing to stackoverflow! Anyways, I checked your Dockerfile. Check my answer below. – gvisoc Oct 31 '22 at 17:56
  • This question doesn't seem to require an image upload at all, but you do need to make sure to include enough source code in the question to help understand what's going on. Can you [edit] the question to include a [mcve]? – David Maze Oct 31 '22 at 18:06
  • 1
    (Also please do search for related questions. There are a couple of common misconfigurations, like setting `spring.datasource.url` to point at a `localhost` URL, that get asked very frequently.) – David Maze Oct 31 '22 at 18:07

2 Answers2

2

I believe the issue is your application's use of localhost for the SQL URL in the Application.yaml property file. Since your app runs on a container by itself it tries to look at localhost of the container, while your SQL server is in another container, with its own localhost. Localhost in docker container do not refer to the host, they refer to the localhost within the container itself. If you want to access the host machine, this is an excellent answer From inside of a Docker container, how do I connect to the localhost of the machine?

url: jdbc:mysql://localhost:3306/dronefeederdb

localhost should not be used, you need to use the sql continainer url.

The fastest option is to use host.docker.internal instead of localhost. But it's not the best.

Another quick option is to run the two containers on the same docker network. Define that in your compose file the same way as the volumes. Then set each container to that network. See Networking in Compose. Then you can set your SQL url to use the SQL container name instead of localhost. So this.. url: jdbc:mysql://localhost:3306/dronefeederdb becomes url: jdbc:mysql://mysql/dronefeederdb

Neither option is robust, since you're hardcoding the container name in the application property file. A better solution is to have an environment variable in your webApp image that can accept the URL to the SQL server. Then you can provide the SQL location when running the container, or in your compose file (Environment variables in Compose). This way the SQL server can be anywhere.

Update:

There were a couple of issues in the compose and env files that caused mySQL container to fail startup. Thus the webApp was not able to connect.

Credentials

  • MYSQL_USER was set to root. mySql already creates the user root. You cannot create it again. I changed that to foo. See the Environment Variables section in the official docker image readme for more.
  • MYSQL_PASSWORD was not set. This is the password for the user your app will use. I set this to pass!123
  • The apps DB_PASSWORD was set to user root. That would have been ok if sql had started and it was using the root user I guess. But I changed that to the non-root user since were setting DB_USER=foo

Network was not defined

  • The two containers need to be on the same "docker network" if they are to run together in docker in the same machine. There's more to this which is beyond my experience. But in this case it needs to be on the same network for app to access mysqldb by its container name. I created dronefeederNet and added each container to it.

Files:

.env

MYSQLDB_USER=foo
MYSQLDB_PASSWORD=pass!123
MYSQLDB_ROOT_PASSWORD=12345678
MYSQLDB_DATABASE=dronefeederdb
MYSQLDB_LOCAL_PORT=3307
MYSQLDB_DOCKER_PORT=3306

APP_LOCAL_PORT=8081
APP_DOCKER_PORT=8080

docker-compose.yml

version: '3.8'

services:
  mysqldb:
    image: mysql
    platform: linux/x86_64
    env_file: ./.env
    restart: always
    environment:
      - MYSQL_USER=$MYSQLDB_USER
      - MYSQL_PASSWORD=$MYSQLDB_PASSWORD
      - MYSQL_ROOT_PASSWORD=$MYSQLDB_ROOT_PASSWORD
      - MYSQL_DATABASE=$MYSQLDB_DATABASE
    ports:
      - $MYSQLDB_LOCAL_PORT:$MYSQLDB_DOCKER_PORT
    volumes:
      - db:/var/lib/mysql
    networks:
      - dronefeederNet
  app:
    depends_on:
     - mysqldb
    build: .
    restart: always
    env_file: ./.env
    ports:
      - $APP_LOCAL_PORT:$APP_DOCKER_PORT
    environment:
      - DB_HOST=mysqldb
      - DB_USER=$MYSQLDB_USER
      - DB_PASSWORD=$MYSQLDB_PASSWORD
      - DB_NAME=$MYSQLDB_DATABASE
      - DB_PORT=$MYSQLDB_DOCKER_PORT
    stdin_open: true
    tty: true
    networks:
      - dronefeederNet
volumes: 
  db:
networks:
  dronefeederNet:

Give this a try and I hope it runs. I was able to start it up ok.

enter image description here

antonkronaj
  • 1,237
  • 12
  • 13
  • I tried to do what you recommended about environment variables but I am still having the same error. I updated the code of my files in the post. Would you check to see if I did something wrong? – Jossany Moura Oct 31 '22 at 20:09
  • I’ll pull your code and give it a try myself to see. I suspect it’s because the two containers are not in the same network. Another option is to try and use host.docker.internal in your docker compose file for the value of DB_host without needing to have the same network. The point of environment variables is you can have the option to use any value, whether it’s host.docker.internal, a container name (when in same network) or a domain outside in the cloud. Also I’m not sure how the env variable makes it into your application.yml? Is it being set somewhere? – antonkronaj Nov 02 '22 at 13:16
  • I set my variables in .env file. I put it above on the codes. It will help me a lot if you pull my code to check if you can solve it :D – Jossany Moura Nov 02 '22 at 15:52
  • Kind of good news. The error com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failures persists and the terminal keep in a loop, but I can connect to the container on workbench and the application is functional: I can save new drones on the mysql container and etc kkkkk I dont know nothing anymore – Jossany Moura Nov 02 '22 at 21:21
0

You need to add in the app definition block a depends on: sentence, to make docker compose to not boot the application until the database is up.

Check this documentation: Docker Compose Startup Order

gvisoc
  • 77
  • 3