Skip to main content

Docker Compose

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

O Docker Compose é uma ferramenta para definir e executar vários container de uma única vez. É a solução para montar um ambiente inteiro sem precisar subir containers um a um.

O Docker compose não elimina o uso do Dockerfile. O propósitos são diferentes. Dockerfile cria imagem e o compose inicia um conjunto de imagens.

Instalação do Docker Compose

https://docs.docker.com/compose/install/ Pré requisitos são o docker instalado e o docker cli.

Nada mais simples do que fazer o download do binário, dar a permissão de exeucução e colocá-lo no em um local que esteja no path.

Configura o projeto para pegar a ultima release caso o curl abaixo não esteja atualizado.

https://github.com/docker/compose/releases

sudo curl -L "https://github.com/docker/compose/releases/download/v2.6.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version

docker-compose.yml

O arquivo docker-compose.yml define as configurações dos containers que irão subir, bem como redes e volumes. É um arquivo yaml, logo é necessário conhecer a estrutura.

Os parâmetros do arquivo docker-compose podem variar de acordo com as versões diferentes do docker-engine então sempre confiram a ompatibilidade. Confira a versão do seu compose com a compatibilidade da sua engine em https://docs.docker.com/compose/compose-file/compose-versioning/.

Os parâmetros e especificações de um arquivo docker-compose estão em https://docs.docker.com/compose/compose-file/.

O docker-compose.yml é definido assim:

# obrigatório passar a versão
version: '3.8'

#volumes caso exista
volumes:
logvolume01: {}

# network caso defina, porém o docker compose sempre cria uma brige para aquele ambiente mesmo não sendo definido
networks:
app-net:

# services são os containers
services:
web: # nome do container
build: . # Caso seja

ports: # mapeamento de portas
- 5000:5000
volumes: # mapeamento de volumes
- logvolume01:/var/log
network: # driver de rede
- app_net

redis: # outro container ....
image: redis
network:
- app_net

É obrigatório passar a versão pelas diferenças entre elas e recursos, para não ter incompatiblidade, deve ser a primeira linha. A ordem de network, volumes e serviços não importa.

Criando Composes

O compose nada mais faz do que rodar vários comandos docker pra voce. Vamos relembrar algumas flags do comando docker container run: https://docs.docker.com/engine/reference/commandline/container_run/

--volume
--name
--publish ou -p
--entrypoint
--hostname
--env ou -e --ip --mount --network --restart

Existem muitas outras, logo essas flags são também apontadas dentro de um docker compose, as vezes com outros nomes.

Crie um diretório para armazenarmos nosso compose

mkdir ~/meuprojeto
cd ~/meuprojeto

Vamos utilizar o seguinte Dockerfile para criar uma imagem, mas também poderíamos utilizar uma imagem pronta.

Crie o Dockerfile na pasta do compose.

cat << EOF > ~/compose/Dockerfile
FROM debian
RUN apt-get update && apt-get install wget git apache2 -yq
EXPOSE 80
CMD ["apachectl", "-D", "FOREGROUND"]
EOF

Agora vamos definir o seguinte docker-compose.yaml

cat << EOF > ~/meu_projeto/docker-compose.yaml
version: '3.8'

services:
webserver: # --name
build: .
hostname: webserver # --hostname
ports: # --publish
- 80:80
restart: always # --restart
EOF

Execute a criação do ambiente

vagrant@worker1:~/_meu_projeto$ docker-compose up -d
# removido ....
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
[+] Running 1/1
⠿ Container meu_projeto-webserver-1 Started 0.4s
# Teste o curl para ver se esta respondendo
vagrant@worker1:~/meu_projeto$ curl localhost
# Saida yaml removida...

O comando docker-compose up com o parâmetro -d inicia o compose de forma detached

O comando docker-compose procura o docker-compose no diretório corrente, mas é possível passar o caminho com a opção "-f path_do_compose Exemplo: docker-compose -f ~/compose/docker-compose.yaml up -d

Para listar os containers criados pelo compose podemos utilizar o parametro ps

vagrant@worker1:~/meu_projeto$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
meu_projeto-webserver-1 "apachectl -D FOREGR…" webserver running 0.0.0.0:80->80/tcp, :::80->80/tcp

Observe as imagens

vagrant@worker1:~/meu_projeto$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
# Cria a imagem com o nome da pasta onde esta o arquivo e o nome do serviço
meu_projeto_webserver latest dc964e587ee9 19 minutes ago 303MB
nginx latest 55f4b40fe486 6 days ago 142MB
debian latest d2780094a226 6 days ago 124MB
busybox latest 62aedd01bd85 3 weeks ago 1.24MB
ubuntu latest 27941809078c 3 weeks ago 77.8MB
alpine latest e66264b98777 5 weeks ago 5.53MB
hello-world latest feb5d9fea6a5 9 months ago 13.3kB

Parar os containers criados com o compose utlizamos o stop

vagrant@worker1:~/meu_projeto$ docker-compose stop
[+] Running 1/1
⠿ Container meu_projeto-webserver-1 Stopped 10.2s
# Observe que o status ficou exited
vagrant@worker1:~/meu_projeto$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
meu_projeto-webserver-1 "apachectl -D FOREGR…" webserver exited (137)
vagrant@worker1:~/meu_projeto$

Para iniciar utilizamos o start

vagrant@worker1:~/meu_projeto$ docker-compose start
[+] Running 1/1
⠿ Container meu_projeto-webserver-1 Started 0.4s
vagrant@worker1:~/meu_projeto$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
meu_projeto-webserver-1 "apachectl -D FOREGR…" webserver running 0.0.0.0:80->80/tcp, :::80->80/tcp
vagrant@worker1:~/meu_projeto$

Uma curiosidade é que o docker-compose sempre cria uma rede bridge mesmo que vc não defina uma para criar um isolamento do projeto.

vagrant@worker1:~/meu_projeto$ docker network ls
NETWORK ID NAME DRIVER SCOPE
86e2c2af5201 bridge bridge local
51533d503c46 host host local
# Rede criada automaticamente
3ebeac156156 meu_projeto_default bridge local
4a528ce35bc2 none null local

Para destruir o ambiente utilizamos down

vagrant@worker1:~/meu_projeto$ docker-compose down
[+] Running 2/2
⠿ Container meu_projeto-webserver-1 Removed 10.2s
⠿ Network meu_projeto_default Removed 0.1s
# Nada rodando
vagrant@worker1:~/meu_projeto$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
# Mas não remove a imagem
vagrant@worker1:~/meu_projeto$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
meu_projeto_webserver latest dc964e587ee9 34 minutes ago 303MB
nginx latest 55f4b40fe486 6 days ago 142MB
debian latest d2780094a226 6 days ago 124MB
busybox latest 62aedd01bd85 3 weeks ago 1.24MB
ubuntu latest 27941809078c 3 weeks ago 77.8MB
alpine latest e66264b98777 5 weeks ago 5.53MB
hello-world latest feb5d9fea6a5 9 months ago 13.3kB
# remove a rede
vagrant@worker1:~/meu_projeto$ docker network ls
NETWORK ID NAME DRIVER SCOPE
86e2c2af5201 bridge bridge local
51533d503c46 host host local
4a528ce35bc2 none null local

Vamos agora criar uma página web para ser exibida em nosso container através do docker-compose, crie a pasta html e o arquivo index.html

vagrant@worker1:~/meu_projeto$ mkdir ~/meu_projeto/html
vagrant@worker1:~/meu_projeto$ echo "<h1> Teste de HTML </h1>" > ~/meu_projeto/html/index.html

vamos subir para o dockerhub a imagem que foi criada com o compose anterior

vagrant@worker1:~/meu_projeto$ docker tag meu_projeto_webserver:latest davidpuziol/webserver:latest
vagrant@worker1:~/meu_projeto$ docker push davidpuziol/webserver

Agora que ja temos uma imagem, vamos usá-la no compose ao contrário de buildar uma imagem.

Agora vamos substituir o docker-compose por esse incluindo um volume bind para o html criado.

cat << EOF > ~/meu_projeto/docker-compose.yaml
version: '3.8'

services:
webserver: # --name
image: davidpuziol/webserver
hostname: webserver # --hostname
ports: # --publish
- 80:80
volumes: # fazendo um bind
# O parâmetro $PWD pega o diretório atual
- $PWD/html:/var/www/html
restart: always # --restart
EOF
vagrant@worker1:~/meu_projeto$ docker-compose up -d
[+] Running 2/2
⠿ Network meu_projeto_default Created 0.1s
⠿ Container meu_projeto-webserver-1 Started 0.5s
vagrant@worker1:~/meu_projeto$ curl localhost
<h1> Teste de HTML </h1>
# Destrua o ambiente
vagrant@worker1:~/meu_projeto$ docker-compose down

Compose Multi-containers

A principal funcionalidade de um compose é subir serviços vários serviços ao mesmo tempo criando um ambiente completo. Vamos subir um wordpress e um banco de dados. Já vamos também apontar um arquivo docker-compose.yaml sendo diferente do padrão.

Foi feito alguns comentários no compose abaixo para mostrar como o compose traduz o comando docker. Outro detalhe que foi feita a criação de uma rede user defined para que os containers conheçam uns aos outros pelo nome.

version: '3.8'

# docker volume cretate mysqldb
volumes:
mysqldb:

# docker network create wordpressnet
networks:
wordpressnet:

services:
# docker container run --name wordpress --restart always -p 80:80 -e WORDPRESS_DB_HOST=db -e WORDPRESS_DB_USER=wpuser -e WORDPRESS_DB_PASSWORD=wppassword -e WORDPRESS_DB_NAME=wordpress wordpress
wordpress:
image: wordpress
restart: always
ports:
- 80:80
environment:
# veja que o host foi apontado pelo nome usando o benefício da user defined network com dns ativo
WORDPRESS_DB_HOST: db # poderia ser db.wordpressnet
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppassword
WORDPRESS_DB_NAME: wordpress
networks:
- wordpressnet
# somente vai subir esse container depois do db
depends_on:
- db

# docker container run --name db --restart always -v mysqldb:/var/lib/mysql -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wpuser -e MYSQL_PASSWORD=wppassword -e MYSQL_RANDOM_ROOT_PASSWORD=1 -p 3306:3306 --network wordpressnet mysql:8.0

db:
image: mysql:8.0
restart: always
volumes:
- mysql_db:/var/lib/mysql
ports:
- 3306:3306
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppassword
MYSQL_RANDOM_ROOT_PASSWORD: '1'
networks:
- wordpressnet
cat << EOF > ~/meu_projeto/wordpress-compose.yml
version: '3.8'

networks:
wordpressnet:

volumes:
mysqldb:

services:
wordpress:
image: wordpress
restart: always
ports:
- 80:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppassword
WORDPRESS_DB_NAME: wordpress
networks:
- wordpressnet
depends_on:
- db

db:
image: mysql:8.0
restart: always
volumes:
- mysqldb:/var/lib/mysql
ports:
- 3306:3306
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppassword
MYSQL_RANDOM_ROOT_PASSWORD: '1'
networks:
- wordpressnet
EOF

Execute a criação do ambiente.

docker-compose -f wordpress-compose.yml up -d
docker-compose ps

Acesse o endereço da maquina e configure o wordpress no meu caso.

vagrant@worker1:~/meu_projeto$ ip -c -br a
lo UNKNOWN 127.0.0.1/8 ::1/128
enp0s3 UP 10.0.2.15/24 fe80::cd:65ff:fe0c:9769/64
# é o 192.168.56.110 pois estou no node 1
enp0s8 UP 192.168.56.110/24 fe80::a00:27ff:fe4e:ae9f/64
docker0 DOWN 172.17.0.1/16 fe80::42:d9ff:fe79:88f1/64
br-10d3a7a94991 UP 172.24.0.1/16 fe80::42:11ff:feb7:166f/64
veth285c6b3@if41 UP fe80::b48a:44ff:fe68:691b/64
veth3a1e02b@if43 UP fe80::c8a5:4fff:fe25:c540/64

Se adicionar as linhas abaixo no seu /etc/hosts vc pode acessar direto pelo domain.

192.168.56.100  master.docker-dca.example
192.168.56.110 worker1.docker-dca.example
192.168.56.120 worker2.docker-dca.example
192.168.56.200 registry.docker-dca.example

wordpress

Titulo do Site: Wordpress - teste
Nome de Usuário: user
Senha: password
email: [email protected]

Para verificar os logs do compose

docker-compose -f wordpress-compose.yml logs

Para ver em tempo real adicionar -f

docker-compose -f wordpress-compose.yaml logs -f
# também é possível pelo linux
watch docker-comose -f wordpress.yaml logs

Para destruir o ambiente também é necessário passar o compose caso contrário irá tentar procurar o docker-compose.yaml

docker-compose -f wordpress-compose.yml down

Os volumes continuam persistentes no disco, sendo possível recriar a aplicação com o mesmo conteúdo. Crie novamente e confira na página.

docker-compose -f wordpress-compose.yml up -d

A página de login do painel de controle é tem o final /wp-admin/index.php

exemplo: http://worker1.docker-dca.example//wp-admin/index.php

Destruindo o ambiente observe a ordem. Ele destroi o na mesma ordem. Se webserver depende do db, o db não pode ser destruido antes do webserver. E a rede é a ultima.

vagrant@worker1:~/meu_projeto$ docker-compose -f wordpress-compose.yml down
[+] Running 3/3
⠿ Container meu_projeto-wordpress-1 Removed 1.3s
⠿ Container meu_projeto-db-1 Removed 1.1s
⠿ Network meu_projeto_wordpressnet Removed

Como falado anteriormente ele não destruiu o volume. Por isso podemos subir e descer o ambiente sem problemas.

EXTRAS

Caso alguma mudança seja feita no docker-compose e executar um up ele irá recriar o que mudou. Não é necessário fazer um down e depois um up quando se faz mudanças.

Com o parametro --force-recreate será recriado todo o ambiente inclusive o build caso exista.

O docker-compose pull baixa as imagens mas não sobe o compose
Com o docker-compose build crias as imagens que estáo nos parâmetros build do compose mas não sobe o compose.
docker-compose up --build recria os builds seguido de um up.

Alterando numero de replicas

Vamos mudar o nosso primeiro docker compose incluindo o parâmetro deploy e removendo a porta. É necessário remover a porta pois ele tentará criar o segundo container com a porta do primeiro e não conseguirá.

cat << EOF > ~/meu_projeto/docker-compose.yaml
version: '3.9'

services:
webserver:
build: .
hostname: webserver
deploy:
replicas: 2
volumes:
- $PWD/html:/var/www/html
restart: on-failure
EOF

vagrant@worker1:~/meu_projeto$ docker-compose --build up -d
unknown flag: --build
vagrant@worker1:~/meu_projeto$ docker-compose up --build -d
[+] Building 0.1s (6/6) 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/debian:latest 0.0s
=> [1/2] FROM docker.io/library/debian 0.0s
=> CACHED [2/2] RUN apt-get update && apt-get install wget git apache2 -yq 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:dc964e587ee9bb82042cde8857f6c02438b9a3f7511dfa9b61d53c9ca3585a62 0.0s
=> => naming to docker.io/library/meu_projeto_webserver 0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
[+] Running 2/2
⠿ Container meu_projeto-webserver-2 Started 10.7s
⠿ Container meu_projeto-webserver-1 Started 10.6s
vagrant@worker1:~/meu_projeto$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
meu_projeto-webserver-1 "apachectl -D FOREGR…" webserver running 80/tcp
meu_projeto-webserver-2 "apachectl -D FOREGR…" webserver running 80/tcp
vagrant@worker1:~/meu_projeto$

para escalar um container

docker-compose up --scale webserver=5 -d