skip to Main Content

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?

2

Answers


  1. Chosen as BEST ANSWER

    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.


  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search