Как мы внедрили zero-downtime деплой с Nginx и Docker: кейс DevOps-команды
Всё началось с одного сообщения: «А почему сайт лежал ночью почти минуту?». На проде выкатили микрофикс — фронт сдох, Nginx поймал 502, мониторинг заорал. Да, всего минута. Но у нас финтех, SLA, и CTO уже закатывал рукава.
Так я занялся zero-downtime деплоем.
Контекст: что мы вообще деплоим
Немного про приложение. Это микросервис, отвечающий за расчёт кредитных скорингов в нашей финтех-платформе. Он получает заявку, парсит данные клиента (доходы, возраст, кредитную историю), запускает ML-модель и возвращает скоринговый балл в формате JSON.
Всё на Node.js (NestJS + TypeORM), база — PostgreSQL. Приложение довольно чувствительное к отклонениям: если не ответит за 2 секунды — фронт отваливает, клиент видит ошибку.
Именно на нём были ночные 502. Даже 10 секунд простоя — это десятки потерянных заявок и потенциальный минус к доверию.
Зачем вообще нужен zero-downtime
Если ты когда-либо деплоил вживую, знаешь, как это выглядит:
- Дёргаем docker-compose pull && up -d;
- Старый контейнер гаснет, пока новый скачивается;
- За это время — пустота, ошибки, 502.
И это не баг — это обычное поведение. Просто никто не позаботился сделать деплой бесшовным.
Для нас важны были:
- минимальный даунтайм (идеально — ноль);
- rollback в случае фейла;
- деплой без дергания балансировщика вручную.
Что мы решили использовать
Мы работаем в Docker. У нас есть docker-compose и CI (GitHub Actions). Решили не прыгать в Kubernetes — всё-таки это не кластер.
Итого:
- Docker + docker-compose;
- Nginx как реверс-прокси;
- два backend-сервиса (актуальный и обновляемый);
- GitHub Actions — CI/CD pipeline.
Паттерн — классический Blue/Green deployment:
Архитектура
Упрощённо:
Конфиг Nginx (упрощён):
Чтобы переключиться: меняем приоритет в upstream, пересобираем Nginx, он переключает трафик.
CI/CD pipeline
GitHub Actions делает всё:
- Билдит docker-образ;
- Тегирует как green;
- Деплоит app_green;
- Прогоняет health-check;
- Меняет конфиг Nginx (переключает на green);
- Рестартит Nginx;
- Убивает app_blue.
Пример deploy.yml:
Подводные камни
1. Медленный healthcheck
curl /healthz может сработать, а сервис ещё не прогрет. Решили:
- добавить sleep 10;
- проверять ответ на бизнес-метрику (не просто 200, а status: ok).
2. Nginx не сразу переключает
После docker restart Nginx может держать кеш. Помогло:
- proxy_cache_bypass в конфиг;
- resolver прописать явно, если имена контейнеров меняются.
3. Сбои при одновременном деплое
CI мог запускаться параллельно. Добавили concurrency в GitHub Actions:
Что получилось
Теперь при каждом пуше в main:
- поднимается green;
- проверяется /healthz;
- Nginx переключается;
- blue удаляется.
Деплой занимает 30–45 секунд. Ни одного 502 за 2 недели.
Советы
- Делай health-check настоящим: не просто 200, а с логикой.
- Всегда держи старый backend до конца.
- Автоматизируй rollback (в проде всё ломается).
- Не забудь про concurrency в CI.
Что дальше
Следующий шаг — Canary: будем прокидывать 10% трафика на новую версию и следить за ошибками.
А пока — zero-downtime работает. Даже ночью. Даже на проде.
Хочешь попробовать деплой или поработать с Nginx в боевых условиях — заходи на GigaDevOps Platform. Там можно развернуть контейнер, отловить баг и сделать rollout — прямо в браузере.
Также присоединяйтесь к сообществу DevOps-инженеров в Telegram, чтобы погружаться в профессию в удобном формате.