Home > Software design >  depends_on docker-compose condition not working - Spring boot API and MS SQL Server
depends_on docker-compose condition not working - Spring boot API and MS SQL Server

Time:01-21

I am capsuling a Spring-boot API that works together with an MS SQL Server using docker compose.

This is how the docker-compose.ymlfile 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:

Terminal in VSCODE

As you may noticed mvn clean installis 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.

  • Related