Docker Compose

Docker Compose

Overview

  • Docker Compose is a tool for defining and running multi-container Docker applications
  • It uses a YAML file to configure your application’s services
  • The command to run Docker Compose:
    • docker compose - subcommand compose (since Compose V2)
    • docker-compose - a separate program for Compose V1

Use cases

  • create and run an application in an isolated environment
  • create and destroy isolated testing environments for your test suite

Using Docker Compose

Typical steps to use Docker Compose.

  1. Define your app’s environment with a Dockerfile
  2. Define the services that make up your app in
    • docker-compose.yml or
    • compose.yml (since Docker Engine version 20.10)
    • services will run together in an isolated environment
  3. Run docker compose up to start and run your entire app
  4. Run docker compose down to stop your app

Docker Compose file

Docker Compose file: Overview

Default Docker Compose file is

  • docker-compose.yml or
  • compose.yml (since Docker Engine version 20.10)

The file provides:

  • a way to document the application's service
  • configuration of application’s service dependencies
    • e.g. databases, queues, caches, web service APIs, etc.

Docker Compose file: Format

The file follows YAML file format.

The file contains one or more top-level elements.

Docker Compose file: Format (2)

Top-level elements:

  • version - obsoleted
  • name - application name
  • services - (required) one or more services in the application
  • volumes - any volume used by the application
  • networks - any network used by the application
  • configs - configurations used by services in the application
  • secrets - any sensitive data used in the application

YAML: Quick Start

  • YAML does not allow the use of tabs
  • YAML is CASE sensitive
  • YAML is a superset of JSON
  • Ther MUST be space between the element parts
  • End YAML file with the .yaml or .yml extension

YAML: Collections

  • block collections use indentation for scope
  • begin each entry on its own line
  • sequences use a dash and space ("- ") for each item
  • mappings use a colon and space (": ") for each key/value pair
  • comments begin with an octothorpe
    • "#" also called a "hash", "sharp", "pound" or "number sign"

YAML: Sequence

- Mark McGwire
- Sammy Sosa
- Ken Griffey

YAML: Mapping

hr:  65    # Home runs
avg: 0.278 # Batting average
rbi: 147   # Runs Batted In

YAML: Mapping to Sequences

american:
  - Boston Red Sox
  - Detroit Tigers
  - New York Yankees
national:
  - New York Mets
  - Chicago Cubs
  - Atlanta Braves

or

american: [Boston Red Sox, Detroit Tigers, New York Yankees]
national: [New York Mets, Chicago Cubs, Atlanta Braves]

YAML: Sequence of Mappings

-
  name: Mark McGwire
  hr:   65
  avg:  0.278
-
  name: Sammy Sosa
  hr:   63
  avg:  0.288

or

- name: Mark McGwire
  hr:   65
  avg:  0.278
- name: Sammy Sosa
  hr:   63
  avg:  0.288

YAML: Mapping of Mappings

Mark McGwire:
  hr:   65
  avg:  0.278
Sammy Sosa:
  hr:   63
  avg:  0.288

or

Mark McGwire: {hr: 65, avg: 0.278}
Sammy Sosa: {hr: 63, avg: 0.288}

Example 1

Minimum contents of docker-compose.yml:

  • MUST declare services top-level element
services:
  my_app:
    image: nginx:alpine
    ports:
      - "8001:80"

Note the indentation, typically 2 spaces.

Example 1: Explaination

  • my_app - your custom service name

  • within a service, there can be one or more keys that configure the container

Example 1: image

  • image - specify the image to start the container from

  • the format is the same for instuction FROM in Dockerfile

Format:

[<registry>/][<project>/]<image>[:<tag>|@<digest>]

Examples:

image: redis
image: redis:5
image: redis@sha256:0ed5d5928d4737458944eb604cc8509e245c3e19d02ad83935398bc4b991aac7
image: library/redis
image: docker.io/library/redis
image: my_private.registry:5000/redis

Example 1: ports

  • ports - specify container ports to host

Format: short syntax

[HOST:]CONTAINER[/PROTOCOL]

where:

  • HOST is [IP:](port | range)
  • CONTAINER is port | range
  • PROTOCOL is tcp, udp or platform-specific protocol names

HOST:CONTAINER should be specified as a (quoted) string

Example 1: ports (2)

Examples:

ports:
  - "3000"
  - "3000-3005"
  - "8000:8000"
  - "9090-9091:8080-8081"
  - "49100:22"
  - "127.0.0.1:8001:8001"
  - "127.0.0.1:5000-5010:5000-5010"
  - "6060:6060/udp"

Example 1: ports (3)

Format: long syntax

  • target: the container port
  • published: the publicly exposed port, cn be a range
  • host_ip: the host IP mapping, unspecified means all network interfaces (0.0.0.0)
  • protocol: the port protocol (tcp or udp), unspecified means any protocol
  • mode:
    • host for publishing a host port on each node
    • ingress for a port to be load balanced

Example 1: ports (4)

Examples:

ports:
  - target: 80
    host_ip: 127.0.0.1
    published: 8080
    protocol: tcp
    mode: host

  - target: 80
    host_ip: 127.0.0.1
    published: 8000-9000
    protocol: tcp
    mode: host

Example 1: Run

Run all services in the application:

docker compose up --detach
[+] Running 2/2
 ⠿ Network ex1_default     Created                                                                 0.1s
 ⠿ Container ex1-my_app-1  Started                                                                 0.9s
  • Note:

    • by default, directory containing the file docker-compose.yml will be used to scope all resources for the application
    • container name is created from directory and service name
    • default network (driver is bridge) is created from directory name
  • Explaination

    • compose - management command for Compose API
    • up - subcommand to create and start containers
    • --detach - run containers in the background

Example 1: List containers

List containers created by Compose.

docker compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
ex1-my_app-1        "/docker-entrypoint.…"   my_app              running             0.0.0.0:8001->80/tcp

Example 1: Down

Stop and remove all services in the application.

docker compose down
[+] Running 2/2
 - Container ex1-my_app-1  Removed                                                                 0.7s
 - Network ex1_default     Removed                                                                 0.2s
  • Explaination

    • down - subcommand to stop and remove containers, networks

Example 2

Simple docker-compose.yml using local Dockerfile

services:
  my_app:
    build: ./
    ports:
      - "8002:80"

Example 2: build

  • build - specify the build configuration for creating container image

Format: string syntax

build: path
  • path
    • specify the build context as a relative path to the Compose file’s parent directory
    • must be a directory and contain a Dockerfile

Example 2: Dockerfile

Create a simple Dockerfile with the following contents.

FROM nginx:alpine

Example 2: Run

Run all services in the application:

docker compose up --detach
[+] Building 0.2s (5/5) FINISHED
 => [internal] load build definition from Dockerfile                                               0.0s
 => => transferring dockerfile: 31B                                                                0.0s
 => [internal] load .dockerignore                                                                  0.0s
 => => transferring context: 2B                                                                    0.0s
 => [internal] load metadata for docker.io/library/nginx:alpine                                    0.0s
 => CACHED [1/1] FROM docker.io/library/nginx:alpine                                               0.0s
 => exporting to image                                                                             0.0s
 => => exporting layers                                                                            0.0s
 => => writing image sha256:c1eb88974f71d9e4831be1314fc28d633075f0b644c256664e36e02d8578f030       0.0s
 => => naming to docker.io/library/ex2-my_app                                                      0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
[+] Running 2/2
 - Network ex2_default     Created                                                                 0.0s
 - Container ex2-my_app-1  Started                                                                 0.7s

Example 2: List containers

docker compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
ex2-my_app-1        "/docker-entrypoint.…"   my_app              running             0.0.0.0:8002->80/tcp

Example 2: Down

Stop and remove all services in the application.

docker compose down
[+] Running 2/2
 - Container ex2-my_app-1  Removed                                                                 0.7s
 - Network ex2_default     Removed                                                                 0.3s

Example 3

Simple docker-compose.yml using local Dockerfile

services:
  my_app:
    build:
      context: ./
      dockerfile: Dockerfile
    ports:
      - "8003:80"

Example 3: build

Format:

build:
  context: path
  dockerfile: path
  args:
    VAR: value
build:
  context: path
  dockerfile: path
  args:
    - VAR=value
  • context - (required) a path to a directory containing a Dockerfile, or a url to a git repository
  • dockerfile - relative path from build context to Dockerfile
  • args - a map or a list of variable-value for build step

Example 3: Dockerfile

Create a simple Dockerfile with the following contents.

FROM nginx:alpine

Example 3: Run

Run all services in the application:

docker compose up --detach
[+] Building 0.2s (5/5) FINISHED
 => [internal] load build definition from Dockerfile                                               0.1s
 => => transferring dockerfile: 56B                                                                0.0s
 => [internal] load .dockerignore                                                                  0.1s
 => => transferring context: 2B                                                                    0.0s
 => [internal] load metadata for docker.io/library/nginx:alpine                                    0.0s
 => CACHED [1/1] FROM docker.io/library/nginx:alpine                                               0.0s
 => exporting to image                                                                             0.0s
 => => exporting layers                                                                            0.0s
 => => writing image sha256:1b4590ffd7d76c57e3337056ef46dfb461fc9e5c6e579c82d54a3bbc6d25204c       0.0s
 => => naming to docker.io/library/ex3-my_app                                                      0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
[+] Running 2/2
 - Network ex3_default     Created                                                                 0.0s
 - Container ex3-my_app-1  Started                                                                 0.8s

Example 3: List containers

docker compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
ex3-my_app-1        "/docker-entrypoint.…"   my_app              running             0.0.0.0:8003->80/tcp

Example 3: Down

Stop and remove all services in the application.

docker compose down
[+] Running 2/2
 - Container ex3-my_app-1  Removed                                                                 0.8s
 - Network ex3_default     Removed                                                                 0.3s

Example 4

With multiple services (containers)

services:
  app_a:
    build:
      context: ./my_app_a
      dockerfile: Dockerfile_a
    ports:
      - "8004:80"

  app_b:
    build:
      context: ./my_app_b
      dockerfile: Dockerfile_b
    ports:
      - "9004:80"

Example 4: build

In subdirectory my_app_a, create Dockerfile_a

FROM nginx:alpine

In subdirectory my_app_b, create Dockerfile_b

FROM httpd:alpine

Directory structure for this example:

.
├── my_app_a
│   └── Dockerfile_a
├── my_app_b
│   └── Dockerfile_b
└── docker-compose.yml

Example 4: Build images

Build any image if not already exists.

docker compose build
[+] Building 1.3s (9/9) FINISHED
 => [ex4-app_b internal] load build definition from Dockerfile_b                                   0.0s
 => => transferring dockerfile: 33B                                                                0.0s
 => [ex4-app_a internal] load build definition from Dockerfile_a                                   0.1s
 => => transferring dockerfile: 33B                                                                0.0s
 => [ex4-app_b internal] load .dockerignore                                                        0.1s
 => => transferring context: 2B                                                                    0.0s
 => [ex4-app_a internal] load .dockerignore                                                        0.1s
 => => transferring context: 2B                                                                    0.0s
 => [ex4-app_b internal] load metadata for docker.io/library/httpd:alpine                          1.1s
 => [ex4-app_a internal] load metadata for docker.io/library/nginx:alpine                          0.0s
 => CACHED [ex4-app_a 1/1] FROM docker.io/library/nginx:alpine                                     0.0s
 => [ex4-app_b] exporting to image                                                                 0.1s
 => => exporting layers                                                                            0.0s
 => => writing image sha256:4111470cf2a4ff6157bdcdb63bca685fb2116f0ce1e72f3538bad259e5dcbdf8       0.0s
 => => naming to docker.io/library/ex4-app_a                                                       0.0s
 => => writing image sha256:1a40790611892a64c25f1aad1af0935379a16f269d155e9d38ff555fed976d28       0.0s
 => => naming to docker.io/library/ex4-app_b                                                       0.0s
 => CACHED [ex4-app_b 1/1] FROM docker.io/library/httpd:alpine@sha256:4658c554fe215c8a17d57adbd9f8595e4922abda40d  0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Example 4: Run

Run all services in the application:

docker compose up --detach
[+] Running 3/3
 - Network ex4_default    Created                                                                  0.0s
 - Container ex4-app_a-1  Started                                                                  2.6s
 - Container ex4-app_b-1  Started                                                                  2.6s

Example 4: List containers

docker compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
ex4-app_a-1         "/docker-entrypoint.…"   app_a               running             0.0.0.0:8004->80/tcp
ex4-app_b-1         "httpd-foreground"       app_b               running             0.0.0.0:9004->80/tcp

Example 4: Down

Stop and remove all services in the application.

docker compose down
[+] Running 3/3
 - Container ex4-app_b-1  Removed                                                                  1.5s
 - Container ex4-app_a-1  Removed                                                                  0.7s
 - Network ex4_default    Removed                                                                  0.2s

Example 5

phpAdmin with mysql database.

services:
  db:
    image: mysql:8.0.39-debian
    volumes:
      - db-mysql:/var/lib/mysql
    networks:
      - db-net
    env_file:
      - default.env
  pma:
    image: phpmyadmin:5.2.1
    networks:
      - db-net
    ports:
      - "8005:80"
    depends_on:
      - db

volumes:
  db-mysql:

networks:
  db-net:

Example 5: default.env

MYSQL_ROOT_PASSWORD=secret
# MYSQL_DATABASE=db_test
# MYSQL_USER=db_user
# MYSQL_PASSWORD=db_pw

# phpmyadmin
PMA_ARBITRARY=1
# PMA_HOST=db
# PMA_USER=root
# PMA_PASSWORD=secret

Example 5: Run

Run all services in the application:

docker compose up --detach
[+] Running 4/4
 - Network ex5_db-net     Created                                                                  0.1s
 - Volume "ex5_db-mysql"  Created                                                                  0.0s
 - Container ex5-db-1     Started                                                                  1.1s
 - Container ex5-pma-1    Started                                                                  1.6s

Example 5: List containers

docker compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
ex5-db-1            "docker-entrypoint.s…"   db                  running             3306/tcp, 33060/tcp
ex5-pma-1           "/docker-entrypoint.…"   pma                 running             0.0.0.0:8005->80/tcp

Example 5: List volumes

docker volume ls
DRIVER    VOLUME NAME
local     ex5_db-mysql

Example 5: List networks

docker network ls
NETWORK ID     NAME              DRIVER    SCOPE
60bf4ba277f2   bridge            bridge    local
12b6d75e9106   ex5_db-net        bridge    local
a8c4ebbecec3   host              host      local
8a9fe937f5ac   none              null      local

Example 5: Down

Stop and remove all services in the application.

docker compose down
[+] Running 3/3
 - Container ex5-pma-1  Removed                                                                    0.5s
 - Container ex5-db-1   Removed                                                                    1.9s
 - Network ex5_db-net   Removed                                                                    0.3s

Example 6

adminer with mariadb database.

services:
  db:
    image: mariadb:11.4.3
    volumes:
      - db-mariadb:/var/lib/mysql
    networks:
      - db-net
    env_file:
      - mariadb.env
  pma:
    image: adminer:4.8.1
    networks:
      - db-net
    ports:
      - "8006:8080"
    depends_on:
      - db

volumes:
  db-mariadb:

networks:
  db-net:

Example 5: default.env

MARIADB_ROOT_PASSWORD=secret
# MARIADB_DATABASE=db_test
# MARIADB_USER=db_user
# MARIADB_PASSWORD=db_pw

# phpmyadmin
PMA_ARBITRARY=1
# PMA_HOST=db
# PMA_USER=root
# PMA_PASSWORD=secret

Example 6: Run

Run all services in the application:

docker compose up --detach
[+] Running 4/4
 - Network ex6_db-net       Created                                                                0.1s
 - Volume "ex6_db-mariadb"  Created                                                                0.0s
 - Container ex6-db-1       Started                                                                1.4s
 - Container ex6-pma-1      Started                                                                2.2s

Example 6: List containers

docker compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
ex6-db-1            "docker-entrypoint.s…"   db                  running             3306/tcp
ex6-pma-1           "entrypoint.sh docke…"   pma                 running             0.0.0.0:8006->8080/tcp

Example 6: List volumes

docker volume ls
DRIVER    VOLUME NAME
local     ex6_db-mariadb
local     ex6_db-mysql

Example 56 List networks

docker network ls
NETWORK ID     NAME              DRIVER    SCOPE
60bf4ba277f2   bridge            bridge    local
be8e5de3ef37   ex6_db-net        bridge    local
a8c4ebbecec3   host              host      local
8a9fe937f5ac   none              null      local

Example 6: Down

Stop and remove all services in the application.

docker compose down
[+] Running 3/3
 - Container ex6-pma-1  Removed                                                                    0.5s
 - Container ex6-db-1   Removed                                                                    1.0s
 - Network ex6_db-net   Removed                                                                    0.3s

References