Já podemos dar como certo que o docker está em um momento ou outro na vida de um programador, e muitas vezes nos utilizamos de arquivos docker-compose.yaml
para executarmos localmente nossas aplicações e suas dependências e algumas vezes até em produção.
Por vezes temos que executar coisas mais complexas como executar uma migração de banco de dados antes da nossa aplicação executar, garantir que o banco de dados já esteja pronto para receber conexões, executar um subconjunto de dependências e assim por diante. Em muitas das minhas experiências vi esses requisitos serem executados através de scripts shell que nem sempre são familiares a todos os membros da equipe o que sempre gera uma estranheza e medo de extender tais funcionalidades, pensando nisso resolvi deixar mais evidentes algumas funcionalidades do Docker Compose
que por muitas vezes são ignoradas.
Healthcheck
A especificação do Docker Compose conta com uma forma de healthcheck que podemos utilizar para garantir que nossa aplicação não só está rodando, mas como ela está operacional, essa checagem é útil quando queremos garantir que um serviço de ser executado somente após outro serviço estar totalmente operacional.
version: '3'
services:
db:
container_name: db
image: postgres:alpine
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_USER=postgres
- POSTGRES_DB=postgres
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
Definindo o banco de dados com esse healthcheck quando adicionarmos a definição da nossa aplicação podemos dizer que ao depender do banco ela só inicie após o serviço dependente estar de pé e saudável.
version: '3'
services:
app:
container_name: app
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/app
ports:
- "8000:8000"
command: "gunicorn -b 0.0.0.0:8000 -k gevent sample.main:create_app() --access-logfile=- --preload -w 4"
depends_on:
db:
condition: service_health
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/healthcheck"]
interval: 5s
timeout: 30s
retries: 5
db:
container_name: db
image: postgres:alpine
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_USER=postgres
- POSTGRES_DB=postgres
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
Profiles
As vezes nos deparamos com arquivos docker-compose.yaml
extensos e com várias definições de serviço, porém ao desenvolver alguma funcionalidade necessitamos somente de um subconjunto desses serviços afim de testarmos nosso código, para isso temos os profiles
version: '3'
services:
app:
container_name: app
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/app
ports:
- "8000:8000"
command: "gunicorn -b 0.0.0.0:8000 -k gevent sample.main:create_app() --access-logfile=- --preload -w 4"
depends_on:
db:
condition: service_healthy
profiles:
- web
- full
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/healthcheck"]
interval: 5s
timeout: 30s
retries: 5
db:
container_name: db
image: postgres:alpine
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_USER=postgres
- POSTGRES_DB=postgres
profiles:
- dependencies
- web
- full
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
Com essa definição podemos utilizar o comando docker-compose --profile dependencies up
para iniciar somente a nossa base de dados.
Extensão
A mesma base de código muitas vezes pode ser utilizada para executar serviços em diferentes contextos, uma aplicação web, um serviço assíncrono de tarefas, uma execução única de linha de comando. Sendo assim é interessante para nós desenvolvedores definirmos uma base comum e somente personalizarmos para cada caso de uso.
Para atingir tal objetivo no Docker Compose
temos a opção de usar um feature da própria
Ancora YAML
A especificação do YAML permite que criemos ancoras para reaproveitamento de definições, assim podemos criar uma base comum para um serviço para depois especializarmos.
version: '3'
x-app: &app
build:
context: .
dockerfile: Dockerfile
depends_on:
db:
condition: service_healthy
queue:
condition: service_healthy
services:
app:
<<: *app
container_name: app
volumes:
- .:/app
ports:
- "8000:8000"
command: "gunicorn -b 0.0.0.0:8000 -k gevent sample.main:create_app() --access-logfile=- --preload -w 4"
profiles:
- web
- full
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/healthcheck"]
interval: 5s
timeout: 30s
retries: 5
background:
<<: *app
container_name: background
command: "celery -A sample.main:celery_app worker"
environment:
- CELERY_RESULT_BACKEND=redis://cache:6379/0
- CELERY_BROKER_URL=amqp://guest:guest@queue:5672//
profiles:
- background
- full
db:
container_name: db
image: postgres:alpine
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_USER=postgres
- POSTGRES_DB=postgres
profiles:
- dependencies
- full
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
queue:
container_name: queue
image: rabbitmq:3-management-alpine
profiles:
- dependencies
- full
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 30s
timeout: 30s
retries: 3
cache:
container_name: cache
image: redis:alpine
profiles:
- dependencies
- full
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 30s
retries: 3
volumes:
db_data:
Essas são algumas formas de deixar seu Docker Compose
um pouco mais dinâmico e inteligente.
Top comments (0)