105 lines
11 KiB
Markdown
105 lines
11 KiB
Markdown
# Лабораторная 3. Балансировка и отказ API-реплики
|
|
|
|
Запустить несколько взаимозаменяемых экземпляров API за единой точкой входа и сохранить корректность при остановке одного из них.
|
|
|
|
Это **skeleton**, а не готовый сервер. Здесь есть задание, контракт и публичные проверки. Реализацию приложения, Dockerfile и требуемую инфраструктуру пишет студент. Язык и framework свободные; UI не обязателен.
|
|
|
|
[Карта курса](COURSE.md) · [Процесс fork/PR](CONTRIBUTING.md) · [OpenAPI](contracts/openapi.json) · [Семантика API](contracts/README.md) · [Проверки](tests/README.md) · [Отчёт](REPORT.md) · [Примеры JSON](contracts/examples.json) · [Памятка преподавателю](MAINTAINERS.md)
|
|
|
|
## Откуда начинаем
|
|
|
|
Нужен минимум lab2: PostgreSQL, общие действующие сессии, миграции. Добавляются X-Instance-Id и обязательный Idempotency-Key при отправке сообщения.
|
|
|
|
**Архитектура:** Клиент → LB → API × 2+ → общие PostgreSQL и хранилище сессий. Compose или Swarm — на выбор.
|
|
|
|
## Что сделать
|
|
|
|
1. Выберите Compose + Nginx/HAProxy/Traefik либо Docker Swarm. Для выбранного варианта дайте одну последовательность запуска.
|
|
2. Уберите локальные сессии/другие данные, от которых зависит корректность, из API. Миграции выполняйте отдельно и согласованно, а не гонкой всех реплик.
|
|
3. Настройте балансировщик, проверки готовности, DNS/обновление backend-адресов и graceful shutdown. Не закрепляйте клиента sticky session как способ исправить раздельное состояние.
|
|
4. Реализуйте общую идемпотентность отправки сообщения. Проверьте одинаковый ключ одновременно через разные реплики.
|
|
5. Подготовьте явный action script, останавливающий одну выбранную API-реплику, и продемонстрируйте чтение/запись во время отказа.
|
|
|
|
## Оценка «3»: работающий минимум
|
|
|
|
- Весь стенд описан в compose.yaml или stack.yaml, запускается документированной командой. Не менее двух API-реплик, одна публичная точка входа, одна общая БД и общее хранилище сессий.
|
|
- Разные запросы одного пользователя обслуживаются разными репликами без повторного входа. Это видно по X-Instance-Id. Доступ через LB — основной проверяемый маршрут.
|
|
- Можно остановить любую одну API-реплику при работающей другой: не позднее 10 секунд продолжаются новые чтения/записи, подтверждённые сообщения доступны. Краткие ошибки/оборванные запросы во время обнаружения отказа измеряются и допустимы.
|
|
- Повтор POST сообщения с тем же Idempotency-Key не создаёт дубль, включая конкурентный повтор и переключение реплики. Неподтверждённую запись клиент может безопасно повторить с тем же ключом.
|
|
- make test, persistence и failover-сценарий проходят; для failover проверяется наличие ≥2 наблюдаемых реплик. Для Swarm сборка образа и его доступность узлам описаны отдельно.
|
|
|
|
## Оценка «4»: уровень стажёра/джуна
|
|
|
|
Весь уровень 3 **и все** пункты ниже:
|
|
|
|
- Health/readiness, timeouts и прекращение приёма при SIGTERM настроены согласованно; LB исключает недоступный backend. Укажите период проверок и максимальное время обнаружения отказа.
|
|
- Последовательно остановите каждую API-реплику (восстанавливая предыдущую); соберите RPS, ошибки и время восстановления. Общая идемпотентность проверяется параллельными запросами с одним ключом.
|
|
- Контейнеры имеют CPU/RAM-лимиты, API не публикуют порты наружу в обход LB; секреты и хранилища общие только там, где нужно. Нет container_name, мешающего масштабированию одного Compose-service.
|
|
- Rolling update или эквивалентная поочерёдная замена проверена под нагрузкой. Миграции запускаются однократно и совместимы с работающей версией либо ограничение честно описано.
|
|
|
|
## Оценка «5»: исследование и доказанный результат
|
|
|
|
Весь уровень 4 **и один законченный трек на выбор**. Приведите гипотезу, повторяемую методику, результаты и ограничения.
|
|
|
|
**Трек 1. Цена масштабирования.** При фиксированном суммарном CPU/RAM сравните 1/2/4 API-реплики на одном профиле нагрузки; затем проведите серию с ростом ресурсов. Найдите saturation point БД/LB/пула и покажите, почему рост реплик перестаёт помогать. Дайте исходные данные, графики и одно подтверждённое улучшение.
|
|
|
|
**Трек 2. Отказ узла.** Расширьте стенд до нескольких узлов, покажите перенос API при потере узла и разберите оставшиеся SPOF (LB, DB, storage). Для заявленной защищённой зависимости покажите реальный failover и сохранность данных. Если DB не реплицирована, не заявляйте выживание её узла.
|
|
|
|
## Как начать и проверить
|
|
|
|
```sh
|
|
# Работает прямо в skeleton, Python 3.9+; приложение не запускается:
|
|
make check
|
|
|
|
# После реализации запустите свой сервер/стенд по REPORT.md.
|
|
# По умолчанию тест использует http://localhost:8080.
|
|
make test
|
|
```
|
|
|
|
`.env.example`, `compose.example.yaml` и `contracts/infrastructure.json` описывают настройки и роли компонентов. **compose.example.yaml — список ролей с пустым services, не запускаемый стенд.** Реализуйте свой `compose.yaml`/`stack.yaml`, закрепите версии образов, заполните локальный `.env` и опишите bootstrap/миграции. Все зависимости поднимаются из этого репозитория; ссылка «у меня БД уже установлена» не заменяет воспроизводимость.
|
|
|
|
```sh
|
|
# Compose-вариант после реализации:
|
|
docker compose --env-file .env config --quiet
|
|
docker compose --env-file .env up -d --build
|
|
# Дождитесь /health/ready; затем make test.
|
|
# Для Swarm укажите эквивалентные build/push/deploy-команды в REPORT.md.
|
|
```
|
|
|
|
|
|
Примеры запросов доступны в `examples/requests.http`, примеры JSON — в `contracts/examples.json`. Для полной проверки схемы (по желанию локально; CI делает её автоматически):
|
|
|
|
```sh
|
|
python3 -m venv .venv
|
|
.venv/bin/python -m pip install -r requirements-dev.txt
|
|
make validate PYTHON=.venv/bin/python
|
|
```
|
|
|
|
Smoke не выставляет оценку автоматически. Проверку сохранности/отказов запускайте явно по [tests/README.md](tests/README.md), остальные доказательства приведите в отчёте.
|
|
|
|
## Сценарий защиты
|
|
|
|
1. Запустить стенд и показать ≥2 разных X-Instance-Id через LB с одной сессией.
|
|
2. Запустить make test-failover ACTION_SCRIPT=scripts/stop-one.sh. Скрипт теста выполняет запросы во время action, учитывает ошибки и повторяет сообщения с исходным ключом.
|
|
3. Показать, какой контейнер действительно остановлен; заголовок сам по себе не доказательство разных процессов. Повторить для второй реплики после восстановления первой.
|
|
4. Проверить restart всего стенда с сохранением volumes и неизменным токеном. Для 4/5 — соответствующие эксперименты.
|
|
|
|
## Что должно быть в PR
|
|
|
|
compose.yaml или stack.yaml, конфигурация LB, scripts/restart.sh, scripts/stop-one.sh, Dockerfile, миграции, результаты отказов.
|
|
|
|
Заполните `REPORT.md` и [шаблон PR](.github/pull_request_template.md), укажите целевую оценку/трек. Не меняйте обязательный контракт и публичные tests ради зелёного результата. Сохраняйте возможности предыдущих лабораторных в пределах [объявленных переходов](COURSE.md).
|
|
|
|
## Вопросы на защите
|
|
|
|
- Чем отказ процесса отличается от отказа узла?
|
|
- Почему depends_on/healthcheck сами по себе не являются балансировщиком?
|
|
- Какие POST можно повторять после timeout и что защищает Idempotency-Key?
|
|
- Что сломается при увеличении числа API, если сессии находятся в локальной памяти?
|
|
|
|
## Первичные материалы
|
|
|
|
- [Compose services](https://docs.docker.com/reference/compose-file/services/)
|
|
- [Swarm routing mesh](https://docs.docker.com/engine/swarm/ingress/)
|
|
- [Swarm services](https://docs.docker.com/engine/swarm/services/)
|