使用 systemd 作爲問題定位工具

沒有人會認爲 systemd 是一個故障定位工具,但當我的 web 服務器遇到問題時,我對 systemd 和它的一些功能的不斷了解幫助我找到並規避了問題。

我遇到的問題是這樣,我的服務器 yorktown 爲我的家庭辦公網絡提供名稱服務 、DHCP、NTP、HTTPD 和 SendMail 郵件服務,它在正常啓動時未能啓動 Apache HTTPD 守護程序。在我意識到它沒有運行之後,我不得不手動啓動它。這個問題已經持續了一段時間,我最近纔開始嘗試去解決它。

你們中的一些人會說,systemd 本身就是這個問題的原因,根據我現在瞭解的情況,我同意你們的看法。然而,我在使用 SystemV 時也遇到了類似的問題。(在本系列文章的 第一篇 中,我探討了圍繞 systemd 作爲舊有 SystemV 啓動程序和啓動腳本的替代品所產生的爭議。如果你有興趣瞭解更多關於 systemd 的信息,也可以閱讀 第二篇 和 第三篇 文章。)沒有完美的軟件,systemd 和 SystemV 也不例外,但 systemd 爲解決問題提供的信息遠遠多於 SystemV。

確定問題所在

找到這個問題根源的第一步是確定 httpd 服務的狀態:

[root@yorktown ~]# systemctl status httpd
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since Thu 2020-04-16 11:54:37 EDT; 15min ago
     Docs: man:httpd.service(8)
  Process: 1101 ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND (code=exited, status=1/FAILURE)
 Main PID: 1101 (code=exited, status=1/FAILURE)
   Status: "Reading configuration..."
      CPU: 60ms
Apr 16 11:54:35 yorktown.both.org systemd[1]: Starting The Apache HTTP Server...
Apr 16 11:54:37 yorktown.both.org httpd[1101]: (99)Cannot assign requested address: AH00072: make_sock: could not bind to address 192.168.0.52:80
Apr 16 11:54:37 yorktown.both.org httpd[1101]: no listening sockets available, shutting down
Apr 16 11:54:37 yorktown.both.org httpd[1101]: AH00015: Unable to open logs
Apr 16 11:54:37 yorktown.both.org systemd[1]: httpd.service: Main process exited, code=exited, status=1/FAILURE
Apr 16 11:54:37 yorktown.both.org systemd[1]: httpd.service: Failed with result 'exit-code'.
Apr 16 11:54:37 yorktown.both.org systemd[1]: Failed to start The Apache HTTP Server.
[root@yorktown ~]#

這種狀態信息是 systemd 的功能之一,我覺得比 SystemV 提供的任何功能都要有用。這裏的大量有用信息使我很容易得出邏輯性的結論,讓我找到正確的方向。我從舊的 chkconfig 命令中得到的是服務是否在運行,以及如果它在運行的話,進程 ID(PID)是多少。這可沒多大幫助。

該狀態報告中的關鍵條目顯示,HTTPD 不能與 IP 地址綁定,這意味着它不能接受傳入的請求。這表明網絡啓動速度不夠快,因爲 IP 地址還沒有設置好,所以 HTTPD 服務還沒有準備好與 IP 地址綁定。這是不應該發生的,所以我查看了我的網絡服務的 systemd 啓動配置文件;在正確的 after 和 requires 語句下,所有這些似乎都沒問題。下面是我服務器上的 /lib/systemd/system/httpd.service 文件:

# Modifying this file in-place is not recommended, because changes
# will be overwritten during package upgrades.  To customize the
# behaviour, run "systemctl edit httpd" to create an override unit.
# For example, to pass additional options (such as -D definitions) to
# the httpd binary at startup, create an override unit (as is done by
# systemctl edit) and enter the following:                                           
#    [Service]
#    Environment=OPTIONS=-DMY_DEFINE
[Unit]                                               
Description=The Apache HTTP Server
Wants=httpd-init.service
After=network.target remote-fs.target nss-lookup.target httpd-init.service
Documentation=man:httpd.service(8)
[Service]
Type=notify
Environment=LANG=C
ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND
ExecReload=/usr/sbin/httpd $OPTIONS -k graceful
# Send SIGWINCH for graceful stop
KillSignal=SIGWINCH
KillMode=mixed
PrivateTmp=true
[Install]
WantedBy=multi-user.target

httpd.service 單元文件明確規定,它應該在 network.target 和 httpd-init.service(以及其他)之後加載。我試着用 systemctl list-units 命令找到所有這些服務,並在結果數據流中搜索它們。所有這些服務都存在,應該可以確保在設置網絡 IP 地址之前,httpd 服務沒有加載。

第一個解決方案

在互聯網上搜索了一下,證實其他人在 httpd 和其他服務也遇到了類似的問題。這似乎是由於其中一個所需的服務向 systemd 表示它已經完成了啓動,但實際上它卻啓動了一個尚未完成的子進程。通過更多搜索,我想到了一個規避方法。

我搞不清楚爲什麼花了這麼久才把 IP 地址分配給網卡。所以我想,如果我可以將 HTTPD 服務的啓動推遲合理的一段時間,那麼 IP 地址就會在那個時候分配。

幸運的是,上面的 /lib/systemd/system/httpd.service 文件提供了一些方向。雖然它說不要修改它,但是它還是指出瞭如何操作:使用 systemctl edit httpd 命令,它會自動創建一個新文件(/etc/systemd/system/httpd.service.d/override.conf)並打開 GNU Nano 編輯器(如果你對 Nano 不熟悉,一定要看一下 Nano 界面底部的提示)。

在新文件中加入以下代碼並保存:

[root@yorktown ~]# cd /etc/systemd/system/httpd.service.d/
[root@yorktown httpd.service.d]# ll
total 4
-rw-r--r-- 1 root root 243 Apr 16 11:43 override.conf
[root@yorktown httpd.service.d]# cat override.conf
# Trying to delay the startup of httpd so that the network is
# fully up and running so that httpd can bind to the correct
# IP address
#
# By David Both, 2020-04-16
[Service]
ExecStartPre=/bin/sleep 30

這個覆蓋文件的 [Service] 段有一行代碼,將 HTTPD 服務的啓動時間推遲了 30 秒。下面的狀態命令顯示了等待時間裏的服務狀態:

[root@yorktown ~]# systemctl status httpd
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
  Drop-In: /etc/systemd/system/httpd.service.d
           └─override.conf
           /usr/lib/systemd/system/httpd.service.d
           └─php-fpm.conf
   Active: activating (start-pre) since Thu 2020-04-16 12:14:29 EDT; 28s ago
     Docs: man:httpd.service(8)
Cntrl PID: 1102 (sleep)
    Tasks: 1 (limit: 38363)
   Memory: 260.0K
      CPU: 2ms
   CGroup: /system.slice/httpd.service
           └─1102 /bin/sleep 30
Apr 16 12:14:29 yorktown.both.org systemd[1]: Starting The Apache HTTP Server...
Apr 16 12:15:01 yorktown.both.org systemd[1]: Started The Apache HTTP Server.
[root@yorktown ~]#

這個命令顯示了 30 秒延遲過後 HTTPD 服務的狀態。該服務已經啓動並正常運行。

[root@yorktown ~]# systemctl status httpd
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
  Drop-In: /etc/systemd/system/httpd.service.d
           └─override.conf
           /usr/lib/systemd/system/httpd.service.d
           └─php-fpm.conf
   Active: active (running) since Thu 2020-04-16 12:15:01 EDT; 1min 18s ago
     Docs: man:httpd.service(8)
  Process: 1102 ExecStartPre=/bin/sleep 30 (code=exited, status=0/SUCCESS)
 Main PID: 1567 (httpd)
   Status: "Total requests: 0; Idle/Busy workers 100/0;Requests/sec: 0; Bytes served/sec:   0 B/sec"
    Tasks: 213 (limit: 38363)
   Memory: 21.8M
      CPU: 82ms
   CGroup: /system.slice/httpd.service
           ├─1567 /usr/sbin/httpd -DFOREGROUND
           ├─1569 /usr/sbin/httpd -DFOREGROUND
           ├─1570 /usr/sbin/httpd -DFOREGROUND
           ├─1571 /usr/sbin/httpd -DFOREGROUND
           └─1572 /usr/sbin/httpd -DFOREGROUND
Apr 16 12:14:29 yorktown.both.org systemd[1]: Starting The Apache HTTP Server...
Apr 16 12:15:01 yorktown.both.org systemd[1]: Started The Apache HTTP Server.

我本來可以實驗下更短的延遲時間是否也能奏效,但是我的系統並不用那麼嚴格,所以我覺得不這樣做。目前系統的工作狀態很可靠,所以我很高興。

因爲我收集了所有這些信息,我將其作爲 Bug1825554 報告給紅帽 Bugzilla。我相信報告 Bug 比抱怨 Bug 更有有用。

更好的解決方案

把這個問題作爲 bug 上報幾天後,我收到了回覆,表示 systemd 只是一個管理工具,如果 httpd 需要在滿足某些要求之後被拉起,需要在單元文件中表達出來。這個回覆指引我去查閱 httpd.service 的手冊頁。我希望我能早點發現這個,因爲它是比我自己想出的更優秀的解決方案。這種方案明確的針對了前置目標單元,而不僅僅是隨機延遲。

來自 httpd.service 手冊頁:

在啓動時開啓服務

httpd.service 和 httpd.socket 單元默認是 禁用 的。爲了在啓動階段開啓 httpd 服務,執行:systemctl enable httpd.service。在默認配置中,httpd 守護進程會接受任何配置好的 IPv4 或 IPv6 地址的 80 口上的連接(如果安裝了 mod_ssl,就會接受 443 端口上的 TLS 連接)。

如果 httpd 被配置成依賴任一特定的 IP 地址(比如使用 Listen 指令),該地址可能只在啓動階段可用,又或者 httpd 依賴其他服務(比如數據庫守護進程),那麼必須配置該服務,以確保正確的啓動順序。

例如,爲了確保 httpd 在所有配置的網絡接口配置完成之後再運行,可以創建一個帶有以下代碼段的 drop-in 文件(如上述):

[Unit]
After=network-online.target
Wants=network-online.target

我仍然覺得這是個 bug,因爲在 httpd.conf 配置文件中使用 Listen 指令是很常見的,至少在我的經驗中。我一直在使用 Listen 指令,即使在只有一個 IP 地址的主機上,在多個網卡和 IP 地址的機器上這顯然也是有必要的。在 /usr/lib/systemd/system/httpd.service 默認配置文件中加入上述幾行,對不使用 Listen 指令的不會造成問題,對使用 Listen 指令的則會規避這個問題。

同時,我將使用建議的方法。

下一步

本文描述了一個我在服務器上啓動 Apache HTTPD 服務時遇到的一個問題。它指引你瞭解我在解決這個問題上的思路,並說明了我是如何使用 systemd 來協助解決問題。我也介紹了我用 systemd 實現的規避方法,以及我按照我的 bug 報告得到的更好的解決方案。

如我在開頭處提到的那樣,這有很大可能是一個 systemd 的問題,特別是 httpd 啓動的配置問題。儘管如此,systemd 還是提供了工具讓我找到了問題的可能來源,並制定和實現了規避方案。兩種方案都沒有真正令我滿意地解決問題。目前,這個問題根源依舊存在,必須要解決。如果只是在 /usr/lib/systemd/system/httpd.service 文件中添加推薦的代碼,那對我來說是可行的。

在這個過程中我發現了一件事,我需要了解更多關於定義服務啓動順序的知識。我會在下一篇文章中探索這個領域,即本系列的第五篇。

資源

網上有大量的關於 systemd 的參考資料,但是大部分都有點簡略、晦澀甚至有誤導性。除了本文中提到的資料,下列的網頁提供了跟多可靠且詳細的 systemd 入門信息。

◈ Fedora 項目有一篇切實好用的 systemd 入門,它囊括了幾乎所有你需要知道的關於如何使用 systemd 配置、管理和維護 Fedora 計算機的信息。

◈ Fedora 項目也有一個不錯的 備忘錄,交叉引用了過去 SystemV 命令和 systemd 命令做對比。

◈ 關於 systemd 的技術細節和創建這個項目的原因,請查看 Freedesktop.org 上的 systemd 描述。

◈ Linux.com 的 “更多 systemd 的樂趣” 欄目提供了更多高級的 systemd 信息和技巧。

此外,還有一系列深度的技術文章,是由 systemd 的設計者和主要開發者 Lennart Poettering 爲 Linux 系統管理員撰寫的。這些文章寫於 2010 年 4 月至 2011 年 9 月間,但它們現在和當時一樣具有現實意義。關於 systemd 及其生態的許多其他好文章都是基於這些文章:

◈ Rethinking PID 1

◈ systemd for Administrators,Part I

◈ systemd for Administrators,Part II

◈ systemd for Administrators,Part III

◈ systemd for Administrators,Part IV

◈ systemd for Administrators,Part V

◈ systemd for Administrators,Part VI

◈ systemd for Administrators,Part VII

◈ systemd for Administrators,Part VIII

◈ systemd for Administrators,Part IX

◈ systemd for Administrators,Part X

◈ systemd for Administrators,Part XI

via: https://opensource.com/article/20/5/systemd-troubleshooting-tool

作者:David Both 選題:lujun9972 譯者:tt67wq 校對:wxy

本文由 LCTT 原創編譯,Linux 中國 榮譽推出

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/dgFj_nG4y8LNosX4d6-00Q