運維排查 | 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 MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...