Что можно настроить или включить, чтобы определить, почему systemd предпринял действие «остановить» службу?

То, как демон snmptrapd работает с ловушками, не позволяет получать какие-либо ловушки SNMPv3 без указания EngineID отправляющего устройства, т. е. если вы просто делаете следующее в /etc/snmp/snmptrapd.conf:

createUser snmpv3USER SHA auth_pass AES priv_pass
authUser log,execute snmpv3USER
perl do "/usr/bin/zabbix_trap_receiver.pl";

Вы никогда не получите ловушки SNMPv3 в файле /tmp/zabbix _traps.tmp, потому что usmUser, созданный внутри /var/lib/net -snmp/snmptrapd.conf, не соответствует фактическим требованиям, т.е. это..

Когда дело доходит до сообщений SNMPv3, большинство коммутаторов не могут их отправлять, поэтому мы застряли только с ловушками SNMPv3, а способ программирования snmptrapd работает только с идентификаторами EngineID для ловушек SNMPv3. Даже если я использую disableAuthorization yes в snmptrapd.conf, ловушки SNMPv3 не будут работать без EngineID для каждого отправляющего устройства, то есть устройства-коммутатора.

Теперь, чтобы получать ловушки SNMPv3, вам нужно получить все EngineID для всех коммутаторов, для этого я написал скрипт на Python, чтобы построить snmptrapd.conf с использованием SNMPv3, что является большим облегчением. Фактический файл имеет комментарий перед каждой строкой createUser, который показывает имя коммутатора и IP-адрес. Эта часть информации о ловушках SNMPv3 на самом деле упоминается в документации net -snmp, но когда я прочитал ее в первый раз, я не мог ее понять, потому что там написано The difference is that SNMPv3 TRAPs use the engineID of the local application sending the trap rather than the engineID of the remote application, поэтому, когда вы прочитаете ее в первый раз, вы будете сбиты с толку, но с немного концентрации становится очень ясно:

SNMPv3 TRAPs are a bit more complicated in some ways, but it makes sense the protocol works this way if you spend a long time thinking about it. The difference is that SNMPv3 TRAPs use the engineID of the local application sending the trap rather than the engineID of the remote application. This means that you have to create users in your remote user database with a bit more care and need to create one for every engineID you wish to send traps from. This means that if you want to have 100 snmp agents send snmpv3 traps to your trap receiver, you need 100 createUser directives in your /var/net-snmp/snmptrapd.conf file.

Файл /etc/snmp/snmptrapd.conf выглядит следующим образом:

createUser -e 0x800007c703609c9f1eff01 snmpv3USER SHA auth_pass AES priv_pass
createUser -e 0x800007c703cc4e24e4c6c3 snmpv3USER SHA auth_pass AES priv_pass
createUser -e 0x800007c703d4c19e743e86 snmpv3USER SHA auth_pass AES priv_pass
createUser -e 0x800007c70378a6e10ca715 snmpv3USER SHA auth_pass AES priv_pass
createUser -e 0x800007c70378a6e10c46f0 snmpv3USER SHA auth_pass AES priv_pass
createUser -e 0x800007c70378a6e121bacd snmpv3USER SHA auth_pass AES priv_pass
createUser -e 0x800007c70378a6e121b7d8 snmpv3USER SHA auth_pass AES priv_pass
createUser -e 0x800007c703609c9f5136f4 snmpv3USER SHA auth_pass AES priv_pass

authUser log,execute snmpv3USER
perl do "/usr/bin/zabbix_trap_receiver.pl";

Файл намного длиннее, но теперь я могу получать все ловушки со всех коммутаторов. Я видел на stackoverflow, что у кого-то получилось с DES без использования EngineID, но это настоящая хрень, у меня никогда не работало ни для DES, ни для AES без EngineID.Это просто то, что было сделано на фоне другого программного обеспечения для мониторинга, которое ведет запись EngineID всех устройств для задачи приема ловушек.

Я также заметил, что ловушки от основного коммутатора поступают с виртуального интерфейса влана, на котором находится zabbix, поэтому мне пришлось модифицировать скрипт zabbix _trap _Receiver.pl следующим образом:

use NetSNMP::TrapReceiver; # should be added in order to get the script to work, otherwise it fails -- elekgeek 2nd DEC 2019
# get the host name
        $pdu_info{'receivedfrom'} =~ s/192.168.168.254/192.168.168.1/ig; # I added this line to get the core switch IP instead of zabbix's VLAN SVI IP, otherwise trap is not added to the CORE trap.fallback -- elekgeek 2nd DEC 2019
        my $hostname = $pdu_info{'receivedfrom'} || 'unknown';
        if ($hostname ne 'unknown')

При работе с ловушками мне приходилось видеть их как историю, поэтому я изменил пункт SNMP traps (fallback )с Тип информации=Журнал на Тип информации=Текст в шаблоне Template Module Generic SNMPv3.

Удачи в заббиксировании!

2
09.04.2021, 14:45
1 ответ

I would like to find out why systemd thinks it should be stopping the service unit. What can I configure or enable to determine why systemd took the "stop" action?

Чтобы увидеть текущее -состояние службы, вы можете:

  • Используйте команду systemd-cgls -l <service-cgroup-path>:там вы увидите все процессы сервисов, как они есть на данный момент. Путь cgroup службы можно получить с помощью команды systemctl show -p ControlGroup <service-name>. В более поздних версияхsystemd(не в v219 )вы также можете использовать удобную -u <service-name>опцию systemd-cglsвместо пути cgroup службы
  • Для подробного анализа вы можете использовать очень подробную systemctl show <service-name>команду :. Это даст массу информации о состоянии службы, как известно systemd, и на основе этой информации вы сможете сделать более подробный вывод о том, что происходит

Чтобы исследовать случай «подозрения на остановку», правильно добавить эти команды как команды ExecStop. Вы можете просто добавить их в начало вашего собственного stop.service-cскрипта (, если это действительно скрипт ).

Или же вы можете добавить их как дополнительные ExecStopкоманды самостоятельно перед вашей stop.service-cкомандой, как в:

[Service]
Type=forking
ExecStart=/local-path/start.service-c
ExecStop=-/bin/sh -c 'systemd-cgls -l -u %n && systemctl show %n'
ExecStop=/local-path/stop.service-c
Restart=on-failure

Обратите внимание, что спецификатор %nправильно обрабатывается systemd, даже если он встречается в строках в кавычках.

В качестве альтернативы вы также можете:

[Service]
Type=forking
ExecStart=/local-path/start.service-c
ExecStop=-/usr/bin/systemd-cgls -l -u %n
ExecStop=-/bin/systemctl show %n
ExecStop=/local-path/stop.service-c
Restart=on-failure

Обратите также внимание на префикс -перед командами, чтобы игнорировать их статус выхода на тот случай, если они потерпят неудачу по непонятным причинам.

Естественно, вы также можете использовать их как ExecStartPostкоманды, чтобы обдумать текущее -состояние сразу после того, как systemdсчитает службу "успешно запущенной".(снова заставляет игнорировать их статус выхода или systemdотключит всю службу в случае сбоя ).

Что касается вывода systemd-cgls, выполняемого как команда ExecStop, вы должны отметить, что процесс MainPIDвсе еще отображается в то время :, если он действительно появляется, то это доказывает, что ExecStopдействительно был выполнен автономно systemd, как вы предполагаете. Иначе (, если MainPIDпроцесс не присутствует в выводе systemd-cglsво время «остановки» ), это означает, что ExecStopбыл запущен в результате процесса MainPID, завершающегося по собственному желанию.(Дополнительные аргументы см. ниже). Вы также можете обратить внимание на номера PID процессов службы вместе с номерами PID команды (now dead )ExecStart, чтобы попытаться сделать вывод о том, чтоfork(2)-происходило с момента запуска службы и всего вместе, потому что это очень важно для службы type=forking, чтобы оценить, хорошо ли она -себя ведет.(Дополнительные аргументы см. ниже).

Что касается systemctl showвывода, выполняемого как команда ExecStop, я бы сказал, что наиболее важными свойствами, на которые следует обратить внимание в вашем конкретном случае , являются:

  • MainPID:читает 0, если основной процесс службы завершился по собственному желанию, иначе считывает PID основного процесса службы, если он все еще жив и, таким образом, действительно остановленsystemd
  • ExecMainExitTimestamp:считывает время выхода в формате date, если основной процесс службы завершился по собственному желанию, иначе не считывает вообще, если процесс все еще жив и, следовательно, действительно остановленsystemd
  • ExecMainExitTimestampMonotonic:как указано выше, но читается в монотонных часах Linux и читается 0, если процесс еще жив
  • ExecMainCode:это соответствует строке code=вsystemctl status1 ,только он сообщает десятичное значение символов CLD_*вместо их перевода в английские слова :в соответствии с текущими значениями Linux для CLD_*символов (, которые представляют собой enum, начиная с 1), ExecMainCodeимеет значение 0, если процесс все еще жив и, таким образом, действительно собирается быть остановленным systemd, в противном случае отображается 1, если процесс уже_exit(2)-ed по собственной инициативе, 2, если он былоkill(2)-издано (в этом варианте использования явно не поsystemd)и так далее

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


Дополнительная информация

В ваших рассуждениях я вижу два ключевых момента, заслуживающих дополнительного разъяснения:

type=forkingуслуги

Службы

type=forkingособенно сложны для systemd, особенно при использованииGuessMainPID=yes(по умолчанию, поэтому то, что вы сейчас используете для своего агента ). Для этих типов служб ожидается, что команда ExecStartбудет fork(2)сама один раз , а затем завершится, в то время как ее разветвленный процесс, как ожидается, будет жить долго и процветать как MainPIDслужбы. Остальное:

  1. Если такой разветвленный процесс снова разветвляется, а затем также завершается, делегируя своему собственному «второму» разветвленному процессу (es )ответственность за функционирование в качестве фактической службы, GuessMainPIDпросто теряет след и systemdпросто считает, что служба завершилась регулярно и спонтанно, и, таким образом, выполняет свою обязанность по очистке всего (, т.е. запускает ExecStopи т. д. ), но без регистрации сообщения Stopping service..., потому что, насколько что касается systemd,он реагировал только на преднамеренный выход службы
  2. Если вместо этогоExecStartисходный процесс fork(2)дважды (или более )выполняется )перед выходом, тогда GuessMainPIDсдается и systemdвоздерживается от сноса всего, когда исходныйExecStartПроцесс завершается. Это лучший случай, потому что фактические процессы службы выживают, но он еще не идеален, потому что тогда systemdтакже не будет полностью отслеживать события, что приводит, например, к. как минимум несогласованные/неполные журналы журналов.

ExecStopисполнение

Команды ExecStopзапускаются также , когда процесс MainPIDуспешно завершается сам по себе, если основной процесс также запущен успешно (, что ваше дело под рукой ). Я понимаю, что это кажется нелогичным, но это просто нормальное поведение, так какsystemd:он просто считает команду службы ExecStopпредпочтительным способом очистки после этой службы, прежде чем прибегать к (по умолчанию, см.systemd.kill(5))отправка Сначала SIGTERM, а потом, возможно, SIGKILL.

Нигде на справочной странице systemd.service(5)об этом не говорится так явно, но об этом можно сделать вывод из нескольких фрагментов документации, особенно тех, которые касаются переменных среды, доступных для команд Exec*. См. $SERVICE_RESULT, $EXIT_CODEи$EXIT_STATUSпеременные, чтобы узнать, какие значения они могут принимать, какое семантическое значение они имеют, и тот факт, что они доступны именно для команд ExecStop, а также ExecStopPost..

Помимо не -явной (или личной интерпретации )документации, давайте посмотрим на источники, которые обеспечивают такое поведение. Взято из версии 219, здесь service_sigchld_event()вызываетservice_enter_running()по событию, относящемуся к дочернему элементу, который, как известно, находится в состоянии "работает",а затем последняя функция, вызывающаяservice_enter_stop()действие «остановки» во всех случаях, кроме случаев, когда RemainAfterExit=yesили type=dbusили основной процесс службы не обнаружен(см. type=forkingпояснение выше)или контрольная -группа не здорова.

Что касается , почему люди systemdрешили сделать это, я не знаю, так как я не systemdразработчик, но я вижу полезность такого поведения, чтобы дать все еще живы -но -"неизвестные" процессы службы, их шанс быть уведомленным об их неизбежном прекращении самым приятным способом, прежде чем получить суровый SIGTERM и SIGKILL в качестве последнего средства systemdпродолжает закрывать всю контрольную -группу. Эта мера особенно полезна именно для служб type=forking, потому что systemdтруднее всего правильно отследить их, как объяснено в абзаце type=systemd.service(5), и потому что systemdпытается очистить устаревшие/ ленивые/плохо -реализованные службы, которые не закрываются корректно перед выходом.

ХТХ


1. code=, за которым следует слово, представляющее «причину выхода» из процесса :, будь то exitedили был killedили trappedили даже dumped; на практике :буквально слово, переводящее различные CLD_*значения, действительные для поля siginfo_t.si_code, как описано вsigaction(2)

2
28.04.2021, 22:53

Теги

Похожие вопросы