Home > Enterprise >  Docker compose: run several containers that only differ in their command
Docker compose: run several containers that only differ in their command

Time:11-03

I have a docker-compose.override.yml file that launches 10 different services that only differ in the name of the container and the command they run. They also need various envrionments variables, several volumes etc.

The file looks like this, but it has 10 section and each section actually has more configuration.

I don't like all that repetition.

Is there a way to move all the common configuration part to some other place and let all the services use this information?

Maybe some other solution to this?

version: "3"
services:
  service-1:
    image: my-image
    tty: true
    environment:
      - APP_ENVIRONMENT=dev
    working_dir: /source
    volumes:
      - .:/source:ro
    command: run_services_1

  service-2:
    image: my-image
    tty: true
    environment:
      - APP_ENVIRONMENT=dev
    working_dir: /source
    volumes:
      - .:/source:ro
    command: run_services_2

CodePudding user response:

Reusing blocks of code from a docker-compose file can be easily achieved by using the YAML anchors and aliases feature.

For example:

services:
  service1: &default
    image: alpine
    environment:
      APP_ENVIRONMENT: dev
    command: env

  service2:
    <<: *default
    command: echo some other command

  service3:
    <<: *default
    environment:
      APP_ENVIRONMENT: prod

For more advanced cases, you can also employ these two tricks:

  1. Mark only a part of the configuration block as belongs to &default or other marker.
  2. Define multiple markers, and create a sort of inheritance between sections.

Here is a simple example of both features:

services:
  bash:
    build: .

    # Only the below will be used for anyone inheriting from &default
    <<: &default
      image: me/my-image
      environment:
        APPENV: dev

  web: &web
    # Inherit from default
    <<: *default
    command: env

  prod:
    # Inherit from web
    <<: *web
    environment:
      APPENV: production

You can try this alongside a simple Dockerfile just containing FROM alpine or similar.

Finally, it is worth mentioning that docker-compose supports hidden configuration blocks, which they call Extension Fields. Anything that starts with x- is ignored, so this can be used to define templates, for example:

x-service: &service
  depends_on: [nginx]
  restart: always

services:
  service1:
    <<: *service
    command: ...

  service2:
    <<: *service
    command: ...
    image: ...

And as a side-tip: Remove version: 3 from your files, unless you have a specific reason. New docker compose no longer needs it.

CodePudding user response:

There is a way called yml templating, which looks like this:

version: "3"
services:
  service-1: &service_template
    image: my-image
    tty: true
    environment:
      - APP_ENVIRONMENT=dev
    working_dir: /source
    volumes:
      - .:/source:ro
    command: run_services_1

  service-2:
    <<: *service_template
    command: run_services_2

Everything you want to override just write it in the service where you are using the defined template, like the 'command' parameter in the example.

For more examples visit https://medium.com/@kinghuang/docker-compose-anchors-aliases-extensions-a1e4105d70bd

CodePudding user response:

Many of the details you show in the Compose file don't actually need to be runtime configuration; you can set them in the Dockerfile.

WORKDIR /source            # replaces Compose working_dir:
COPY . ./                  # application built into the image, so no Compose volumes:
# ENV APP_ENVIRONMENT=dev  # if this is a build-time parameter
CMD run_default_command    # good practice 

You don't usually need to specify tty: true (one prominent create-react-app bug requires stdin_open: true). Your Compose file doesn't specify these, but you also don't usually need to provide network: or container_name: options, since Compose provides a reasonable network setup by default.

So, if you can specify the environment variables in the image, the best case looks like:

version: '3.8'
services:
  service-1:
    image: my-image
    command: run_service_1

  service-2:
    image: my-image
    command: run_service_2
    # ports: [...]

In practice you probably do need some runtime configuration and it may need to be repeated across services. A common trick is to use YAML alias nodes in combination with Compose extension fields to have a block of environment variable definitions and include them in other services:

version: '3.8'
x-environment: &environment
  - APP_ENVIRONMENT=dev
services:
  service-1:
    image: my-image
    environment: *environment
    command: run_service_1

Compose also supports YAML's merge type, if environment: is a mapping, so it's possible to extend this list:

x-environment: &environment
  APP_ENVIRONMENT: dev
services:
  service-1:
    image: my-image
    command: run_service_1
    environment:
      << : *environment
      EXTERNAL_SERVICE_URL: http://external.example.com

You can use this same technique to provide arbitrary additional Compose settings, but relying on Compose's defaults and settings built into images is frequently easier to write and read.

  • Related