I am capsuling a Spring-boot API that works together with an MS SQL Server using docker compose.
This is how the docker-compose.yml
file looks like:
version: '3.8'
services:
api:
depends_on:
db:
condition: service_completed_successfully
build: ./mdm-web-api
restart: on-failure
env_file: ./.env
ports:
- $SPRING_LOCAL_PORT:$SPRING_DOCKER_PORT
stdin_open: true
tty: true
db:
ports:
- 1488:1433
build: ./db
This is the Dockerfile for the SQL Server:
# We choose exact tag (not 'latest'), to be sure that new version won't break creating image
FROM mcr.microsoft.com/mssql/server:2019-latest
# change active user to root
USER root
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Copy initialization scripts
COPY . /usr/src/app
# Grant permissions for the run-initialization script to be executable
RUN chmod x /usr/src/app/run-initialization.sh
# change back to user mssql
USER mssql
# Set environment variables, not to have to write them with docker run command
# Note: make sure that your password matches what is in the run-initialization script
ENV SA_PASSWORD <my pw>
ENV ACCEPT_EULA Y
ENV MSSQL_PID Express
# Expose port 1433 in case accesing from other container
EXPOSE 1433
# Run Microsoft SQl Server and initialization script (at the same time)
# Note: If you want to start MsSQL only (without initialization script) you can comment bellow line out, CMD entry from base image will be taken
CMD /bin/bash ./entrypoint.sh
And this one for the API:
FROM maven:3.8.7-eclipse-temurin-19
WORKDIR /mdm-web-api
COPY . .
RUN mvn clean install
CMD mvn spring-boot:run
When I run docker-compose up
I am sure the API is not waiting for the db to be ready. I tried with both, service_completed_successfully
and service_healthy
without success. This is how the terminal looks like:
As you may noticed mvn clean install
is executed right away and it does not wait for the db service to be started. I even need additional time because a large initialization script in included in the database service.
When testing the service_healthy
I added this line to the MSSQL Dockerfile:
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "/opt/mssql-tools/bin/sqlcmd", "-S", "localhost", "-U", "sa" ,"-P", "CorrectHorseBatteryStapleFor$", "-d", "dwh-demo", "-Q", "SET NOCOUNT ON SELECT \"YAY WE ARE UP\"" ]
Same result.
Any ideas or new things to try?
CodePudding user response:
depends_on
only applies at run-time. It does not apply at build time.
Docker-compose will first build any images that need to be built.
After all images are built, it will start them up and it's at this time that depends_on
is used.
CodePudding user response:
This is how I solved it:
docker-compose.yml
version: '3.9'
services:
api:
depends_on:
db:
condition: service_healthy
build: ./mdm-web-api
restart: on-failure
env_file: ./.env
ports:
- $SPRING_LOCAL_PORT:$SPRING_DOCKER_PORT
stdin_open: true
tty: true
db:
ports:
- 1480:1433
build: ./db
Notice I have added a condition to the depends_on using the long syntax. I want to be sure the database is up and running, including the initialization script, before the spring-boot API tries to connect to it.
I also added a health check to the Dockerfile of the database but could also be added directly to the docker-compose file: Dockerfile mssql:
# We choose exact tag (not 'latest'), to be sure that new version won't break creating image
FROM mcr.microsoft.com/mssql/server:2019-latest
# change active user to root
USER root
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Copy initialization scripts
COPY . /usr/src/app
# Grant permissions for the run-initialization script to be executable
RUN chmod x /usr/src/app/run-initialization.sh
# change back to user mssql
USER mssql
# Set environment variables, not to have to write them with docker run command
# Note: make sure that your password matches what is in the run-initialization script
ENV SA_PASSWORD my-super-strong-password
ENV ACCEPT_EULA Y
ENV MSSQL_PID Express
# Expose port 1433 in case accesing from other container
EXPOSE 1433
# Run Microsoft SQl Server and initialization script (at the same time)
# Note: If you want to start MsSQL only (without initialization script) you can comment bellow line out, CMD entry from base image will be taken
CMD /bin/bash ./entrypoint.sh
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "/opt/mssql-tools/bin/sqlcmd", "-S", "localhost", "-U", "sa" ,"-P", "CorrectHorseBatteryStapleFor$", "-d", "dwh-demo", "-Q", "SET NOCOUNT ON SELECT \"YAY WE ARE UP\"" ]
This did not solve the problem alone, since the docker-compose up --build
command builds both, database and API before running the containers as explained above @Hans Kilian. Therefore, and with my limited maven knowledge I just added an option to the maven install command to skip tests and this did the trick.
Dockerfile spring-boot:
FROM maven:3.8.7-eclipse-temurin-19
WORKDIR /mdm-web-api COPY . . RUN mvn clean install -DskipTests
CMD mvn spring-boot:run
That's still weird, because for unit testing I'm using an embedded h2 database with MS SQL mode, but I guess the mvn install tries to connect to the configured database.
As a final comment, you may want to have a elaborated CI pipeline where tests run in a stage and image build in another one.