PurpleSchool — курсы программирования онлайн
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развития
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • Карьерные пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюс
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • PurpleSchool — курсы программирования онлайн
    • Сообщество
    • PurpleПлюс
    • AI тренажёр
    • Проекты
    Главная
    Сообщество
    Graceful shutdown, health checks и zero-downtime deploy: чек-лист для продакшна

    Graceful shutdown, health checks и zero-downtime deploy: чек-лист для продакшна

    Аватар автора Graceful shutdown, health checks и zero-downtime deploy: чек-лист для продакшна

    Антон Ларичев

    Иконка календаря03 апреля 2026
    devopsnode.jsdockerseniorИконка уровня senior

    Введение

    Graceful shutdown в Node.js — это механизм корректного завершения приложения, при котором ни один пользовательский запрос не теряется. В продакшне, где каждый потерянный запрос — это потенциально потерянный клиент, правильная реализация graceful shutdown критически важна. В сочетании с health checks и стратегией zero-downtime deploy вы получаете систему, которая обновляется без простоя.

    В этой статье разберём практический чек-лист: от обработки сигналов завершения до настройки readiness probe в Kubernetes. Все примеры — на Node.js с Docker и Kubernetes.

    Как реализовать graceful shutdown в Node.js

    При получении сигнала SIGTERM (который отправляет Docker или Kubernetes при остановке контейнера) приложение должно выполнить три действия: прекратить приём новых соединений, дождаться завершения текущих запросов и закрыть внешние подключения.

    import { createServer } from 'http';
    
    const server = createServer((req, res) => {
      // Обработка запроса
      res.writeHead(200);
      res.end('OK');
    });
    
    let isShuttingDown = false;
    
    // Обработчик graceful shutdown
    function shutdown(signal: string) {
      console.log(`Получен сигнал ${signal}, начинаем завершение...`);
      isShuttingDown = true;
    
      // Прекращаем приём новых соединений
      server.close(() => {
        console.log('Все соединения закрыты, завершаем процесс');
        process.exit(0);
      });
    
      // Принудительное завершение через таймаут
      setTimeout(() => {
        console.error('Таймаут graceful shutdown, принудительное завершение');
        process.exit(1);
      }, 10000);
    }
    
    process.on('SIGTERM', () => shutdown('SIGTERM'));
    process.on('SIGINT', () => shutdown('SIGINT'));
    
    server.listen(3000);
    

    Как закрыть соединения с базой данных и Redis при завершении

    Одного server.close() недостаточно. Нужно закрыть все внешние ресурсы — пулы соединений с базой данных, Redis-клиенты, очереди сообщений:

    import { Pool } from 'pg';
    import { createClient } from 'redis';
    
    const pgPool = new Pool({ connectionString: process.env.DATABASE_URL });
    const redis = createClient({ url: process.env.REDIS_URL });
    
    async function shutdown(signal: string) {
      console.log(`Получен ${signal}, начинаем drain...`);
      isShuttingDown = true;
    
      server.close(async () => {
        try {
          // Закрываем пул PostgreSQL
          await pgPool.end();
          console.log('PostgreSQL пул закрыт');
    
          // Закрываем Redis
          await redis.quit();
          console.log('Redis соединение закрыто');
    
          process.exit(0);
        } catch (err) {
          console.error('Ошибка при закрытии ресурсов:', err);
          process.exit(1);
        }
      });
    
      setTimeout(() => process.exit(1), 15000);
    }
    

    Настройка health checks для Docker и Kubernetes

    Health checks — это механизм, который сообщает оркестратору о состоянии приложения. В Kubernetes используются три типа проб: liveness probe, readiness probe и startup probe.

    Эндпоинт health check в приложении

    import express from 'express';
    
    const app = express();
    let isShuttingDown = false;
    let isReady = false;
    
    // Liveness — приложение живо
    app.get('/health/live', (req, res) => {
      res.status(200).json({ status: 'alive' });
    });
    
    // Readiness — приложение готово принимать трафик
    app.get('/health/ready', (req, res) => {
      if (isShuttingDown || !isReady) {
        return res.status(503).json({ status: 'not ready' });
      }
      res.status(200).json({ status: 'ready' });
    });
    
    // Отмечаем готовность после инициализации
    async function bootstrap() {
      await pgPool.connect(); // Проверяем соединение с БД
      await redis.connect();
      isReady = true;
      console.log('Приложение готово к приёму трафика');
    }
    

    Readiness probe в Kubernetes: как настроить правильно

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: api-server
    spec:
      replicas: 3
      template:
        spec:
          containers:
            - name: api
              image: myapp:latest
              ports:
                - containerPort: 3000
              livenessProbe:
                httpGet:
                  path: /health/live
                  port: 3000
                initialDelaySeconds: 5
                periodSeconds: 10
                failureThreshold: 3
              readinessProbe:
                httpGet:
                  path: /health/ready
                  port: 3000
                initialDelaySeconds: 3
                periodSeconds: 5
                failureThreshold: 2
              startupProbe:
                httpGet:
                  path: /health/live
                  port: 3000
                failureThreshold: 30
                periodSeconds: 2
          terminationGracePeriodSeconds: 30
    

    Ключевые параметры: readinessProbe с failureThreshold: 2 быстро выводит под из балансировки, а terminationGracePeriodSeconds задаёт максимальное время для graceful shutdown.

    Чек-лист zero-downtime deploy для продакшна

    Zero-downtime deploy — это стратегия обновления, при которой пользователи не замечают процесс деплоя. Вот полный чек-лист:

    Приложение

    1. Обрабатывает SIGTERM и выполняет graceful shutdown
    2. Отдаёт 503 на readiness probe при завершении
    3. Закрывает все соединения (БД, Redis, очереди)
    4. Имеет таймаут принудительного завершения
    5. Эндпоинты /health/live и /health/ready реализованы

    Docker

    FROM node:20-alpine
    
    WORKDIR /app
    COPY package*.json ./
    RUN npm ci --only=production
    COPY dist/ ./dist/
    
    # Используем tini для корректной передачи сигналов
    RUN apk add --no-cache tini
    ENTRYPOINT ["/sbin/tini", "--"]
    
    HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
      CMD wget -qO- http://localhost:3000/health/live || exit 1
    
    CMD ["node", "dist/main.js"]
    

    Важный момент: без init-процесса (tini или --init в docker run) Node.js не получит SIGTERM, и контейнер будет убит через SIGKILL после таймаута.

    Kubernetes

    spec:
      strategy:
        type: RollingUpdate
        rollingUpdate:
          maxUnavailable: 0
          maxSurge: 1
      template:
        spec:
          containers:
            - name: api
              lifecycle:
                preStop:
                  exec:
                    command: ["sh", "-c", "sleep 5"]
    

    Параметр maxUnavailable: 0 гарантирует, что ни один под не будет удалён, пока новый не пройдёт readiness probe. А preStop hook с задержкой даёт время на распространение изменений в endpoint-ах кластера.

    Частые ошибки при настройке безостановочного деплоя

    Нет обработки SIGTERM. Приложение не слушает сигналы — контейнер убивается через SIGKILL через 30 секунд, все текущие запросы теряются.

    Liveness вместо readiness. Если использовать только liveness probe без readiness, Kubernetes не знает, готов ли под к трафику. Новый под начинает получать запросы до полной инициализации.

    Слишком короткий terminationGracePeriodSeconds. Если у вас есть длительные запросы (загрузка файлов, генерация отчётов), 30 секунд по умолчанию может не хватить. Увеличьте до 60-120 секунд.

    Отсутствие preStop hook. Без задержки перед завершением Kubernetes может отправлять трафик на под, который уже начал завершаться. Добавьте sleep 5 в preStop.

    Node.js как PID 1 в Docker. Без tini или --init Node.js запускается как PID 1 и не обрабатывает сигналы стандартным образом.

    Заключение

    Graceful shutdown, health checks и zero-downtime deploy — это три столпа надёжного продакшна. Реализация graceful shutdown в Node.js требует обработки SIGTERM, закрытия соединений и таймаута. Health checks через liveness и readiness probe дают оркестратору полную картину состояния приложения. А правильная стратегия rolling update с maxUnavailable: 0 и preStop hook обеспечивает безостановочный деплой.

    Используйте чек-лист из этой статьи перед каждым выводом сервиса в продакшн — это сэкономит часы отладки и предотвратит потерю пользовательских запросов.

    Иконка глаза13

    Комментарии

    0

    Постройте личный план изучения Основы разработки до уровня Middle — бесплатно!

    Основы разработки — часть карты развития Frontend, Backend, Mobile

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    Основы Git

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.9
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    HTML и CSS

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.9
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Neovim

    Антон Ларичев
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее

    Похожие статьи

    Картинка поста FastAPI vs NestJS: какой бэкенд-фреймворк выбрать в 2026
    Иконка аватараАнтон
    Иконка календаря31 марта 2026
    pythonnestjstypescriptmiddleИконка уровня middle

    FastAPI vs NestJS: какой бэкенд-фреймворк выбрать в 2026

    Сравнение FastAPI и NestJS — двух популярных бэкенд-фреймворков для разработки API. Разбираем производительность, архитектуру, экосистему и помогаем выбрать подходящий инструмент для вашего проекта в 2026 году.

    Иконка чипа0
    Иконка глаза115
    Иконка комментариев0
    Картинка поста NestJS: как организовать модули, чтобы проект не стал монолитом
    Иконка аватараАнтон
    Иконка календаря28 марта 2026
    nestjstypescriptarchitecturemiddleИконка уровня middle

    NestJS: как организовать модули, чтобы проект не стал монолитом

    Организация модулей в NestJS определяет масштабируемость проекта. Разберем принципы модульной архитектуры, разделение по доменам и типичные ошибки, превращающие проект в монолит.

    Иконка чипа0
    Иконка глаза170
    Иконка комментариев0
    Картинка поста Оптимизация LCP: как я довёл Web Vitals до 1.5 секунд на реальном проекте
    Иконка аватараАнтон
    Иконка календаря27 марта 2026
    performancehtmlmiddleИконка уровня middle

    Оптимизация LCP: как я довёл Web Vitals до 1.5 секунд на реальном проекте

    Пошаговое руководство по оптимизации Largest Contentful Paint (LCP): от 4.8 до 1.5 секунд на реальном проекте. Изображения, шрифты, CSS, серверная оптимизация и конкретные примеры кода.

    Иконка чипа0
    Иконка глаза169
    Иконка комментариев0
    Иконка чипа0