1. Подними два backend-инстанса
Для начала не усложняй архитектуру. Достаточно двух процессов приложения на разных портах или в разных Docker-контейнерах.
app_1: 127.0.0.1:8001
app_2: 127.0.0.1:80022. Добавь upstream в Nginx
Upstream описывает, куда Nginx может отправлять запросы. Если один backend упал, Nginx временно перестанет слать туда трафик.
upstream app_backend {
least_conn;
server 127.0.0.1:8001 max_fails=3 fail_timeout=20s;
server 127.0.0.1:8002 max_fails=3 fail_timeout=20s;
}
server {
server_name example.com;
location / {
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}3. Сделай health endpoint
В приложении нужен быстрый endpoint, который не ходит в тяжёлые внешние API. Обычно достаточно проверить, что процесс жив и может подключиться к базе.
- Добавь endpoint
/health. - Верни HTTP 200, если приложение готово принимать трафик.
- Верни 500, если нет соединения с базой или критичным сервисом.
4. Проверь отказоустойчивость
curl -I https://example.com/health
docker stop app_1
curl -I https://example.com/
docker start app_1Если сайт продолжает открываться после остановки одного инстанса, базовая балансировка работает.
5. Выбери стратегию распределения
Для большинства web-проектов достаточно least_conn: новый запрос уходит туда, где меньше активных соединений. Если проект хранит состояние в памяти процесса, сначала вынеси session в Redis или базу. Sticky sessions оставляй только как временный компромисс, иначе один перегруженный инстанс будет получать одного и того же активного клиента.
round_robin— простой вариант по умолчанию.least_conn— лучше для запросов разной длительности.ip_hash— привязка клиента к одному backend, но хуже для равномерной нагрузки.
6. Zero-downtime deploy
Не перезапускай все backend-инстансы одновременно. Обновляй по одному: сначала убери инстанс из upstream или останови контейнер, дождись запуска новой версии, проверь /health, потом переходи к следующему.
docker compose up -d --build app_1
curl -f https://example.com/health
docker compose up -d --build app_2
curl -f https://example.com/health7. Что логировать
В access log добавь upstream, время ответа и request id. Тогда при ошибке будет понятно, какой backend отвечал и почему запрос тормозил.
log_format upstreamlog '$remote_addr $request '
'status=$status upstream=$upstream_addr '
'request_time=$request_time upstream_time=$upstream_response_time';Checklist перед production
- SSL работает на внешнем Nginx.
- Логи каждого backend-инстанса разделены.
- Static/media файлы вынесены в общий volume или object storage.
- Миграции запускаются отдельно от web-процессов.
- Deploy не перезапускает все инстансы одновременно.