我可以配置或啟用什麼來確定 systemd 對服務採取“停止”操作的原因?
我們有一個啟動第三方代理的 systemd 服務單元;稱之為“服務c”。服務單元執行正常——至少,據我所知!在一個更新檔週期之後,systemd 會啟動這個服務單元(如預期的那樣),但是它會在成功啟動它大約兩秒後轉身並*停止服務單元。*我完全有理由相信該服務第一次成功啟動。重啟後登錄,可以看到服務確實沒有執行;那時,我可以手動啟動服務單元(
systemctl start service-c
),它會按預期啟動服務。我想知道為什麼 systemd 認為它應該停止服務單元。我可以配置或啟用什麼來確定 systemd 為何採取“停止”操作?
我知道systemd LogLevel 選項並已將其設置為“調試”,而不是預設的“資訊”。
類似的構想是
Environment=SYSTEMD_LOG_LEVEL=debug
在service unit文件中設置,不過我不是特別需要調試的service,而是systemd本身。服務單元配置為:
# /etc/systemd/system/service-c.service [Unit] Description=service c After=network-online.target local-fs.target [Service] Type=forking ExecStart=/local-path/start.service-c ExecStop=/local-path/stop.service-c Restart=on-failure [Install] WantedBy=multi-user.target
…證據是:
$ systemctl status service-c ● service-c.service - service c Loaded: loaded (/etc/systemd/system/service-c.service; enabled; vendor preset: disabled) Active: inactive (dead) since Wed 2021-04-07 17:49:30 EDT; 14h ago Process: 3162 ExecStop=/local-path/stop.service-c (code=exited, status=0/SUCCESS) Process: 1319 ExecStart=/local-path/start.service-c (code=exited, status=0/SUCCESS) Main PID: 1478 (code=exited, status=0/SUCCESS)
/local-path
是系統上本地目錄的混淆版本。由於這是一個持續存在的問題,因此在上次重新啟動後,我檢測了“停止”包裝腳本以記錄程序父樹(使用
pstree -a -A -l -p -s $$)
);該日誌文件顯示:04/07/2021 17:49:19 stop.service-c: systemd,1 --switched-root --system --deserialize 22 `-stop.service-c,3162 /local-path/stop.service-c `-pstree,3178 -a -A -l -p -s 3162
… 其中 PID 3162 對應於 systemd 對停止腳本的呼叫。這在我看來就像 systemd 正在為服務呼叫 ExecStop。
systemd 在完成啟動後大約兩秒鐘停止此服務;代理的日誌文件具有以下時間戳:
04/07/2021 17:49:12 start.service-c: Starting agent 04/07/2021 17:49:17 start.service-c: startup success 04/07/2021 17:49:19 stop.service-c: Executing from /agent/home as user
……結尾……
04/07/2021 17:49:30 stop.service-c: Finished with RC=0
…這對應於 systemd 的“死亡”時間戳 17:49:30。
“Restart=on-failure”指令將重新啟動服務,但 systemd 告訴我服務已成功啟動:
Apr 07 17:49:10 hostname systemd[1]: Starting service c... Apr 07 17:49:17 hostname systemd[1]: Started service c.
由於服務乾淨地啟動,並且由於 systemd 沒有嘗試重新啟動服務,我不認為 Restart 參數正在發揮作用。
也許有趣的是,journalctl 中沒有相應的“正在停止服務 c…”日誌(就像我手動停止服務時一樣),但證據表明 systemd 呼叫了 ExecStop。
我目前正在執行 systemd 219。
我想知道為什麼 systemd 認為它應該停止服務單元。我可以配置或啟用什麼來確定 systemd 為何採取“停止”操作?
為了查看服務的實時狀態,您可以:
- 使用
systemd-cgls -l <service-cgroup-path>
命令:在那裡您將看到當時所有服務的程序。可以使用systemctl show -p ControlGroup <service-name>
命令檢索服務的 cgroup 路徑。在較新的版本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
。(再次 make 忽略它們的退出狀態,或者systemd
如果它們失敗將關閉整個服務)。關於
systemd-cgls
的輸出 run asExecStop
command ,你應該注意MainPID
那個時候程序是否仍然出現:如果它確實出現了,那麼它證明ExecStop
確實已經systemd
按照你的建議自主執行了。否則(如果MainPID
程序在“停止”時沒有出現在systemd-cgls
’ 的輸出中),則表示該ExecStop
程序已作為程序自行退出的結果而執行。(有關其他推理,請參見下文)。您可能還需要注意服務程序的 PID 編號以及(現已死亡的)命令的 PID 編號,以嘗試推斷出什麼MainPID``ExecStart``fork(2)
-ing 自服務啟動以來一直在進行,因為這與服務非常相關,type=forking
以便評估它是否表現良好。(有關其他推理,請參見下文)。關於
systemctl show
’s output run as command ,我想說在您的特定情況下ExecStop
要注意的最相關的屬性是:
MainPID
: 讀取0
服務的主程序是否自行退出,否則讀取服務的主程序的 PID,如果它還活著並且確實被停止systemd
ExecMainExitTimestamp
: 如果服務的主程序已自行退出,則以格式讀取退出時間date
,否則如果程序仍處於活動狀態並且確實被停止,則根本不讀取systemd
ExecMainExitTimestampMonotonic
:如上,但讀取 Linux 的單調時鐘並讀取0
程序是否還活著ExecMainCode
:這對應於1code=
中的字元串,僅報告符號的十進制值而不是它們的英文翻譯:根據 Linux 目前的符號值(這是從 開始),該欄位讀取程序是否仍然存在因此確實即將被 停止,否則讀取程序是否已經自行 -ed,如果它已經被-ed(在這個案例中顯然不是by ),依此類推systemctl status``CLD_*``CLD_*``enum``1``ExecMainCode``0``systemd``1``_exit(2)``2``kill(2)``systemd
但是請注意,如果在服務啟動時無法檢測到服務的主程序,則上述欄位與服務的目前狀態不對應。(解釋見下文)。它們寧願對應於能夠完全完成檢測的最近執行。
systemd``systemd
進一步的見解
在您的推理中,我可以看到兩個值得額外澄清的關鍵點:
type=forking
服務
type=forking
services 對於 來說特別棘手systemd
,尤其是在使用時GuessMainPID=yes
(預設值,因此您目前正在為您的代理使用什麼)。對於這些服務類型,該ExecStart
命令預計會fork(2)
自行執行一次,然後退出,而其分叉的程序預計會隨著服務的執行而長壽和繁榮MainPID
。別的:
- 如果這樣的分叉程序寧可再次分叉然後也退出,將作為實際服務的責任委託給它自己的“第二個”分叉程序,則
GuessMainPID
只會迷失方向,systemd
只是認為服務已經定期自發地完成,因此完成清理所有內容(即執行ExecStop
等)的職責,但不記錄Stopping service...
消息,因為就systemd
目前而言,它只對服務的故意退出做出反應- 相反,如果
ExecStart
原始程序fork(2)
在退出之前 s 兩次(或更多),則在原始程序最終退出時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
。除了非顯式(或個人解釋)文件之外,讓我們看看執行該行為的來源。從 v219 開始,這裡
service_sigchld_event()
呼叫service_enter_running()
了一個事件,該事件引用了一個已知處於“執行”狀態的子程序,然後後一個函式在所有情況下呼叫service_enter_stop()
RemainAfterExit=yes
“停止”操作,除非type=dbus
服務的主程序沒有被檢測到*(見type=forking
上面的解釋)*或對照組不健康。至於為什麼人們
systemd
決定這樣做,我不知道,因為我不是systemd
開發人員,但我可以看到這種行為的有用性,以便為所有仍然存在但“未知”的服務程序提供機會以最好的方式通知他們即將終止,然後按照systemd
最後的手段得到嚴厲的 SIGTERM 和 SIGKILL ,因為它繼續關閉整個控制組。這個措施對type=forking
服務特別有用,因為這些服務最難systemd
正確追踪,如type=
段落中所解釋的systemd.service(5)
,並且因為systemd
試圖清理遺留/延遲/實施不佳的服務,這些服務在退出之前沒有正常關閉。高溫高壓
1.
code=
後面跟一個代表程序“退出原因”的詞:無論是,exited
還是曾經killed
,trapped
甚至是dumped
;在實踐中:字面意思是翻譯對欄位有效的各種值CLD_*
,如siginfo_t.si_code``sigaction(2)