I have a problem to run docker-compose up -d
to run Spring Boot on Docker. I have a MySQL database connection issue when the app tries to run integration test with test container.
As I cannot connect to MySQL provided by test container, I got this error shown below
Here are logs shown
#0 131.6 2023-09-17T15:12:16.594Z INFO 103 --- [ main] com.zaxxer.hikari.HikariDataSource : mysql - Starting...
#0 132.8 2023-09-17T15:12:17.783Z ERROR 103 --- [ main] com.zaxxer.hikari.pool.HikariPool : mysql - Exception during pool initialization.
#0 132.8
#0 132.8 com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure
#0 139.8 [ERROR] AuthControllerTest>AbstractTestContainerConfiguration.beforeAll:17 » IllegalState Previous attempts to find a Docker environment failed. Will not retry. Please see l
ogs and check configuration
#0 139.8 [ERROR] BookControllerTest>AbstractTestContainerConfiguration.beforeAll:17 » IllegalState Previous attempts to find a Docker environment failed. Will not retry. Please see l
ogs and check configuration
#0 139.8 [ERROR] OrderControllerTest>AbstractTestContainerConfiguration.beforeAll:17 » IllegalState Could not find a valid Docker environment. Please see logs and check configuration
#0 139.8 [ERROR] StatisticsControllerTest>AbstractTestContainerConfiguration.beforeAll:17 » IllegalState Previous attempts to find a Docker environment failed. Will not retry. Please
see logs and check configuration
Here is the Dockerfile shown below
# Stage 1: Build stage
FROM maven:3.8.4-openjdk-17-slim AS build
# Copy Maven files for dependency resolution
COPY pom.xml ./
COPY .mvn .mvn
# Copy application source code
COPY src src
# Build the project and create the executable JAR
RUN mvn package
# Stage 2: Run stage
FROM openjdk:17-jdk-slim
# Set working directory
WORKDIR bookdelivery
# Copy the JAR file from the build stage
COPY --from=build target/*.jar bookdelivery.jar
# Expose port 1221
EXPOSE 1221
# Set the entrypoint command for running the application
ENTRYPOINT ["java", "-jar", "bookdelivery.jar"]
Here is the docker-compose.yml shown below
version: "3.9"
services:
database:
container_name: database
image: mysql:8.0.33
restart: always
env_file:
- .env # Use the .env file for environment variables
environment:
MYSQL_DATABASE: bookdelivery
MYSQL_PASSWORD: ${DATABASE_PASSWORD}
MYSQL_ROOT_PASSWORD: ${DATABASE_PASSWORD}
MYSQL_ROOT_HOST: '%'
MYSQL_PORT: 3307
volumes:
- ./db:/var/lib/mysql
ports:
- "3307:3306"
networks:
- bookDeliveryNetwork
bookdelivery:
build:
context: .
dockerfile: Dockerfile
container_name: bookdelivery
restart: on-failure
env_file:
- .env # Use the .env file for environment variables
ports:
- "1221:1221"
environment:
- server.port=1221
- spring.datasource.username=${DATABASE_USERNAME}
- spring.datasource.password=${DATABASE_PASSWORD}
- BOOK_DELIVERY_DB_IP=database
- BOOK_DELIVERY_DB_PORT=3307
- spring.datasource.url=jdbc:mysql://database:3307/bookdelivery
depends_on:
- database
networks:
- bookDeliveryNetwork
networks:
bookDeliveryNetwork:
Here is the testContainer shown below
@Testcontainers
public abstract class AbstractTestContainerConfiguration {
static MySQLContainer<?> MYSQL_CONTAINER = new MySQLContainer<>("mysql:8.0.33");
@BeforeAll
static void beforeAll() {
MYSQL_CONTAINER.withReuse(true);
MYSQL_CONTAINER.start();
}
@DynamicPropertySource
private static void overrideProps(DynamicPropertyRegistry dynamicPropertyRegistry) {
dynamicPropertyRegistry.add("spring.datasource.username", MYSQL_CONTAINER::getUsername);
dynamicPropertyRegistry.add("spring.datasource.password", MYSQL_CONTAINER::getPassword);
dynamicPropertyRegistry.add("spring.datasource.url", MYSQL_CONTAINER::getJdbcUrl);
}
}
Here is the application.yml of test shown below
spring:
config:
import: optional:file:.env[.properties]
sql:
init:
mode: always
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
format_sql: true
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
datasource:
name: mysql
username: ${DATABASE_USERNAME:root}
password: ${DATABASE_PASSWORD:password}
url: jdbc:mysql://${BOOK_DELIVERY_DB_IP:localhost}:${BOOK_DELIVERY_DB_PORT:3306}/bookdelivery
maximum-pool-size: 5
connection-timeout: 180000
maximum-lifetime: 170000
jwt:
secret: 404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970
expireMs: 60000
refrEshexpireMs: 120000
Here is the application.yml of main shown below
server:
port: 1221
# MYSQL
spring:
config:
import: optional:file:.env[.properties]
datasource:
url: jdbc:mysql://${BOOK_DELIVERY_DB_IP:localhost}:${BOOK_DELIVERY_DB_PORT:3306}/bookdelivery
username: ${DATABASE_USERNAME:root}
password: ${DATABASE_PASSWORD:password}
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
show-sql: true
# JWT
jwt:
secret: 404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970
expireMs: 600000 # 10 Minutes
refrEshexpireMs: 120000
# SWAGGER
springdoc:
api-docs:
enabled: true
show-actuator: true
How can I fix it?
Here is the repo : Link
2
Answers
I fixed the issue
Change
mvn package
tomvn clean install -DskipTests
inDockerfile
as Testcontainer tries to find Docker itself but it cannot find it. That's why I have to skip tests.Few things are wrong and overly complicated here:
First, inside your ‘docker-compose.yml’ file for the database service, “MYSQL_USER” is not set.
Secondly i beleive your ‘.env’ file or your enviorment file is containing two environment variable named “DATABASE_USERNAME” and “DATABASE_PASSWORD”.
Thirdly, it’s not mandatory, but things getting complicated in management perspective to set the “MYSQL_PORT” to 3306. Then exposing this port to the host in port 3306. Keep the port 3306 in the container end and expose that port (if you want to access it directly from the host) otherwise no need this exposure. Simplifying the “spring.datasource.url” values port part to “3306”
Fourth, follows the third, we are setting “spring.datasource.url” directly, then we don’t need to set the “BOOK_DELIVERY_DB_IP” and “BOOK_DELIVERY_DB_PORT” setting. because they got overidden by the setting of “spring.datasource.url”
Now the most important thing,
like every database services out there, mysql need time to get ready for incoming connection. You can call it readiness of database service. You already specified that the your application service is dependent on the database service, but thats not enough, because, docker have no way to know, that your database service is ready for incoming connection. In that case, you case introduce HEALTHCHECK for your database service, which will tell docker that your database service is healthy, and then docker tries to spin up your application container.
Simplest healthcheck can be formulated as following
Complete working implementation can be found here https://github.com/ratulSharker/webrest-starter
Happy coding.
Edited
1 ) You don’t need to use
MYSQL_USER
2 ) DATABASE_USERNAME and DATABASE_PASSWORD come from
.env
fileThere is a problem to get variables from AbstractTestContainerConfiguration
I tried to run
docker compose config
and here is the result shown belowI can get the BOOK_DELIVERY_DB_IP, BOOK_DELIVERY_DB_PORT, DATABASE_USERNAME and DATABASE_PASSWORD.
Can you revise the answer?