Чем SIGTERM отличается от SIGKILL и как правильно завершать сервис?
- Что такое graceful shutdown?
- Что происходит с приложением при остановке контейнера?
- Что такое сигналы в Unix?
SIGTERM — вежливая просьба завершиться: процесс может её перехватить и закончить работу. SIGKILL — убийство ядром, перехватить нельзя.
- Graceful shutdown: перестать принимать новые запросы, доделать текущие, закрыть соединения
- Docker и Kubernetes шлют SIGTERM, через срок (10 с / 30 с) — SIGKILL
- Процесс с PID 1 в контейнере без обработчика SIGTERM не завершится сам
А ещё Kubernetes: снятие пода из балансировки и SIGTERM идут параллельно — нужен preStop
Подробно
Сигнал — асинхронное уведомление процессу от ядра или другого процесса. Процесс может перехватить большинство сигналов своим обработчиком, игнорировать или оставить действие по умолчанию.
Частые сигналы:
SIGTERM(15) — запрос на завершение, по умолчанию его шлётkill;SIGINT(2) — Ctrl+C в терминале;SIGKILL(9) — немедленное завершение, перехватить нельзя; ресурсы освобождает ядро, но приложение ничего не успевает;SIGHUP— потеря терминала; многие демоны перечитывают по нему конфиг;SIGSEGV— недопустимый доступ к памяти;SIGCHLD— дочерний процесс завершился.
Graceful shutdown — корректная остановка без потерь:
- перестать принимать новые соединения и сообщить балансировщику, что экземпляр уходит (readiness = false);
- дождаться завершения запросов в работе, с таймаутом;
- закоммитить смещения в Kafka, вернуть сообщения в очередь, дописать буферы логов;
- закрыть пулы соединений к базам;
- выйти.
В Spring Boot это server.shutdown=graceful, в Go — http.Server.Shutdown, в Python — обработчик signal.SIGTERM.
Контейнеры. docker stop шлёт SIGTERM главному процессу и через 10 секунд SIGKILL; Kubernetes — через terminationGracePeriodSeconds (30 с). Ловушки:
- PID 1. Ядро не применяет действие по умолчанию к PID 1, поэтому процесс без обработчика игнорирует SIGTERM и умирает только от SIGKILL через таймаут. А если запустить через
sh -c, сигнал получит оболочка, а не приложение. Решение — exec-формаENTRYPOINT ["java", ...],execв скрипте или минимальный init (tini,--init); - гонка с балансировкой. Kubernetes одновременно шлёт SIGTERM и убирает под из Endpoints, а прокси узнают об этом с задержкой. Если сразу перестать принимать соединения, часть запросов получит ошибку. Добавляют
preStopс паузой в несколько секунд.
Пример
import signal, threading
stopping = threading.Event()
signal.signal(signal.SIGTERM, lambda *_: stopping.set())
while not stopping.is_set():
handle_next_task(timeout=1) # берём задачи, пока не попросили остановиться
finish_in_flight() # доделываем начатое и выходимЧто хотят услышать
- Перехватываемый SIGTERM против неперехватываемого SIGKILL.
- Шаги graceful shutdown.
- Поведение Docker/Kubernetes: SIGTERM, грейс-период, SIGKILL.
- PID 1 и exec-форма запуска, preStop — плюс.
Частые ошибки
- Использовать
kill -9как обычный способ остановки. - Запускать приложение через shell-обёртку, которая не передаёт сигнал.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗SIGKILL можно перехватить и корректно завершиться: приложение доделывает запросы и закрывает соединения, а SIGTERM — мгновенное убийство ядром без обработки
- ✓SIGTERM — просьба завершиться, её можно обработать: перестать принимать запросы, доделать текущие, закрыть соединения; SIGKILL перехватить нельзя
- ✗Kubernetes сразу шлёт SIGKILL
- ✗Процесс с PID 1 в контейнере всегда корректно обрабатывает SIGTERM
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.