Docker Compose: Difference between revisions

From Leo's Notes
This page was last edited on 23 July 2021, at 05:06.
m Added navbox and categories
No edit summary
Line 3: Line 3:
Unlike Docker Swarm or Kubernetes, it does not provide orchestration or any infrastructure level support around your application. However, it does simplify defining services that are intended to run on a single-node Docker node. Rather than starting Docker containers from the command line, where volumes and network linking can get complicated, the entire application can be laid out in a yaml file and the entire application can be deployed using the <code>docker-compose</code> command.
Unlike Docker Swarm or Kubernetes, it does not provide orchestration or any infrastructure level support around your application. However, it does simplify defining services that are intended to run on a single-node Docker node. Rather than starting Docker containers from the command line, where volumes and network linking can get complicated, the entire application can be laid out in a yaml file and the entire application can be deployed using the <code>docker-compose</code> command.


== Installation ==
==Installation==
To use Docker Compose, download the latest binary and stick it in your PATH. Get the binary from:
To use Docker Compose, download the latest binary and stick it in your PATH. Get the binary from:


* https://docs.docker.com/compose/install/
*https://docs.docker.com/compose/install/


== Usage Example ==
==Usage Example==
For instance, if a LAMP stack is required, a docker-compose configuration file would define each of the components making up the LAMP stack (Apache+PHP, MySQL, optionally a Load Balancer). Rather than manually creating/removing each of the Docker containers and networks manually, the entire service can be brought up or down with one command.
For instance, if a LAMP stack is required, a docker-compose configuration file would define each of the components making up the LAMP stack (Apache+PHP, MySQL, optionally a Load Balancer). Rather than manually creating/removing each of the Docker containers and networks manually, the entire service can be brought up or down with one command.


Line 113: Line 113:
}}
}}
===Networks===
===Networks===
Provide a list of networks that the container should be connected to. In the example above, 'traefik' (the load balancer/proxy) is connected to the backend network (used to communicate to the web server), as well as the internet with a defined IP address.
A service may connect to one or more networks. For example:
 
{{Highlight
| code = services:
  nodered:
    image: nodered/node-red-docker:latest
    labels:
      - traefik.enable=true
      - traefik.docker.network=traefik
    networks:
      - traefik
      - mqtt-net
    expose:
      - "1880"
    restart: always
 
  broker:
    image: eclipse-mosquitto:1.6
    networks:
      - mqtt-net
    expose:
      - "1883"
    ports:
      - "10.1.2.200:1883:1883/tcp"
    restart: always
 
networks:
  mqtt-net:
  traefik:
    external:
      name: traefik
| lang = terminal
}}
 
Each of the networks listed under nodered and broker must be defined in the network section in the <code>docker-compose.yml</code> file. Typically, you would want to associate a network with an actual docker network so that there is connectivity between other container stacks. In the example above, the mqtt-net network is private to only this container stack (and we don't really care what IPs they get, so long as they see each other) while the traefik network is connected to some external docker network which traefik (in another container stack) is connected to.
 
For more complex use cases, networks can be created with a different driver such as macvlan which allows for more advanced networking. For example, the following network configs were used for my Pi-Hole setup so that the container has access to a specific IP address on the network in order to allow DHCP to work.
 
{{Highlight
| code = pihole:
    image: pihole/pihole
    restart: unless-stopped
    environment:
      - "TZ=MST7MDT,M3.2.0,M11.1.0"
      - ServerIP=10.1.0.8
    volumes:
      - '/var/volumes/pihole/etc-pihole/:/etc/pihole/'
      - '/var/volumes/pihole/etc-dnsmasq.d/:/etc/dnsmasq.d/'
    ports:
      - "10.1.0.8:53:53/tcp"
      - "10.1.0.8:53:53/udp"
      - "10.1.0.8:67:67/udp"
      - "10.1.0.8:80:80/tcp"
    cap_add:
      - NET_ADMIN
    #network_mode: "host"
    networks:
      pihole_network:
        ipv4_address: 10.1.0.8
 
 
networks:
  pihole_network:
    driver: macvlan
    driver_opts:
      parent: eth0
    ipam:
      config:
        - subnet: "10.1.0.0/22"
          gateway: "10.1.1.1"
          ip-range: "10.1.0.8/32"
| lang = text
}}


If a private network is used (eg. the backend network in the example above), it is necessary to define the subnet that network is on by specifying the ipam subnet. Ensure that this subnet does not conflict with any other subnets that are running on the same docker host.


{{Navbox Linux}}
{{Navbox Linux}}

Revision as of 05:06, 23 July 2021

Docker Compose is a program that simplifies the building of Docker applications by allowing you to define all the service components of the application in a single yaml file.

Unlike Docker Swarm or Kubernetes, it does not provide orchestration or any infrastructure level support around your application. However, it does simplify defining services that are intended to run on a single-node Docker node. Rather than starting Docker containers from the command line, where volumes and network linking can get complicated, the entire application can be laid out in a yaml file and the entire application can be deployed using the docker-compose command.

Installation

To use Docker Compose, download the latest binary and stick it in your PATH. Get the binary from:

Usage Example

For instance, if a LAMP stack is required, a docker-compose configuration file would define each of the components making up the LAMP stack (Apache+PHP, MySQL, optionally a Load Balancer). Rather than manually creating/removing each of the Docker containers and networks manually, the entire service can be brought up or down with one command.

An example docker-compose.yml file containing such a LAMP stack:

version: '3.3'

services:
  traefik:
    image: traefik:latest
    restart: always
    command: --web --docker --docker.watch --docker.exposedbydefault=false
    volumes:
      - /var/volumes/lamp/traefik/traefik.toml:/traefik.toml
      - /var/volumes/lamp/traefik/acme.json:/acme.json
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      external_net:
        ipv4_address: 1.1.1.1
      backend:

  web:
    build: web/.
    restart: always
    volumes:
      - /var/volumes/lamp/data:/export/data
      - /var/volumes/lamp/config:/export/config
    networks:
      backend:
    ports:
      - "8080:8080"
    depends_on:
      - db
    labels:
      - "traefik.enable=true"
      - "traefik.port=8080"
      - "traefik.frontend.rule=Host:hello.steamr.com"

  db:
    image: mariadb:10
    restart: always
    volumes:
      - /var/volumes/lamp/db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=xxxx
      - MYSQL_DATABASE=web
      - MYSQL_USER=web
      - MYSQL_PASSWORD=xxxx
    networks:
      - backend
        aliases:
          - mysql

networks:
  external_net:
    external: true

  backend:
    ipam:
      config:
        - subnet: 192.168.246.0/24

To bring this LAMP service up, run:

# ls
docker-compose.yml

##  the '-d' is for detached mode
# docker-compose up -d

docker-compose will then read the configuration file and set up the Docker networks and bring up the Docker containers with the required configs (image, volumes, network settings, environment variables, etc.) as defined in the file. Networking between different containers within the same service are done using dynamic linking automatically (rather than static links with hosts files) since individual containers can be recreated without bringing the entire stack down. The stack can be brought down using

# docker-compose down

If any changes are made to the docker-compose.yml file later on, the stack can be brought up to date by running docker-compose up again:

# docker-compose up -d

Networking

Some notes on networking in docker-compose:

Ports

Expose ports. Either specify both ports (HOST:CONTAINER), or just the container port (a random host port will be chosen).

  • Ports listed in docker-compose.yml will be accessible among all containers within this service.
  • These ports will be exposed to the host machine to a random port or a given port.

For example, we can start a MySQL server on port 3306. Any other container in this stack will be able to reach mysql:3306 (container-name:port). The DNS name is resolved by Docker's internal DNS service. The host machine is also able to reach the container, though its port will be randomized.

mysql:
  image: mysql:5.7
  ports:
    - "3306"

If you need MySQL exposed to the host on a specific port, such as 3306, pass in the HOST:CONTAINER port numbers. Since this binds to a specific port, be mindful of any existing ports in use either as containers or services running on the host.

mysql:
  image: mysql:5.7
  ports:
    - "3306:3306"

If the host machine has no firewall restricting access to port 3306, other machines that can reach the host machine will be able to talk on port 3306 to reach the MySQL service. If your host machine has multiple addresses, you may also bind to a specific address:

mysql:
  image: mysql:5.7
  ports:
    - "10.1.2.3:3306:3306"

Expose

Exposed ports will only be accessible to other containers within this stack. Like above, other containers can reach the MySQL server at mysql:3306 (container-name:port). Since this port is not exposed and bound to the host machine's network address, you may have multiple containers exposing the same ports without running into conflicts.

mysql:
  image: mysql:5.7
  expose:
    - "3306"

Networks

A service may connect to one or more networks. For example:

services:
  nodered:
    image: nodered/node-red-docker:latest
    labels:
      - traefik.enable=true
      - traefik.docker.network=traefik
    networks:
      - traefik
      - mqtt-net
    expose:
      - "1880"
    restart: always

  broker:
    image: eclipse-mosquitto:1.6
    networks:
      - mqtt-net
    expose:
      - "1883"
    ports:
      - "10.1.2.200:1883:1883/tcp"
    restart: always

networks:
  mqtt-net:
  traefik:
    external:
      name: traefik

Each of the networks listed under nodered and broker must be defined in the network section in the docker-compose.yml file. Typically, you would want to associate a network with an actual docker network so that there is connectivity between other container stacks. In the example above, the mqtt-net network is private to only this container stack (and we don't really care what IPs they get, so long as they see each other) while the traefik network is connected to some external docker network which traefik (in another container stack) is connected to.

For more complex use cases, networks can be created with a different driver such as macvlan which allows for more advanced networking. For example, the following network configs were used for my Pi-Hole setup so that the container has access to a specific IP address on the network in order to allow DHCP to work.

pihole:
    image: pihole/pihole
    restart: unless-stopped
    environment:
      - "TZ=MST7MDT,M3.2.0,M11.1.0"
      - ServerIP=10.1.0.8
    volumes:
      - '/var/volumes/pihole/etc-pihole/:/etc/pihole/'
      - '/var/volumes/pihole/etc-dnsmasq.d/:/etc/dnsmasq.d/'
    ports:
      - "10.1.0.8:53:53/tcp"
      - "10.1.0.8:53:53/udp"
      - "10.1.0.8:67:67/udp"
      - "10.1.0.8:80:80/tcp"
    cap_add:
      - NET_ADMIN
    #network_mode: "host"
    networks:
      pihole_network:
        ipv4_address: 10.1.0.8


networks:
  pihole_network:
    driver: macvlan
    driver_opts:
      parent: eth0
    ipam:
      config:
        - subnet: "10.1.0.0/22"
          gateway: "10.1.1.1"
          ip-range: "10.1.0.8/32"