Compose
  • Deploy multiple containers on a single host
  • A YAML file defining version (DEPRECATED), services (REQUIRED), networks, volumes, configs and secrets
  • Default path is compose.yaml (or compose.yml, docker-compose.yaml, docker-compose.yml) in working directory
  • Build and Compose
  • 1. Build an image from local
  • 2. Compose containers
  • version: "3.9" # version of Docker Compose
    services:
      webapp: # container
        image: lchenlangley/count # image name [domain/image:tag]
        build: . # search local, if image does not exist, build image from local
        ports:
          - 8000:5000 # host port : container port
        environment:
          - MYREDIS_HOST=redisserver # environment variable
      redisserver: # container
        image: redis:alpine # if image exist in local, use the local image
    # a bridge network is created by default
            
    Fetch and Compose
  • 1. Fetch images from repositories
  • 2. Compose containers
  • version: "3.9"
    services:
      webapp:
        image: lchenlangley/count # fetch image from Docker Hub
        ports:
          - 8000:5000
        environment:
          - MYREDIS_HOST=redisserver
      redisserver:
        image: "redis:alpine" # fetch image from Docker Hub
            
    Multiple Compose Files
    # compose_1.yaml
    version: "3.9"
    services:
      webapp: # container
        image: lchenlangley/count
        #build: .
        volumes:
          - type: volume
            source: voltemp
            target: /code
    volumes:
      voltemp:
            
    # compose_2.yaml
    version: "3.9"
    services:
      webapp: # container
        ports:
          - target: 5000
            published: 8000
            protocol: tcp
            mode: host
        environment:
          MYREDIS_HOST: redisserver
      redisserver: # container
        image: redis:alpine
            
    docker-compose -f compose_1.yaml -f compose_2.yaml up
    docker-compose -f compose_1.yaml -f compose_2.yaml down
            
    Configuration Extension
  • re-use a common configuration
  • # compose.yaml
    version: "3.9"
    services:
      webapp: # container
        extends:
          file: common.yaml
          service: template
        image: lchenlangley/count
        #build: .
        volumes:
          - type: volume
            source: voltemp
            target: /code
      redisserver: # container
        image: redis:alpine
    volumes:
      voltemp:
            
    # common.yaml, contains a common configuration
    version: "3.9"
    services:
      template: # container
        ports:
          - target: 5000
            published: 8000
            protocol: tcp
            mode: host
        environment:
          MYREDIS_HOST: redisserver
            
    docker-compose up
    docker-compose down
            
    Services
  • Abstract concept implementing docker run
  • image
  • image: redis # [domainName/imageName:tag]
            
  • ports
  • ports:
          - 8000:5000 # host_port:container_port
            
  • build
  • services:
      webapp:
        image: awesome/webapp # image name
        build: . # path for source code and Dockerfile
            
    services:
      webapp:
        image: awesome/database # image name
        build:
            context: . # source code path
            dockerfile: ../Dockerfile # Dockerfile path
            
    services:
      webapp:
        build: . # source code path
            
  • environment
  • services:
      webapp:
        ...
        environment:
           MYREDIS_HOST: redisserver
            
    services:
      webapp:
        ...
        environment:
           - MYREDIS_HOST=redisserver
            
  • networks
  • services:
      webapp:
        ...
        networks:
         - frontend-network
         - backend-network
            
  • restart
  • restart: always
            
  • env_file
  • # .env, default environment file
    MYREDIS_HOST=redisserver
    
    # compose.yml
    services:
      webapp:
        ...
        environment:
           - MYREDIS_HOST=${MYREDIS_HOST}
            
    # env/webapp.env
    MYREDIS_HOST=redisserver
    
    # compose.yml
    services:
      webapp:
        ...
        environment:
           - MYREDIS_HOST=${MYREDIS_HOST}
    
    # compose
    docker-compose --env-file env/webapp.env up
    
    # turn off compose
    docker-compose --env-file env/webapp.env down
            
  • profiles and depends_on
  • services:
      foo:
        image: foo
      bar:
        image: bar
        profiles:
          - test
      baz:
        image: baz
        depends_on:
          - bar
        profiles:
          - test
      zot:
        image: zot
        depends_on:
          - bar
        profiles:
          - debug
            
    # enable test (bar, baz) and foo
    docker-compose --profile test up
    
    # invalid, profile debug (zot) is enabled, but zot depends on bar which is in the profile test
    docker-compose --profile debug up
    
    # enable test(bar, baz), debug(zot) and foo
    docker-compose --profile debug --profile test up
    
    # enable bar, profile test is active, but baz is not enabled
    docker-compose up bar
    
    # enable baz, profile test is active, bar is enabled by depends_on constraint
    docker-compose up baz
    
    # enable zot and test, bar is enabled by depends_on constraint
    docker-compose --profile test up zot
    		
  • command
  • command: python app.py
            
  • expose
  • expose:
      - "5000"
            
  • container_name
  • container_name: redis-container
            
  • hostname
  • hostname: redis-host
            
    Networks
  • Capability abstraction to establish an IP route between containers, analogous to docker network create
  • By default, docker-compose sets up a single network, containers join the network for communication
  • version: "3.9"
    services:
      webapp: # container
        image: lchenlangley/count
        #build: .
        volumes:
          - type: volume
            source: voltemp
            target: /code
        ports:
          - target: 5000
            published: 8000
            protocol: tcp
            mode: host
        environment:
          MYREDIS_HOST: ${MYREDIS_HOST}
        networks:
          - app-net
      redisserver: # container
        image: redis:alpine
        networks:
          - app-net
    volumes:
      voltemp:
    networks:
      app-net: # create a network named [directory]_app-net
    		
    Volumes
  • Analogous to docker volume create
  • Volume
  • # Short Syntax
    services:
      app:
        # ...
        volumes: # volumes for a particular service
          - [source]:[target]:[mode]
            # source, named volume, defined in the top level "volumes"
            # target, an absolute path in container
            # mode, read-only or read-write
    
    volumes: # define volumes
    		
    version: "3.9"
    services:
      webapp:
        image: lchenlangley/count
        build: .
        ports:
          - 8000:5000
        volumes:
          - voltemp:/code
        environment:
          - MYREDIS_HOST=redisserver
          - FLASK_ENV=development
        depends_on:
          - redisserver
      redisserver:
        image: "redis:alpine"
    volumes:
      voltemp: # volume name length should greater than five
            
    # Syntax
    services:
      app:
        # ...
        volumes: # volumes for a particular service
          - type: volume
            source: [volume_name]
            target: [path]
    
    volumes: # define volumes
            
    version: "3.9"
    services:
      webapp:
        image: lchenlangley/count
        build: .
        ports:
          - 8000:5000
        volumes:
          - type: volume
            source: voltemp
            target: /code
        environment:
          - MYREDIS_HOST=redisserver
          - FLASK_ENV=development
        depends_on:
          - redisserver
      redisserver:
        image: "redis:alpine"
    volumes:
      voltemp:
            
  • Bind Mount
  • # Short Syntax
    services:
      app:
        # ...
        volumes: # volumes for a particular service
          - [source]:[target]:[mode]
            # source, a path (absolute or relate)
            # target, an absolute path in container
            # mode, read-only or read-write
            
    version: "3.9"
    services:
      webapp:
        image: lchenlangley/count
        build: .
        ports:
          - 8000:5000
        volumes:
          - .:/code
        environment:
          - MYREDIS_HOST=redisserver
          - FLASK_ENV=development
        depends_on:
          - redisserver
      redisserver:
        image: "redis:alpine"
            
    # Syntax
    services:
      app:
        # ...
        volumes: # volumes for a particular service
          - type: bind
            source: [path]
            target: [path]
            
    version: "3.9"
    services:
      webapp:
        image: lchenlangley/count
        build: .
        ports:
          - 8000:5000
        volumes:
          - type: bind
            source: .
            target: /code
        environment:
          - MYREDIS_HOST=redisserver
          - FLASK_ENV=development
        depends_on:
          - redisserver
      redisserver:
        image: "redis:alpine"
            
    Configs
  • Grant access to configs on a per-service basis
  • 		
    Secret
  • Grant access to secrets on a per-service basis
  • 		
    Example

  • 2 services, backed by Docker images: webapp and database
  • 1 secret (HTTPS certificate), injected into the frontend
  • 1 configuration (HTTP), injected into the frontend
  • 1 persistent volume, attached to the backend
  • 2 networks: front-tier and back-tier
  • services:
      frontend:
        image: awesome/webapp
        ports:
          - "443:8043"
        networks:
          - front-tier
          - back-tier
        configs:
          - httpd-config
        secrets:
          - server-certificate
    
      backend:
        image: awesome/database
        volumes:
          - db-data:/etc/data
        networks:
          - back-tier
    
    volumes:
      db-data:
        driver: flocker
        driver_opts:
          size: "10GiB"
    
    configs:
      httpd-config:
        external: true
    
    secrets:
      server-certificate:
        external: true
    
    networks:
      # The presence of these objects is sufficient to define them
      front-tier: {}
      back-tier: {}
    		
    docker-compose
    # get command help
    docker-compose --help
    
    # start services
    # --build, build images from source code instead of loading built images before starting containers
    # -d, detached
    # docker-compose up [service list]
    
    docker-compose up # start all services
    docker-compose up webapp # start service webapp
    docker-compose -f [composeFileName] up # specify compose file
    
    # turn off and remove services
    docker-compose down
    
    # build images in compose file
    docker-compose build
    
    # list images of the current compose
    docker-compose images
    
    # list service of the current compose
    docker-compose ps
    
    # stop services of the current compose
    docker-compose stop [serviceName] # stop a specific service
    docker-compose stop # stop services in the current compose
    
    # start services
    docker-compose start [serviceName] # start a specific service
    docker-compose start # start services in the current compose
    
    # one-off command
    # docker-compose run [serviceName] [command]
    docker-compose run webapp env
    
    # view the compose file
    docker-compose config
    		
    Reference
  • Docker Compose Tutorial: advanced Docker made simple
  • docker-compose CLI
  • Compose File Reference
  • The Compose Specification
  • User Guide