Por que gerenciar containers um a um não escala.
docker run repetido para cada uma delas.Limitação: o cliente docker (CLI) é um wrapper para a REST API do Docker — cada comando afeta um container por vez.
Solução: o Docker Compose — com um único comando é possível configurar e rodar múltiplos containers, com muito mais possibilidades de configuração.

compose.yamlCada bloco descreve um container: imagem, build, portas, variáveis, volumes…
Volumes nomeados, compartilhados entre serviços e persistentes entre reinícios.
Por padrão o Compose já cria uma rede e coloca todos os serviços nela — cada serviço vira um hostname nessa rede.
services — configurações principaisimage / build — imagem a rodar (ou como construir).ports — mapeia "HOST:CONTAINER".environment — variáveis de ambiente.volumes — o que persistir ou montar.depends_on — ordem de inicialização.
services:
web:
image: nginx:alpine
ports:
- "8080:80"
depends_on:
- db
environment:
- DB_HOST=db
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=secret
ports:
- "5432:5432"
Em ports, a porta da esquerda é do seu computador; a da direita, a que a app escuta dentro do container. O serviço web fala com o db pelo hostname db — resolvido pela rede interna do Compose.
Do Dockerfile ao docker compose up.
Dockerfile
FROM python:3-slim
RUN mkdir /code
COPY . /code
WORKDIR /code/
RUN pip install django django-crispy-forms crispy-bootstrap5
compose.yamlEstrutura do projeto
cafecompao/
├── cafecompao/
├── padarias/
├── prototipo/
├── static/
├── templates/
├── compose.yaml
├── Dockerfile
├── manage.py
└── requirements.txt
services:
django:
build: .
working_dir: /app
ports:
- "8000:8000"
volumes:
- .:/app:z
command: sh -c "python manage.py migrate &&
python manage.py runserver 0.0.0.0:8000"
docker compose build
$ docker compose build
[+] Building 4.9s (8/10)
=> [django internal] load build definition from Dockerfile
=> [django internal] load metadata for docker.io/library/python:3-alpine
=> [django internal] load .dockerignore
=> CACHED [django 1/5] FROM docker.io/library/python:3-alpine
=> [django 2/5] COPY . /app
=> [django 3/5] WORKDIR /app
=> [django 4/5] RUN pip install django Pillow django-crispy-forms crispy-bootstrap5
$ docker compose run django python manage.py migrate
[+] Running 1/0
✓ Container cafecompao_db Started
Operations to perform:
Apply all migrations: admin, auth, contenttypes, padarias, sessions
Running migrations:
Applying padarias.0001_initial... OK
Applying padarias.0006_padaria_cestas_endereco... OK
docker compose up
$ docker compose up
[+] Running 1/1
✓ Container cafecompao-django-1 Recreated
Attaching to django-1
django-1 | Watching for file changes with StatReloader
django-1 | [06/Sep/2024 04:21:58] "GET / HTTP/1.1" 200 3034
django-1 | [06/Sep/2024 04:22:01] "GET /padarias HTTP/1.1" 200 1944
Instruções no GitHub da disciplina.
Django + PostgreSQL num só compose.yaml.
compose.yaml — volumes e serviço db
volumes:
bd_data: {}
services:
db:
image: postgres
container_name: cafecompao_db
ports:
- "5432:5432"
volumes:
- bd_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=cafecompao
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_PORT=5432
compose.yaml — serviço django
django:
build:
context: .
dockerfile: ./Dockerfile
image: newtonsbm/cafecompao_exemplo:latest
container_name: cafecompao_app
depends_on:
- db
volumes:
- .:/code
ports:
- "3000:8000"
command: python manage.py runserver 0.0.0.0:8000
depends_on garante que o db suba antes do django.
settings.py — configurando o Postgres
# Database
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'cafecompao',
'USER': 'postgres',
'PASSWORD': 'postgres',
'HOST': 'db',
'PORT': '5432',
}
}
HOST é o nome do serviço no compose.yaml (db), não localhost — os containers se enxergam pela rede interna do Compose.
Dois serviços prontos, sem escrever uma linha de Dockerfile.

docker-compose up
$ docker-compose up
Creating network "compose_wordpress_default" with the default driver
Creating volume "compose_wordpress_db_data" with the default driver
Pulling db (mysql:5.7)...
Status: Downloaded newer image for mysql:5.7
Pulling wordpress (wordpress:latest)...
Status: Downloaded newer image for wordpress:latest
Creating compose_wordpress_db_1 ... done
Creating compose_wordpress_wordpress_1 ... done
db_1 | Entrypoint script for MySQL Server 5.7.31 started.
wordpress_1 | WordPress not found in /var/www/html - copying now...

localhost:8000, o WordPress já sobe pronto para configurar.compose.yml — WordPress + MySQL
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
Persistindo dados além do ciclo de vida do container.

$ docker volume create my-vol
$ docker volume ls
$ docker volume rm my-vol
docker volume inspect
$ docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
--mount
$ docker run -d \
--name devtest \
--mount source=myvol2,target=/app \
nginx:latest
compose.yml — volumes nomeados e bind mount
services:
web:
image: nginx:alpine
volumes:
- type: volume
source: mydata
target: /data
volume:
nocopy: true
- type: bind
source: ./static
target: /opt/app/static
db:
image: postgres:latest
volumes:
- "dbdata:/var/lib/postgresql/data"
volumes:
mydata:
dbdata:
Sintaxe longa (type/source/target) permite opções extras; a curta (origem:destino) é só um atalho.
Implemente a composição de serviços de uma aplicação utilizando Docker Compose.
Suba os arquivos no repositório da disciplina.