Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can one make a Docker Compose service build depend on another service?

Here's what I mean. How can I write a docker-compose.yaml file so that when one of the services is "built", it first runs another service?

I'll try to be even more specific. I'm trying to build a Java application using the database library JOOQ, which wants to connect to a database at build time in order to generate Java classes that correspond to database tables. I would like some combination of Dockerfile(s) and docker-compose.yaml file, such that the following happens in roughly the following order.

  1. A "database" service is started (in my case, using a postgres image).
  2. The database is initialized with SQL scripts in my development repo.
  3. The build phase of my "web" service runs, which uses a Dockerfile in the same repo, which invokes the Gradle build, which tells JOOQ to connect to the database service started in step 1. This generates Java files, compiles them, and does everything else for building the container.
  4. The "web" service is started, connecting to the database service started in step 1.

Here's the docker-compose.yaml file I'm working with

version: '3.7' 
services:
  postgres:
    image: postgres:10.5-alpine
    restart: always
    ports:
      - "6432:5432"
    environment:
      POSTGRES_DB: flashtools
      POSTGRES_USER: flashtools
      POSTGRES_PASSWORD: flashtools
    volumes:
      - ./src/main/scripts/01_init.sql:/docker-entrypoint- 
initdb.d/01_init.sql
  web:
    build: .
    network_mode: host
    depends_on:
      - postgres
    ports:
      - "8080:8080"

I'll flesh this out with more details, but hopefully what I'm asking is pretty straightforward.

like image 879
David Ventimiglia Avatar asked Oct 13 '18 00:10

David Ventimiglia


2 Answers

I had the same issue and solved it in a hacky way...

In my case the web service is Haskell, which builds with the stack build command, then executes with the stack exec APP_NAME. There's also a shorthand like stack build --exec APP_NAME.

Also there is an option to build only the dependencies with stack build --only-dependencies, so the Docker build cache them.

So I changed my Dockerfile like this:

COPY ./package.yaml /app/package.yaml        # Copying the package file
RUN stack build --only-dependencies          # Build dependencies only

COPY . /app                                  # Copying the rest of the files

EXPOSE 3000

CMD ["stack", "build", "--exec", "APP_NAME"] # Build the application itself

so the final build runs only when the service is started.

I don't know Java enough, but I guess there must be a similar option there too.

Also some hacking on the depends_on part is also needed: https://docs.docker.com/compose/startup-order/

like image 173
Gergo Avatar answered Oct 02 '22 03:10

Gergo


This should now (technically) be possible:

since 2021-04-06 in version 1.29.0 (release note link)

Using the service_completed_successfully condition (which is a "Long Form" depends_on option):

Long syntax:

The long form syntax enables the configuration of additional fields that can't be expressed in the short form.

  • condition: condition under which dependency is considered satisfied
    • service_completed_successfully: specifies that a dependency is expected to run to successful completion before starting a dependent service.

(source - emphasis added)

Long Syntax Example:

depends_on:
  service_name:
    condition: service_completed_successfully

Update your docker-compose.yml file:

You'll need to make the following changes:

  • bump your compose version up to 3.8
  • add the new service_completed_successfully condition to your depends_on
version: "3.8"
services:
  postgres:
    image: postgres:10.5-alpine
    restart: always
    ports:
      - "6432:5432"
    environment:
      POSTGRES_DB: flashtools
      POSTGRES_USER: flashtools
      POSTGRES_PASSWORD: flashtools
    volumes:
      - ./src/main/scripts/01_init.sql:/docker-entrypoint-initdb.d/01_init.sql
  web:
    build: .
    network_mode: host
    depends_on:
      postgres:
        condition: service_completed_successfully
    ports:
      - "8080:8080"

You might also need to use a multi-stage Dockerfile:

(multi-stage build info)

Multi-stage builds are typically used to slim down your final Docker image, but it can also be used to run scripts and create "artifacts" needed before finishing your build.

It's hard to suggest this without seeing your exact Dockerfile, but I thought it was worth mentioning that you may need to also use this.

like image 45
skplunkerin Avatar answered Oct 02 '22 02:10

skplunkerin