Как работает graceful shutdown в backend-приложениях
В продакшене backend-приложения регулярно перезапускаются: деплой новой версии, автоскейлинг, обновления системы или аварийные рестарты. Если в этот момент приложение просто завершается, оно может оборвать активные запросы, потерять данные или оставить систему в неконсистентном состоянии.
Для решения этой проблемы используется graceful shutdown — корректное завершение работы сервиса.
Что такое graceful shutdown
Graceful shutdown — это процесс управляемого завершения приложения, при котором оно:
- перестаёт принимать новые запросы;
- корректно обрабатывает текущие;
- освобождает ресурсы;
- завершает работу без потери данных.
В отличие от «жёсткого» завершения (kill -9), graceful shutdown даёт приложению время привести себя в порядок.
Как ОС сообщает приложению о завершении
В Linux завершение сервиса обычно начинается с отправки сигналов:
- SIGTERM — стандартный сигнал на корректное завершение;
- SIGINT — прерывание (например, Ctrl+C);
- SIGKILL — немедленное завершение (не перехватывается).
Системы оркестрации (systemd, Docker, Kubernetes) сначала отправляют SIGTERM и ждут заданное время. Если приложение не завершилось — используется SIGKILL.
Graceful shutdown строится вокруг обработки SIGTERM/SIGINT.
Общий алгоритм graceful shutdown
Независимо от языка, логика примерно одинаковая:
- Получить сигнал завершения.
- Перестать принимать новые соединения.
- Дать текущим запросам завершиться.
- Закрыть соединения с БД, очередями, внешними сервисами.
- Завершить процесс.
Важно, чтобы этот процесс был ограничен по времени, иначе сервис может «зависнуть» при выключении.
Graceful shutdown в Node.js
В Node.js сигналы можно перехватывать через process.on:
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);Типичный пример с HTTP-сервером:
const http = require('http');
const server = http.createServer((req, res) => {
setTimeout(() => {
res.end('ok');
}, 1000);
});
server.listen(3000);
function shutdown() {
console.log('Shutting down...');
server.close(() => {
console.log('HTTP server closed');
process.exit(0);
});
setTimeout(() => {
console.error('Force shutdown');
process.exit(1);
}, 10000);
}Что происходит:
server.close()перестаёт принимать новые соединения;- активные запросы завершаются;
- по таймауту происходит принудительное завершение.
Важно помнить, что Node.js — однопоточный, и блокирующие операции могут сорвать graceful shutdown.
Работа с базами данных и очередями в Node.js
При завершении приложения нужно явно закрывать ресурсы:
Пример:
await db.close();
await redis.quit();Если этого не сделать, процесс может не завершиться или потерять данные.
Graceful shutdown в Go
В Go стандартная библиотека предоставляет удобные инструменты для этого.
Перехват сигналов:
ctx, stop := signal.NotifyContext(
context.Background(),
os.Interrupt,
syscall.SIGTERM,
)
defer stop()Пример с HTTP-сервером:
server := &http.Server{
Addr: ":8080",
Handler: handler,
}
go server.ListenAndServe()
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
server.Shutdown(shutdownCtx)Метод Shutdown:
- прекращает приём новых соединений;
- ждёт завершения активных запросов;
- корректно закрывает сервер.
Это один из примеров, где Go даёт более безопасный и выразительный API «из коробки».
Контексты и отмена операций в Go
Ключевая идея graceful shutdown в Go — контексты.
Любая долгоживущая операция должна принимать context.Context и корректно реагировать на его отмену:
select {
case <-ctx.Done():
return ctx.Err()
default:
// работа
}Без этого сервер может зависнуть при завершении.
Таймауты — обязательная часть
Graceful shutdown не должен длиться бесконечно.
Хорошая практика:
- 5–10 секунд для HTTP-сервисов;
- меньше для фоновых воркеров;
- больше — только для критичных batch-задач.
Таймаут защищает систему от «залипших» соединений и некорректного кода.
Частые ошибки
На практике часто встречаются такие проблемы:
- SIGTERM не обрабатывается вообще;
- сервер продолжает принимать новые запросы;
- нет таймаута завершения;
- не закрываются соединения с БД;
- фоновые горутины или таймеры продолжают работать.
Все эти ошибки проявляются только в продакшене — при деплоях и масштабировании.
Итоги
Graceful shutdown — обязательный элемент надёжного backend-приложения.
Ключевые моменты:
- всегда обрабатывайте SIGTERM и SIGINT;
- прекращайте приём новых запросов;
- завершайте активные операции корректно;
- используйте таймауты;
- тестируйте shutdown так же, как и обычные сценарии.
Корректный graceful shutdown снижает количество ошибок при деплоях, улучшает стабильность системы и делает сервисы готовыми к реальной эксплуатации.
Настроить мониторинг за 30 секунд
Надежные оповещения о даунтаймах. Без ложных срабатываний