Files
docker/docs/4. Docker Compose — несколько сервисов, сеть, тома.md
T
2026-05-04 11:07:31 +03:00

225 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Зачем Docker Compose
Когда сервисов несколько (БД, бэкенд, фронт, прокси), писать длинные `docker run` неудобно. **Compose** описывает весь стек в одном файле (`compose.yaml` или `docker-compose.yml`), поднимает общую сеть и тома, учитывает порядок старта через `depends_on` и проверки `healthcheck`.
Команды:
```
docker compose up -d --build
docker compose ps
docker compose logs -f api
docker compose down
docker compose down -v
```
Последняя команда с **`-v`** удалит именованные тома — данные БД пропадут (удобно для сброса демо, опасно если это прод).
---
## Где лежит учебный проект
В репозитории подготовлен полный стек (четыре сервиса):
| Путь | Содержимое |
|------|------------|
| `docker/examples/multi-service/compose.yaml` | Описание всех сервисов, сетей, томов |
| `docker/examples/multi-service/.env.example` | Пример переменных окружения для Compose |
| `docker/examples/multi-service/web/` | Nginx + статика через `COPY` |
| `docker/examples/multi-service/api/` | Flask + Postgres + загрузка файлов в том |
| `docker/examples/multi-service/proxy/` | Входной nginx: `/` → web, `/api/` → api |
Скопируйте переменные окружения:
```
cd docker/examples/multi-service
cp .env.example .env
```
**Скриншот:** файловый менеджер или IDE с деревом папок `multi-service` (видны `compose.yaml`, `web`, `api`, `proxy`).
<img src="./assets/compose_project_tree.png" width="auto">
Запуск:
```
docker compose up -d --build
```
**Скриншот:** вывод `docker compose up` — этапы build, Creating..., Started.
<img src="./assets/compose_up_build_started.png" width="auto">
**Скриншот:** `docker compose ps` — все сервисы `running` (или `healthy` где настроено).
<img src="./assets/compose_ps_all_running.png" width="auto">
Откройте в браузере `http://127.0.0.1:8080` (если в `.env` не меняли `HTTP_PORT`).
**Скриншот:** главная страница демо со ссылками на API.
<img src="./assets/compose_browser_home.png" width="auto">
Проверка API через прокси:
```
curl -s http://127.0.0.1:8080/api/health | jq .
curl -s http://127.0.0.1:8080/api/notes | jq .
curl -s -X POST http://127.0.0.1:8080/api/notes \
-H 'Content-Type: application/json' \
-d '{"body":"Первая заметка из curl"}' | jq .
echo demo > /tmp/upload-demo.txt
curl -s -F "file=@/tmp/upload-demo.txt" http://127.0.0.1:8080/api/upload | jq .
curl -s http://127.0.0.1:8080/api/uploads | jq .
```
**Скриншот:** терминал с ответами JSON.
<img src="./assets/compose_curl_api_notes.png" width="auto">
---
## Логическая схема стека
1. **Пользователь хоста** подключается только к **proxy** (порт `HTTP_PORT`).
2. **proxy** по пути `/` отдаёт статику с **web**; по `/api/`**api** (внутренний URL без префикса `/api`).
3. **api** ходит в **db** по hostname `db` и порту `5432` (стандарт Postgres) — резолвится DNS-ом Docker в той же сети `backend`.
4. Данные БД — в томе **`pgdata`**; загруженные файлы API — в томе **`api_uploads`**.
**Скриншот:** нарисованная схема стрелок browser → proxy → web/api → db; подписать тома.
<img src="./assets/compose_architecture_diagram.png" width="700">
---
## Разбор ключевых фрагментов `compose.yaml`
### Имена и сеть
Поле `name: multi-service-demo` задаёт **префикс** для имён контейнеров/ресурсов по умолчанию (удобно не конфликтовать с другими проектами).
Сеть `backend` с драйвером `bridge` — все перечисленные сервисы в одной L2-сети, видят друг друга по **DNS-имени сервиса** (`db`, `api`, `web`, `proxy`).
### Зависимости и здоровье
```
depends_on:
db:
condition: service_healthy
```
Без `condition` Compose лишь **упорядочивает** старт, но не ждёт готовности БД. С `healthcheck` у `db` сервис `api` не начнёт считаться поднятым для зависимых, пока Postgres не пройдёт проверку.
**Скриншот:** `docker compose logs db` в момент `database system is ready to accept connections`.
<img src="./assets/compose_logs_db_ready.png" width="auto">
### `expose` vs `ports`
- **`ports`** — публикация на хост (нужно для proxy).
- **`expose`** — документация + открытие порта **между** контейнерами сети; на сетевой интерфейс хоста не выводится.
Так мы не публикуем Postgres наружу — к БД можно попасть только из контейнеров в той же сети (или если вы явно добавите `ports` для отладки).
### Тома
```yaml
volumes:
pgdata:
api_uploads:
```
Именованные тома хранятся в области Docker на диске. Пример «скопировать/сохранить файл в Docker» в runtime:
```
echo hello > /tmp/up.txt
# имя контейнера смотрите в выводе: docker compose ps -q api
docker cp /tmp/up.txt "$(docker compose ps -q api)":/data/uploads/host-demo.txt
docker compose exec api ls -la /data/uploads
```
**Скриншот:** список файлов в `/data/uploads` внутри контейнера `api`.
<img src="./assets/compose_api_uploads_volume.png" width="auto">
Альтернатива для разработки — **bind-mount** каталога с хоста в сервис (в учебном файле не включено, чтобы не привязывать пути к вашему `$HOME`):
```yaml
services:
api:
volumes:
- ./local_uploads:/data/uploads
```
---
## Копирование файлов в контейнер в контексте Compose
| Способ | Когда использовать |
|--------|---------------------|
| `COPY` в `Dockerfile` | Статика, код, конфиги, которые должны быть **в образе** на момент деплоя |
| Именованный / bind том | Данные, логи, загрузки пользователей, меняющиеся без пересборки |
| `docker cp` | Разовая отладка, экстренная подмена файла |
---
## Обновление одного сервиса после правки кода
```
# изменили только api/app.py
docker compose build api
docker compose up -d api
```
**Скриншот:** пересборка только слоя `COPY app.py` (кэш BuildKit для предыдущих шагов).
<img src="./assets/compose_rebuild_api_cache.png" width="auto">
---
## Просмотр конфигурации, которую «увидел» Compose
```
docker compose config
```
**Скриншот:** полный YAML после подстановки `.env` (проверьте порты и пароли перед скрином — размыть секреты).
<img src="./assets/compose_config_interpolated.png" width="auto">
---
## Остановка и очистка
Мягко (тома сохраняются):
```
docker compose down
```
Полный сброс демо:
```
docker compose down -v --rmi local
```
**Скриншот:** `docker volume ls` без томов проекта после `down -v`.
<img src="./assets/compose_down_volumes_removed.png" width="auto">
---
## Идеи для дополнительных скриншотов (по желанию)
- **Portainer** или Docker Desktop — список контейнеров и томов в GUI.
- **`docker compose top`** — процессы внутри сервисов.
- **Ошибка** неверного `depends_on` / healthcheck — учебный кадр «как выглядит», если api стартует раньше БД без `condition`.
---
## Документация
- [Compose file reference](https://docs.docker.com/compose/compose-file/)
- [Networking in Compose](https://docs.docker.com/compose/networking/)
- [Use Compose Watch](https://docs.docker.com/compose/file-watch/) — live reload при разработке (отдельная тема)