АйТи Фреш
Главная / Статьи / DevOps и автоматизация
DevOps и автоматизация

systemctl stop отработал, а порт занят: разбираемся с KillMode и сигналами systemd

Автор: Семёнов Евгений Сергеевич, директор ООО «АйТи-Фреш» · · ~24 мин чтения
systemctl stop отработал, а порт занят: разбираемся с KillMode и сигналами systemd
Иллюстрация к статье «systemctl stop отработал, а порт занят: разбираемся с KillMode и сигналами systemd».

Знакомая картина: `systemctl stop` отработал молча, `systemctl status` рисует inactive (dead), а следующий `start` падает с «Address already in use». Или хуже — новая версия сервиса поднялась, а рядом продолжает жить старая: разгребает ту же очередь, пишет в ту же базу, и в отчётах у клиента цифры вперемешку. В девяти случаях из десяти виноват KillMode= — либо он выставлен в process, либо systemd просто не считает главным тот процесс, который вы думаете. Разбираю, что реально делает каждое из четырёх значений, как за минуту доказать, что процессы остались в cgroup службы, какие тайм-ауты и сигналы за этим стоят, и какой набор директив я по умолчанию кладу в юнит клиентского сервиса.

«Служба остановлена» — а кто тогда держит порт

Первое, от чего надо отучиться, — верить строчке в статусе. systemctl status показывает состояние ЮНИТА, а не состояние контрольной группы. «inactive (dead)» означает ровно одно: systemd считает работу законченной — главный процесс вышел, команды ExecStop= отработали, тайм-ауты не сработали. Живут ли при этом ещё пятнадцать процессов в cgroup службы, менеджер вам в этой строчке не скажет. При KillMode=process он вообще не обязан ими интересоваться.

Поэтому диагностику я начинаю не с чтения файла юнита, а с трёх команд. Они занимают полминуты и сразу разводят два принципиально разных диагноза.

# 1. Кто РЕАЛЬНО слушает порт (PID и имя процесса)
ss -ltnp 'sport = :8080'

# 2. Что осталось в контрольной группе службы (cgroup v2)
systemd-cgls -u bindery-api.service
cat /sys/fs/cgroup/system.slice/bindery-api.service/cgroup.procs

# 3. Как юнит настроен ПО ФАКТУ, а не как написано в .service-файле
systemctl show bindery-api.service \
  -p Type -p KillMode -p KillSignal -p SendSIGKILL -p FinalKillSignal \
  -p TimeoutStopUSec -p PIDFile -p ControlGroup

Дальше развилка. Если ss показал живой PID — берём его и смотрим cat /proc/<PID>/cgroup: если там путь вида /system.slice/bindery-api.service, поздравляю, это ваш беглец, и дальше по статье. А вот если ss -ltnp не показывает вообще никого, а bind всё равно падает — это НЕ висящие воркеры. Это либо сокет в TIME_WAIT и приложение не выставляет SO_REUSEADDR, либо порт занял совсем другой юнит (проверьте .socket-юниты), либо вы смотрите не тот сетевой namespace. В KillMode тут лезть незачем — сэкономите себе вечер.

inactive (dead) — это про юнит, а не про процессы. Пока файл cgroup.procs не пуст, у вас в проде работает старый код.

Четыре значения KillMode и что каждое реально делает

**control-group** — значение по умолчанию, и в 95 % случаев правильное. После того как отработали команды ExecStop=, все оставшиеся процессы контрольной группы юнита получают сигнал из KillSignal= (по умолчанию SIGTERM). Дальше, если SendSIGKILL=yes — а он по умолчанию именно yes, — по истечении TimeoutStopSec= выжившим прилетает FinalKillSignal= (по умолчанию SIGKILL). То есть из коробки systemd гарантированно вычищает cgroup. Если у вас что-то переживает стоп при control-group — почти наверняка кто-то трогал тайм-ауты или SendSIGKILL.

**mixed** — компромисс для демонов, которые сами умеют корректно гасить своих детей. Первый сигнал (KillSignal=, по умолчанию SIGTERM) уходит только главному процессу, а SIGKILL (или FinalKillSignal=) — всем оставшимся в cgroup. Важная деталь, которую часто упускают: по man systemd.kill(5) добивание при mixed происходит в двух случаях — когда главный процесс вышел ИЛИ когда истёк TimeoutStopSec=, смотря что наступит раньше. То есть если мастер вышел, не дождавшись воркеров, те получат SIGKILL сразу, без всякого тайм-аута. Поэтому mixed подходит только демонам, у которых мастер завершается последним, после своих детей. Это не экзотика: в upstream-юнитах systemd с KillMode=mixed идут systemd-udevd.service и user@.service.

**process** — гасится только главный процесс, потомки не трогаются вообще. Man-страница systemd.kill(5) комментирует это значение прямо в скобках: «(not recommended!)». **none** — не убивается никто, выполняется только ExecStop=, и там формулировка ещё жёстче: «(strongly recommended against!)». Общее обоснование в документации одно на оба режима: процессы «сбегают» из-под управления жизненным циклом и ресурсами менеджера служб и продолжают работать, пока служба считается остановленной. Со всеми вытекающими: MemoryMax= перестаёт быть гарантией, при выключении машины эти процессы никто корректно не остановит, а systemctl restart перестаёт быть атомарной операцией.

Про предупреждения в журнале — тут много путаницы, поэтому по исходникам. Начиная с systemd 246 при загрузке юнита с KillMode=none в журнал пишется предупреждение: в 246–251 оно начиналось со слов «Unit configured to use KillMode=none», с 252 — «Unit uses KillMode=none. This is unsafe…», и заканчивается фразой «Support for KillMode=none is deprecated and will eventually be removed». Для KillMode=process такого предупреждения нет ни в одной версии — только пометка «not recommended!» в документации, поэтому этот режим годами живёт в юнитах незамеченным. Второй сигнал, который стоит знать в лицо, появился в той же 246-й версии: при старте юнита, в cgroup которого остались процессы прошлого запуска, systemd пишет Found left-over process <PID> (<имя>) in control group while starting unit. Ignoring. Увидели эту строку в journalctl -u <unit> — у вас хвосты, и KillMode первым под подозрением.

Резонный вопрос: если process такой плохой, почему он есть в дистрибутивных юнитах? Есть. В Debian и Ubuntu KillMode=process стоит в пакетных ssh.service и cron.service, а в upstream-поставке systemd — в debug-shell.service. У каждого причина осознанная и специфическая: рестарт sshd не должен рубить вашу текущую SSH-сессию, запущенные cron-джобы должны досчитаться до конца, отладочная консоль не должна умирать вместе с юнитом. Беда в том, что админ видит эту строчку в системном юните, копирует к себе «чтобы рестарт был помягче» — и получает зоопарк осиротевших воркеров.

KillMode=none в 2026 году — это не «тонкая настройка», а способ гарантированно потерять контроль над сервисом. Если очень хочется, сначала объясните себе вслух, кто будет останавливать эти процессы при reboot.
systemctl stop отработал, а порт занят: разбираемся с KillMode и сигналами systemd — схема
Схема к статье. Открыть схему в полном размере

Разбор из практики: переплётная мастерская на 48 мест и очередь заказов вперемешку

Переплётная мастерская «Книга в обложке»: 48 рабочих мест — менеджеры приёма, цех переплёта и тиснения, бухгалтерия, мы ведём инфраструктуру целиком. На виртуалке с Debian 12 (systemd 252) живёт внутренний сервис bindery-api: Python/FastAPI под gunicorn с uvicorn-воркерами, четыре воркера, слушает 127.0.0.1:8080, наружу выставлен через nginx. Задача сервиса — забирать заказы из 1С и формировать сменные задания для цеха: какие тиражи шить, какие обложки тиснить, в каком порядке. Писал его подрядчик, деплой — ansible-плейбук с systemctl restart.

Симптом, с которым пришли: «после обновления в сменном задании иногда старые позиции». Иногда — это ключевое слово, из-за него проблему несколько месяцев списывали на кэш браузера на планшете мастера цеха. Плюс примерно каждый третий деплой падал на Address already in use, и плейбук просто прогоняли повторно. Юнит выглядел так:

[Unit]
Description=Bindery orders API
After=network-online.target

[Service]
Type=simple
User=bindery
WorkingDirectory=/opt/bindery/api
ExecStart=/bin/bash /opt/bindery/api/run.sh
KillMode=process
Restart=always

[Install]
WantedBy=multi-user.target

А run.sh — вот такой, без exec:

#!/bin/bash
source /opt/bindery/api/.env
/opt/bindery/venv/bin/gunicorn -w 4 -k uvicorn.workers.UvicornWorker \
  -b 127.0.0.1:8080 app.main:app

Здесь два греха накладываются друг на друга и дают ровно тот эффект, который мы видели. Первый: главный процесс службы — это bash, а не gunicorn, потому что в скрипте нет exec. Второй: KillMode=process гасит только главный процесс, то есть этот самый bash. Мастер gunicorn и четыре воркера остаются в cgroup живыми. Systemd честно рапортует «остановлено», ansible тут же запускает новый экземпляр — и дальше как повезёт: если старый мастер ещё держит 8080, новый падает с EADDRINUSE; если по времени успел отпустить (или его прибили руками) — новый поднимается и работает бок о бок со старым.

Замерили. После обычного systemctl stop bindery-api в /sys/fs/cgroup/system.slice/bindery-api.service/cgroup.procs оставалось пять PID: мастер плюс четыре воркера. А в journalctl -u bindery-api при каждом старте висели строки Found left-over process … (gunicorn) in control group while starting unit. Ignoring. — systemd честно предупреждал, просто журнал никто не читал. За две недели накопилось три поколения — пятнадцать процессов, суммарно около 2 ГБ RSS. И главное: старые мастера продолжали брать задачи из общей очереди Redis и перезаписывать свежие сменные задания старой логикой расчёта. Вот вам и «иногда старые позиции» — никакого кэша, просто три версии кода одновременно пишут в одну базу.

Переписали юнит вот так, без промежуточного bash вообще:

[Service]
Type=simple
User=bindery
EnvironmentFile=/opt/bindery/api/.env
WorkingDirectory=/opt/bindery/api
ExecStart=/opt/bindery/venv/bin/gunicorn -w 4 \
  -k uvicorn.workers.UvicornWorker -b 127.0.0.1:8080 app.main:app
KillMode=mixed
KillSignal=SIGTERM
TimeoutStopSec=45
SendSIGKILL=yes
Restart=on-failure
RestartSec=3

Логика простая. Переменные окружения переехали из source .env в EnvironmentFile=, поэтому обёртка стала не нужна и главным процессом стал сам gunicorn. KillMode=mixed: мастер получает SIGTERM и гасит воркеров своим штатным механизмом — даёт им дообработать текущий запрос в пределах graceful timeout (у gunicorn по умолчанию 30 секунд) и выходит последним. Как только мастер вышел, всё, что осталось в cgroup, systemd сразу добивает SIGKILL; если мастер завис — добивание наступит по TimeoutStopSec=45, поэтому этот тайм-аут и выбран заметно больше graceful timeout gunicorn. Restart=always заменили на on-failure, чтобы сбой после ручных манипуляций не поднимал сервис неожиданно. Итог: остановка вместо «висит или не завершается вовсе» занимает 2–6 секунд, cgroup после stop пустая, дубли в сменных заданиях пропали. В плейбук добавили дешёвую проверку, которая ловит регресс сразу, а не через две недели:

systemctl stop bindery-api
test ! -s /sys/fs/cgroup/system.slice/bindery-api.service/cgroup.procs \
  || { echo 'FATAL: в cgroup остались процессы'; exit 1; }
systemctl start bindery-api
bash-обёртка без exec — самая частая причина «systemd не видит мой сервис». Либо `exec` последней строкой скрипта, либо запускайте бинарь из ExecStart= напрямую.
Цифры и версии: Разбор из практики: переплётная мастерская на 48 мест и очередь заказов вперемешку — схема
Цифры и версии: Разбор из практики: переплётная мастерская на 48 мест и очередь заказов вперемешку. Открыть схему в полном размере

Не только KillMode: три способа потерять потомков

**Обёртка без exec.** Разобрали выше, но повторю, потому что это правда номер один. Когда ExecStart= запускает шелл-скрипт, главный процесс службы — это shell. С KillMode=control-group это не смертельно: потомки всё равно в cgroup и умрут. Но главный PID всё равно неверный, а от него зависит многое — переменная $MAINPID в ExecReload=, реакция Restart=on-failure (systemd смотрит код возврата шелла, а не приложения), watchdog через sd_notify. Лечится одной строчкой: exec /path/to/binary ... в конце скрипта.

**Type=forking и кривой PIDFile.** Демон форкается, родитель выходит, systemd должен понять, кто теперь главный. GuessMainPID= по умолчанию yes, но эта догадка игнорируется, если задан PIDFile=. И если PID-файл пишется с задержкой, не тем пользователем или в путь, который systemd не читает — менеджер шлёт сигналы в никуда, ExecStop= уходит впустую, а TimeoutStopSec= честно отрабатывает целиком. Мой порядок предпочтений: Type=notify, если приложение умеет sd_notify; иначе Type=simple/exec и не форкаться вовсе; Type=forking — только для чужого софта, который иначе не умеет.

**ExecStop=/bin/kill -TERM $MAINPID.** Популярный копипаст, который ровно ничего не улучшает: без ExecStop= systemd и так отправит KillSignal= главному процессу. Хуже того, man systemd.service(5) прямо говорит, что после выполнения команд ExecStop= служба считается остановленной, а оставшиеся процессы сразу завершаются согласно KillMode= и KillSignal=. И там же отдельное предупреждение: команда, которая лишь просит сервис завершиться, но не ждёт этого, обычно недостаточна — ExecStop= должен быть синхронным. То есть ваш kill вернул ноль — systemd тут же пошёл по KillMode=, хотя приложение ещё и не начинало выходить. И отдельно: TimeoutStopSec= ограничивает и время каждой команды ExecStop= (при превышении остальные пропускаются и службе уходит SIGTERM), и ожидание завершения самой службы, так что длинный graceful drain в ExecStop= надо соотносить с этим значением.

**Уход из-под юнита.** Если в ExecStartPost= или внутри приложения кто-то делает setsid, nohup & или systemd-run, процесс уезжает в другую cgroup — и он больше не ваш ни при каком KillMode. Это не баг systemd, это ровно то, о чём говорит документация словом «escape». А вот вложенные подгруппы внутри собственной cgroup службы (типичная история для контейнерных рантаймов с Delegate=yes) при control-group накрываются целиком — здесь бояться нечего.

Правки в .service без `systemctl daemon-reload` не применяются, но и ошибки не выдают. Проверяйте не файл, а вывод `systemctl show <unit> -p KillMode` — он показывает то, что systemd реально держит в памяти.

Тайм-ауты и сигналы: где stop висит вечно и как добить руками

Из коробки systemd не висит на остановке вечно: SendSIGKILL=yes, FinalKillSignal=SIGKILL, а DefaultTimeoutStopSec=90s — встроенное умолчание, которое в system.conf показано закомментированной строкой. Вечно висящий systemctl stop получается прежде всего из TimeoutStopSec=infinity: тайм-аут отключён, значит и SIGKILL не наступит никогда, если процесс игнорирует SIGTERM. SendSIGKILL=no ломает иначе: по истечении тайм-аута systemd сдаётся, но процессы остаются в cgroup, и, как предупреждает man systemd.kill(5), служба с control-group или mixed потом не перезапустится, пока там живут процессы прошлого запуска. Отдельный случай — процесс в состоянии D (непрерываемое ожидание ввода-вывода, чаще всего зависший NFS или умирающий диск): его не берёт даже SIGKILL, и тут никакая директива не поможет. Поэтому, когда у клиента стоп висит минутами, я первым делом смотрю не в код приложения, а в systemctl show <unit> -p SendSIGKILL -p TimeoutStopUSec и в ps -o pid,stat,cmd по PID из cgroup.

Дефолтные 90 секунд — величина для десктопа. На сервере это 90 секунд простоя на каждый залипший юнит при shutdown, и если таких юнитов пять, перезагрузка превращается в семь с половиной минут молчания. Я ставлю значения осознанно: веб-воркерам 30–45 секунд (успеть дообработать входящие запросы и не больше), обработчикам очередей 120–180 секунд (задача может быть длинной, обрывать дороже), СУБД — строго по регламенту вендора и точно не меньше. И никогда не ставлю 5 секунд «чтобы быстрее»: это гарантированный SIGKILL посреди записи.

Когда надо вычистить остатки прямо сейчас, руками, не выясняя причину — есть штатный инструмент, который бьёт по всей контрольной группе, а не только по главному процессу:

# мягко — всем процессам cgroup службы
systemctl kill --kill-whom=all --signal=SIGTERM bindery-api.service
sleep 5
# жёстко, если кто-то остался
systemctl kill --kill-whom=all --signal=SIGKILL bindery-api.service

# проверяем, что вычистили
cat /sys/fs/cgroup/system.slice/bindery-api.service/cgroup.procs

Флаг --kill-whom= появился в systemd 252 (Debian 12 — как раз 252, Ubuntu 24.04 — 255); в более старых сборках, например в systemd 247 из Debian 11, он называется --kill-who=, а в свежих версиях старое имя может уже не приниматься — проверяйте systemctl --help. Кроме all он принимает main, control и cgroup. Если приходится делать это регулярно — вы лечите симптом, а болезнь в юните. Из смежного: SendSIGHUP= по умолчанию no, включать его стоит только для шелл-подобных вещей, которым надо сообщить «связь разорвана»; RestartKillSignal= позволяет при рестарте слать другой сигнал, чем при обычном стопе — редкая, но иногда полезная штука для демонов, у которых graceful-перезапуск висит на отдельном сигнале.

SendSIGKILL=no не делает остановку «бережнее» — он делает её ненадёжной. Если вам нужно больше времени на корректный выход, увеличивайте TimeoutStopSec=, а не отключайте страховку.
Памятка: Тайм-ауты и сигналы: где stop висит вечно и как добить руками — схема
Памятка: Тайм-ауты и сигналы: где stop висит вечно и как добить руками. Открыть схему в полном размере

Когда KillMode=process действительно оправдан

Первый честный случай — sshd. ssh.service в Debian и Ubuntu идёт с KillMode=process именно для того, чтобы systemctl restart ssh не оборвал вашу текущую сессию и вы не заперли себя снаружи удалённого сервера. Сессии живут как потомки, демон перезапускается, вы продолжаете печатать. Ровно та же логика у пакетного cron.service (запущенные джобы досчитываются до конца) и у debug-shell.service. Обратите внимание на общее: во всех случаях потомки — это осмысленные, самостоятельные, конечные по времени сущности, а не воркеры, которые будут вечно висеть на порту.

Второй случай — socket activation. Systemd сам держит слушающий сокет и передаёт его сервису файловым дескриптором; при рестарте сокет никуда не девается, соединения копятся в ядерной очереди, а старый и новый процессы могут какое-то время сосуществовать и разбирать её оба. Это подход, который подробно разобран у Vincent Bernat на примере Go-сервиса, и там KillMode=process — не костыль, а часть конструкции. Минимальный .socket-юнит выглядит так:

# bindery-api.socket
[Unit]
Description=Bindery orders API socket

[Socket]
ListenStream=127.0.0.1:8080
BindIPv6Only=both

[Install]
WantedBy=sockets.target

Главный бонус здесь даже не в нулевом даунтайме: при socket activation ошибка «Address already in use» не возникает в принципе, потому что порт принадлежит systemd, а не приложению. Но честно скажу: для внутренних сервисов компании до 50 рабочих мест это чаще всего переусложнение. Приложение должно уметь sd_listen_fds, деплой усложняется, а выигрыш — доли секунды простоя раз в неделю. Мы ставим socket activation там, где реально нельзя потерять ни одного запроса: платёжные вебхуки, приём заданий с цехового оборудования. Во всех остальных случаях связка KillMode=mixed + вменяемый TimeoutStopSec= + Restart=on-failure закрывает вопрос полностью и понятна любому админу, который придёт после вас.

Скопировать KillMode=process из ssh.service в свой юнит «чтобы рестарт был мягче» — самая дорогая экономия трёх секунд, которую я видел. У sshd для этого есть причина, у вашего воркера — нет.

Мой порядок действий: что чинить первым, на что забить

Порядок, по которому я разбираю такие инциденты у клиентов, устоялся и почти не меняется. Сначала — эффективная конфигурация, а не файл юнита: в /etc/systemd/system/<unit>.d/ вполне может лежать drop-in, о котором никто не помнит, и он перекрывает всё. systemd-analyze cat <unit> покажет полный склеенный конфиг с указанием источников, systemctl show — итоговые значения. Только после этого имеет смысл что-то править.

Дальше иду по списку ниже — и именно в таком порядке, потому что каждый следующий пункт имеет смысл только когда закрыт предыдущий. Менять тайм-ауты, пока главным процессом службы остаётся bash-обёртка, бессмысленно: сигналы всё равно придут не тому процессу.

На что можно спокойно забить. Не нужно подбирать экзотические KillSignal= и FinalKillSignal=, если ваше приложение не документирует особую обработку сигналов — SIGTERM/SIGKILL покрывают почти всё. Не нужно писать ExecStop=/bin/kill: систему это только путает. Не нужно бояться control-group — это дефолт, он правильный, и большинство «мягких» альтернатив ему на самом деле просто ломают учёт ресурсов. И не нужно отключать SendSIGKILL= ни при каких обстоятельствах: страховка, которая срабатывает раз в год, стоит дешевле, чем сервер, который не может перезагрузиться.

И последнее, банальное, но именно с этого начинается половина бесплодных расследований: после правки юнита — systemctl daemon-reload. Без него systemd продолжает жить со старой конфигурацией и молча игнорирует ваши изменения. Проверка — снова systemctl show <unit> -p KillMode: если там всё ещё старое значение, значит вы правите файл, а не систему.

Если после всех правок процессы всё равно переживают stop — почти наверняка они ушли из cgroup через setsid/nohup внутри самого приложения. Это чинится в коде, а не в юните.
Порядок действий: Мой порядок действий: что чинить первым, на что забить — схема
Порядок действий: Мой порядок действий: что чинить первым, на что забить. Открыть схему в полном размере

Частые вопросы

Почему systemctl status показывает inactive (dead), а процессы живы?

Потому что статус описывает состояние юнита, а не содержимое его контрольной группы. Systemd считает службу остановленной, когда вышел главный процесс и отработали команды ExecStop=. При KillMode=process потомков он при этом не трогает вообще. Проверяйте `cat /sys/fs/cgroup/system.slice/<unit>/cgroup.procs` или `systemd-cgls -u <unit>` — вот это честная картина.

Какой KillMode ставить по умолчанию?

Никакой. Не задавайте директиву вовсе — значение по умолчанию control-group правильное для подавляющего большинства сервисов: SIGTERM всей cgroup, потом SIGKILL выжившим. Осознанное исключение — KillMode=mixed для мастер-воркерных демонов (gunicorn, uwsgi, php-fpm), которые сами умеют корректно гасить своих детей.

Можно ли поставить KillMode=none, чтобы сервис пережил обновление systemd?

Не надо. Документация формулирует это как «strongly recommended against»: процессы выходят из-под управления жизненным циклом и учёта ресурсов менеджера. Практические последствия — лимиты вроде MemoryMax= перестают что-либо гарантировать, а при shutdown такие процессы никто не остановит корректно. Для переживания рестартов существует socket activation, а не отключение убийства.

Что значит «Found left-over process … in control group while starting unit» в журнале?

Это сообщение systemd (появилось в версии 246): при старте юнита в его cgroup нашлись процессы прошлого запуска. systemd их не трогает («Ignoring») и запускает новый экземпляр рядом. Почти всегда причина — KillMode=process или none, SendSIGKILL=no либо bash-обёртка без exec. Проверьте `systemctl show <unit> -p KillMode -p SendSIGKILL` и содержимое cgroup.procs.

KillMode=process объявлен устаревшим, как и none?

Нет. Предупреждение о небезопасности и грядущем удалении systemd пишет в журнал только для KillMode=none, начиная с версии 246. KillMode=process в документации помечен «not recommended!», но предупреждения при загрузке юнита не вызывает — поэтому такие юниты годами работают незамеченными. Ищите их сами: `grep -r 'KillMode=' /etc/systemd/system`.

Почему systemctl stop висит ровно 90 секунд?

Это DefaultTimeoutStopSec=90s из system.conf. Значит, приложение не вышло по SIGTERM, и systemd честно ждёт тайм-аут, прежде чем послать SIGKILL. Лечится не уменьшением тайм-аута, а выяснением, почему процесс игнорирует SIGTERM: чаще всего он его просто не обрабатывает, либо главным процессом службы оказался shell-скрипт без exec.

Как быстро убить всё, что осталось от службы, не перезагружая сервер?

`systemctl kill --kill-whom=all --signal=SIGTERM <unit>`, через несколько секунд то же самое с SIGKILL. Опция `--kill-whom=` есть в systemd 252 и новее, в более старых сборках она называлась `--kill-who=`. Это разовое лечение симптома: если приходится делать регулярно, чинить надо юнит.

Правда ли, что KillMode=process нужен для sshd?

Да, и это тот редкий случай, когда режим оправдан: пакетный `ssh.service` в Debian и Ubuntu идёт именно с KillMode=process, чтобы `systemctl restart ssh` не обрывал текущие сессии администратора. Та же логика у cron.service. Но копировать эту строку в юнит своего веб-сервиса нельзя — там потомки не завершаются сами, а вечно висят на порту.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 💬 MAX: +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи