15. Docker Compose¶
Довідник бонус-уроку «Linux для розробника»
Розділ перевірено: кожну команду виконано в контейнерах Ubuntu, Debian і python:3.12-slim. Урок з вправами — Бонус. Linux. Docker для проєкту курсу — урок 49; Compose — урок 50.
Навіщо це потрібно¶
Django-проєкт — це не один контейнер. Це зазвичай: - Django app (Gunicorn) - PostgreSQL - Redis (для Celery або кешування) - Celery worker - Nginx
Запускати кожен вручну через docker run з довгими параметрами — складно і незручно. Docker Compose дозволяє описати весь стек у одному файлі і запустити все однією командою.
Просте пояснення¶
Docker Compose — це як диригент оркестру. Ти описуєш, які "музиканти" (контейнери) мають грати, і одна команда запускає всіх разом у правильному порядку.
Мінімальний docker-compose.yml для Django¶
services:
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
ports:
- "8000:8000"
volumes:
- .:/app
env_file:
- .env
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
environment:
POSTGRES_DB: app_db
POSTGRES_USER: app_user
POSTGRES_PASSWORD: app_password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app_user -d app_db"]
interval: 5s
timeout: 5s
retries: 5
volumes:
postgres_data:
Production docker-compose.yml¶
services:
web:
build:
context: .
dockerfile: Dockerfile
command: gunicorn myapp.wsgi:application --bind 0.0.0.0:8000 --workers 3
expose:
- "8000"
volumes:
- static_files:/app/staticfiles
- media_files:/app/media
env_file:
- .env
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: always
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
env_file:
- .env.db
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: always
worker:
build: .
command: celery -A myapp worker --loglevel=info
env_file:
- .env
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: always
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- static_files:/var/www/static
- media_files:/var/www/media
depends_on:
- web
restart: always
volumes:
postgres_data:
static_files:
media_files:
$$POSTGRES_USER — з подвійним $: так Compose не підставляє змінну сам (з оточення хоста чи .env поруч із docker-compose.yml, де її немає — лише попередження The "POSTGRES_USER" variable is not set і порожній рядок), а передає $POSTGRES_USER у контейнер, де її знає shell з .env.db.
Ключові поля docker-compose.yml¶
services¶
Кожен ключ всередині services: — це ім'я сервісу (контейнер отримає ім'я на кшталт myproject-web-1):
build vs image¶
ports vs expose¶
ports:
- "8000:8000" # HOST:CONTAINER — доступно ззовні
expose:
- "8000" # тільки між контейнерами в мережі Compose
На production: expose для web (тільки Nginx має доступ), ports: "80:80" для Nginx.
volumes¶
volumes:
- .:/app # bind mount: директорія хоста → контейнер
- postgres_data:/var/lib/postgresql/data # іменований volume
Bind mount (.:/app) зручний у розробці: зміни в коді одразу видні в контейнері без перезбірки.
depends_on¶
Без condition: service_healthy Django може стартувати раніше ніж PostgreSQL готовий приймати підключення.
restart¶
restart: always # завжди перезапускати при падінні
restart: unless-stopped # окрім явної зупинки
restart: on-failure # тільки при помилці
Основні команди Docker Compose¶
# Запуск всього стека
docker compose up
docker compose up -d # у фоні (detached)
docker compose up --build # перезібрати images перед запуском
# Зупинка
docker compose down
docker compose down -v # зупинити і видалити volumes (дані!)
# Логи
docker compose logs
docker compose logs -f # стежити в реальному часі
docker compose logs web # логи конкретного сервісу
# Виконати команду в контейнері
docker compose exec web bash
docker compose exec web python manage.py migrate
docker compose exec web python manage.py createsuperuser
docker compose exec web python manage.py shell
# Статус
docker compose ps
# Перезапуск одного сервісу
docker compose restart web
Мережа між контейнерами¶
Docker Compose автоматично створює мережу і всі сервіси знаходяться в ній. Вони спілкуються по імені сервісу:
# settings.py (або .env)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'HOST': 'db', # ім'я сервісу з docker-compose.yml
'PORT': '5432',
'NAME': 'app_db',
'USER': 'app_user',
'PASSWORD': 'app_password',
}
}
Замість localhost — db. Замість localhost:6379 для Redis — redis:6379.
flowchart LR
classDef step fill:#eceff1,stroke:#546e7a,stroke-width:1px,color:#1a1a1a;
classDef decision fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#1a1a1a;
classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1a1a1a;
classDef error fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#1a1a1a;
classDef warning fill:#fff8e1,stroke:#e65100,stroke-width:2px,color:#1a1a1a;
Browser["Browser"] --> Nginx["nginx<br>порт 80"]
Nginx --> Web["web (Django+Gunicorn)<br>порт 8000"]
Web --> DB["db (PostgreSQL)<br>порт 5432"]
Web --> Redis["redis<br>порт 6379"]
Worker["worker<br>(Celery)"] --> DB
Worker --> Redis
class Browser,Nginx,Web,Worker,DB,Redis step
Типові помилки початківців¶
Помилка 1: connection to server at "db" (...), port 5432 failed: Connection refused при запуску Django
PostgreSQL ще не стартував. Додай
healthcheckіcondition: service_healthyвdepends_on.
Помилка 2: Зміни в коді не відображаються
У dev-режимі потрібен bind mount:
- .:/app. Або перезбери:docker compose up --build.
Помилка 3: docker compose down -v видалив дані бази
-vвидаляє volumes! Для зупинки без втрати даних — простоdocker compose down.
Помилка 4: HOST=localhost для бази в Docker
В мережі Compose —
HOST=db(ім'я сервісу), неlocalhost.
Практичне завдання¶
Завдання 1¶
Напиши docker-compose.yml для Django + PostgreSQL. Запусти і перевір:
Завдання 2¶
Всередині shell:from django.contrib.auth.models import User; User.objects.all()
Завдання 3¶
Перевір що сервіси бачать одне одного:
(ping в образі python:3.12-slim немає; getent покаже IP, у який резолвиться ім'я db.)
Самоперевірка¶
- [ ] Я розумію структуру
docker-compose.yml: services, volumes, ports - [ ] Я знаю різницю між
portsіexpose - [ ] Я розумію, що контейнери спілкуються по імені сервісу, не через localhost
- [ ] Я вмію запустити стек через
docker compose up --build - [ ] Я знаю команди:
exec,logs,down,ps
Короткий підсумок¶
Docker Compose дозволяє описати весь стек у YAML і запустити однією командою. Контейнери в одному Compose-файлі утворюють спільну мережу і знаходять одне одного по імені сервісу. Для dev — bind mount коду. Для prod — іменовані volumes для даних. Наступний крок — DevOps workflow.