運維排查 | Systemd 之服務停止後狀態為 failed

来源:https://www.cnblogs.com/edisonfish/p/18109224
-Advertisement-
Play Games

哈嘍大家好,我是鹹魚。 我們知道 CentOS 7 之後,Systemd 代替了原來的 SystemV 來管理服務,相比 SystemV ,Systemd 能夠很好地解決各個服務間的依賴關係,還能讓所有的服務同時啟動,而不是串列啟動。 通常情況下,yum 安裝的軟體會由系統的包管理器(如 RPM)安 ...


哈嘍大家好,我是鹹魚。

我們知道 CentOS 7 之後,Systemd 代替了原來的 SystemV 來管理服務,相比 SystemV ,Systemd 能夠很好地解決各個服務間的依賴關係,還能讓所有的服務同時啟動,而不是串列啟動。

通常情況下,yum 安裝的軟體會由系統的包管理器(如 RPM)安裝,並且會配置相應的 systemd 服務,因此由 systemd 來管理。然而,在一些情況下,特別是當採用源碼編譯安裝軟體或者軟體本身並沒有提供 systemd 管理的解決方案時,就需要手動編寫 systemd 服務文件(service 文件)來管理這些軟體。

那今天我們就來看看手動編寫 systemd 服務文件來管理軟體時發現的一些問題。

問題出現

我們的 zookeeper 是通過源碼編譯來安裝,為了方便管理,決定改成通過 systemd 來管理。

下麵是 zookeeper 的 service 文件

# zookeeper
[Unit]
Description=Zookeeper
After=network.target

[Service]
Type=forking
ExecStart=/opt/zookeeper/bin/zkServer.sh start
ExecStop=/opt/zookeeper/bin/zkServer.sh stop
PIDFile=/var/lib/zookeeper/zookeeper_server.pid

[Install]
WantedBy=multi-user.target

可以看到,配置文件分成幾個區塊,每個區塊包含若幹條鍵值對。

[Unit]

Unit 部分的 Description 欄位給出當前服務的簡單描述接下來的設置是啟動順序:

  1. After 欄位:表示 zookeeper.service 應該在哪些服務之後啟動。
  2. Before欄位,表示 zookeeper.service 應該在哪些服務之前啟動。

註意,AfterBefore欄位只涉及啟動順序,不涉及依賴關係。

[Service]

Service 部分定義如何啟動當前服務。

  • ExecStart:啟動進程時執行的命令。

  • ExecStop:停止服務時執行的命令。

  • Type:定義啟動類型

    1. simple(預設值):ExecStart欄位啟動的進程為主進程
    2. forking:ExecStart欄位將以fork()方式啟動,此時父進程將會退出,子進程將成為主進程
    3. oneshot:類似於simple,但只執行一次,Systemd 會等它執行完,才啟動其他服務
    4. dbus:類似於simple,但會等待 D-Bus 信號後啟動
    5. notify:類似於simple,啟動結束後會發出通知信號,然後 Systemd 再啟動其他服務
    6. idle:類似於simple,但是要等到其他任務都執行完,才會啟動該服務。一種使用場合是為讓該服務的輸出,不與其他服務的輸出相混合

[Install]

Install 部分定義如何安裝這個配置文件,即怎樣做到開機啟動

  • WantedBy:表示該服務所在的 Target。

Target 的含義是服務組,表示一組服務。WantedBy=multi-user.target指的是,kafka 和 zookeeper 所在的 Target 是 multi-user.target

這個設置非常重要,因為執行systemctl enable 命令時,zookeeper .service 的一個符號鏈接,就會放在/etc/systemd/system目錄下麵的multi-user.target.wants子目錄之中。

Systemd 有預設的啟動 Target。

[root@localhost ~]# systemctl get-default
multi-user.target

上面的結果表示,預設的啟動 Target 是 multi-user.target。在這個組裡的所有服務,都將開機啟動。這就是為什麼 systemctl enable 命令能設置開機啟動的原因。

編寫好 service 文件之後,我們執行下麵的命令來啟動 zookeeper:

[root@localhost ~]# systemctl start zookeeper.service

接著看下 zookeeper 的運行狀態

[root@localhost ~]# systemctl status zookeeper.service       
● zookeeper.service - Zookeeper
   Loaded: loaded (/usr/lib/systemd/system/zookeeper.service; enabled; vendor preset: disabled)
   Active: active (running) since 一 2024-04-01 09:10:23 CST; 2s ago
  Process: 60955 ExecStop=/opt/zookeeper/bin/zkServer.sh stop (code=exited, status=0/SUCCESS)
  Process: 61116 ExecStart=/opt/zookeeper/bin/zkServer.sh start (code=exited, status=0/SUCCESS)
 Main PID: 61132 (java)
   CGroup: /system.slice/zookeeper.service
           └─61132 java -Dzookeeper.log.dir=/opt/zookeeper/bin/../logs -Dzookeeper.log.file=zookeeper--server-localhost.localdomain.log -Dzookeepe...

4月 01 09:10:22 localhost.localdomain systemd[1]: Starting Zookeeper...
4月 01 09:10:22 localhost.localdomain zkServer.sh[61116]: /usr/sbin/java
4月 01 09:10:22 localhost.localdomain zkServer.sh[61116]: ZooKeeper JMX enabled by default
4月 01 09:10:22 localhost.localdomain zkServer.sh[61116]: Using config: /opt/zookeeper/bin/../conf/zoo.cfg
4月 01 09:10:23 localhost.localdomain zkServer.sh[61116]: Starting zookeeper ... STARTED
4月 01 09:10:23 localhost.localdomain systemd[1]: Started Zookeeper.

active (running) 表示運行正常

當我們執行 systemctl stop zookeeper.service 命令停止 zookeeper 的時候,問題出現了

[root@localhost ~]# systemctl status zookeeper.service 
● zookeeper.service - Zookeeper
   Loaded: loaded (/usr/lib/systemd/system/zookeeper.service; enabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since 一 2024-04-01 09:10:30 CST; 906ms ago
  Process: 61183 ExecStop=/opt/zookeeper/bin/zkServer.sh stop (code=exited, status=0/SUCCESS)
  Process: 61116 ExecStart=/opt/zookeeper/bin/zkServer.sh start (code=exited, status=0/SUCCESS)
 Main PID: 61132 (code=exited, status=143)

4月 01 09:10:23 localhost.localdomain systemd[1]: Started Zookeeper.
4月 01 09:10:29 localhost.localdomain systemd[1]: Stopping Zookeeper...
4月 01 09:10:29 localhost.localdomain zkServer.sh[61183]: /usr/sbin/java
4月 01 09:10:29 localhost.localdomain zkServer.sh[61183]: ZooKeeper JMX enabled by default
4月 01 09:10:29 localhost.localdomain zkServer.sh[61183]: Using config: /opt/zookeeper/bin/../conf/zoo.cfg
4月 01 09:10:29 localhost.localdomain systemd[1]: zookeeper.service: main process exited, code=exited, status=143/n/a
4月 01 09:10:30 localhost.localdomain zkServer.sh[61183]: Stopping zookeeper ... STOPPED
4月 01 09:10:30 localhost.localdomain systemd[1]: Stopped Zookeeper.
4月 01 09:10:30 localhost.localdomain systemd[1]: Unit zookeeper.service entered failed state.
4月 01 09:10:30 localhost.localdomain systemd[1]: zookeeper.service failed.

可以看到,zookeeper 服務在停止後並不是 inactive ,而是 failed 狀態,最後兩行輸出里有 Unit zookeeper.service entered failed state./zookeeper.service failed 欄位

問題定位

我們接著看上面的輸出,可以看到在設置了 Type=forking 後,服務在啟動或關閉時執行對應的腳本會開啟一個進程,並且兩個進程都成功執行了(返回狀態碼為 0 )。

  Process: 61183 ExecStop=/opt/zookeeper/bin/zkServer.sh stop (code=exited, status=0/SUCCESS)
  Process: 61116 ExecStart=/opt/zookeeper/bin/zkServer.sh start (code=exited, status=0/SUCCESS)
  Main PID: 61132 (code=exited, status=143)

但是主進程退出時返回的狀態碼卻是 143,而不是狀態碼 0。

接著看下 zookeeper 進程還在不在:

[root@localhost ~]# jps -l
61287 sun.tools.jps.Jps

[root@localhost ~]# ps -ef | grep zookeeper
root      61300  61250  0 09:49 pts/0    00:00:00 grep --color=auto zookeeper

奇怪,明明 zookeeper 進程已經成功退出了,但是 systemd 卻說它退出失敗

此時我註意到儘管在停止服務時,狀態碼為 0,但也只是表明執行 /opt/zookeeper/bin/zkServer.sh stop 命令本身成功完成,這個狀態碼並不代表腳本內部的執行邏輯一定是成功的。

我們看下 zkServer.sh 腳本中關於 stop 的邏輯

stop)
    echo -n "Stopping zookeeper ... "
    if [ ! -f "$ZOOPIDFILE" ]
    then
      echo "no zookeeper to stop (could not find file $ZOOPIDFILE)"
    else
      $KILL  $(cat "$ZOOPIDFILE")
      rm "$ZOOPIDFILE"
      sleep 1
      echo STOPPED
    fi
    exit 0
    ;;

沒有發現有什麼不妥,接著我們註釋掉 ExecStop 欄位,採用 systemd 預設的方式來停止服務。

預設情況下,systemd 將向進程發送 SIGTERM 信號(相當於 kill 命令發送的終止信號),等待一段時間後,如果服務進程未正常退出,則發送 SIGKILL 信號(相當於 kill -9 命令發送的強制終止信號)強制終止服務進程。

然後重新啟停一下 zookeeper ,看下狀態:

[root@localhost ~]# systemctl status zookeeper.service 
● zookeeper.service - Zookeeper
   Loaded: loaded (/usr/lib/systemd/system/zookeeper.service; enabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since 一 2024-04-01 10:03:04 CST; 1s ago
  Process: 61453 ExecStart=/opt/zookeeper/bin/zkServer.sh start (code=exited, status=0/SUCCESS)
 Main PID: 61469 (code=exited, status=143)

4月 01 10:02:55 localhost.localdomain systemd[1]: Started Zookeeper.
4月 01 10:02:55 localhost.localdomain zkServer.sh[61453]: /usr/sbin/java
4月 01 10:02:55 localhost.localdomain zkServer.sh[61453]: ZooKeeper JMX enabled by default
4月 01 10:02:55 localhost.localdomain zkServer.sh[61453]: Using config: /opt/zookeeper/bin/../conf/zoo.cfg
4月 01 10:02:56 localhost.localdomain zkServer.sh[61453]: Starting zookeeper ... STARTED
4月 01 10:03:04 localhost.localdomain systemd[1]: Stopping Zookeeper...
4月 01 10:03:04 localhost.localdomain systemd[1]: zookeeper.service: main process exited, code=exited, status=143/n/a
4月 01 10:03:04 localhost.localdomain systemd[1]: Stopped Zookeeper.
4月 01 10:03:04 localhost.localdomain systemd[1]: Unit zookeeper.service entered failed state.
4月 01 10:03:04 localhost.localdomain systemd[1]: zookeeper.service failed.
[root@localhost ~]# jps -l
61524 sun.tools.jps.Jps
[root@localhost ~]# ps -ef | grep zookeeper
root      61537  61250  0 10:04 pts/0    00:00:00 grep --color=auto zookeeper

還是一樣的問題,zookeeper 已經成功退出但是卻顯示 failed 狀態,狀態碼是 143。

從上面我們得知:無論是通過 zkserver.sh 還是 systemd 預設方式來關閉服務,本質上都是向 zookeeper 進程發送 SIGTERM 信號(數值為 15 ),雖然 zookeeper 進程成功退出,但是 systemd 將此解釋為異常退出,因為預期的退出狀態碼為 0。

而根據 POSIX 規範:【因接收到信號而終止的命令的退出狀態應報告為大於 128】,所以被信號中斷的進程退出時會返回 128 加上信號數值作為退出狀態碼。

也就是說,當 zookeeper 進程收到 SIGTERM 信號時,會返回 128 + 15 也就是 143 作為退出狀態碼,這也就是為什麼進程在成功退出後 systemctl 顯示為 failed 狀態。

解決問題

既然知道了進程在退出時的狀態碼是 143 但是 systemd 不會解釋為成功,因為預期的退出狀態碼為 0,那麼我們只需要讓 systemd 把狀態碼 143 也解釋為成功就行。

所以在 zookeeper 的 service 文件中添加下麵的配置:

[Service]
...
SuccessExitStatus=143
...

表示當服務進程以狀態碼 143 正常退出時,systemd 將其視為成功退出而不是異常退出。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 實驗準備: 一臺dns1域控制器,一臺虛擬機登錄域用戶驗證設置 一:“財務處”用戶自動映射z盤 打開dns1,在c盤下新建share目錄,添加test文本文件 修改share目錄的共用為所有人 完成可以看到share目錄的網路路徑 在共用裡面複製網路路徑 打開組策略管理器 右鍵財務部GPO,點擊編輯 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 微服務架構已經成為搭建高效、可擴展系統的關鍵技術之一,然而,現有許多微服務框架往往過於複雜,使得我們普通開發者難以快速上手並體驗到微服務帶了的便利。為瞭解決這一問題,於是作者精心打造了一款最接地氣的 .NET 微服務框架,幫助我們輕鬆構建和管理微服務應用。 本框架不僅支持 Consul 服務註 ...
  • 先看一下效果吧: 如果不會寫動畫或者懶得寫動畫,就直接交給Blend來做吧; 其實Blend操作起來很簡單,有點類似於在操作PS,我們只需要設置關鍵幀,滑鼠點來點去就可以了,Blend會自動幫我們生成我們想要的動畫效果. 第一步:要創建一個空的WPF項目 第二步:右鍵我們的項目,在最下方有一個,在B ...
  • Prism:框架介紹與安裝 什麼是Prism? Prism是一個用於在 WPF、Xamarin Form、Uno 平臺和 WinUI 中構建鬆散耦合、可維護和可測試的 XAML 應用程式框架 Github https://github.com/PrismLibrary/Prism NuGet htt ...
  • 在WPF中,屏幕上的所有內容,都是通過畫筆(Brush)畫上去的。如按鈕的背景色,邊框,文本框的前景和形狀填充。藉助畫筆,可以繪製頁面上的所有UI對象。不同畫筆具有不同類型的輸出( 如:某些畫筆使用純色繪製區域,其他畫筆使用漸變、圖案、圖像或繪圖)。 ...
  • 前言 嗨,大家好!推薦一個基於 .NET 8 的高併發微服務電商系統,涵蓋了商品、訂單、會員、服務、財務等50多種實用功能。 項目不僅使用了 .NET 8 的最新特性,還集成了AutoFac、DotLiquid、HangFire、Nlog、Jwt、LayUIAdmin、SqlSugar、MySQL、 ...
  • 本文主要介紹攝像頭(相機)如何採集數據,用於類似攝像頭本地顯示軟體,以及流媒體數據傳輸場景如傳屏、視訊會議等。 攝像頭採集有多種方案,如AForge.NET、WPFMediaKit、OpenCvSharp、EmguCv、DirectShow.NET、MediaCaptre(UWP),網上一些文章以及 ...
  • 前言 Seal-Report 是一款.NET 開源報表工具,擁有 1.4K Star。它提供了一個完整的框架,使用 C# 編寫,最新的版本採用的是 .NET 8.0 。 它能夠高效地從各種資料庫或 NoSQL 數據源生成日常報表,並支持執行複雜的報表任務。 其簡單易用的安裝過程和直觀的設計界面,我們 ...
  • 背景需求: 系統需要對接到XXX官方的API,但因此官方對接以及管理都十分嚴格。而本人部門的系統中包含諸多子系統,系統間為了穩定,程式間多數固定Token+特殊驗證進行調用,且後期還要提供給其他兄弟部門系統共同調用。 原則上:每套系統都必須單獨接入到官方,但官方的接入複雜,還要官方指定機構認證的證書 ...
  • 本文介紹下電腦設備關機的情況下如何通過網路喚醒設備,之前電源S狀態 電腦Power電源狀態- 唐宋元明清2188 - 博客園 (cnblogs.com) 有介紹過遠程喚醒設備,後面這倆天瞭解多了點所以單獨加個隨筆 設備關機的情況下,使用網路喚醒的前提條件: 1. 被喚醒設備需要支持這WakeOnL ...
  • 前言 大家好,推薦一個.NET 8.0 為核心,結合前端 Vue 框架,實現了前後端完全分離的設計理念。它不僅提供了強大的基礎功能支持,如許可權管理、代碼生成器等,還通過採用主流技術和最佳實踐,顯著降低了開發難度,加快了項目交付速度。 如果你需要一個高效的開發解決方案,本框架能幫助大家輕鬆應對挑戰,實 ...