Systemd 服務:比啟動停止服務更進一步

Paul Brown發表於2018-06-02

在上一篇文章中,我們展示瞭如何建立一個 systemd 服務並使普通使用者可以啟動和終止遊戲伺服器。但到目前為止,使用這個服務並不比直接執行伺服器高明多少。讓我們更進一步,讓其可以向玩家發郵件,包括在伺服器可用時通知玩家,在伺服器關閉前警告玩家:

# minetest.service

[Unit]
Description= Minetest server
Documentation= https://wiki.minetest.net/Main_Page

[Service]
Type= simple

ExecStart= /usr/games/minetest --server
ExecStartPost= /home/<username>/bin/mtsendmail.sh "Ready to rumble?" "Minetest Starting up"

TimeoutStopSec= 180
ExecStop= /home/<username>/bin/mtsendmail.sh "Off to bed. Nightie night!" "
  Minetest Stopping in 2 minutes"
ExecStop= /bin/sleep 120
ExecStop= /bin/kill -2 $MAINPID

這裡涉及幾個新的指令。首先是 ExecStartPost 指令,該指令可以在主程式啟動後馬上執行任何你指定的操作。在本例中,你執行了一個自定義指令碼 mtsendmail (內容如下),該指令碼以郵件形式通知你的朋友伺服器已經啟動。

#!/bin/bash
# mtsendmail
echo $1 | mutt -F /home/<username>/.muttrc -s "$2" my_minetest@mailing_list.com

我們使用 Mutt 這個命令後郵件客戶端傳送訊息。雖然從實際效果來看,上述指令碼僅有 1 行,但 systemd 單元的引數中不能包含管道及重定向操作,故我們需要將其封裝到指令碼中。

順便提一下,還有一個 ExecStartPre 指令,用於在服務主程式執行之前進行指定操作。

接下來我們看到,關閉伺服器涉及了好幾條指令。TimeoutStopSec 指令用於設定 systemd 友好關閉服務的最大等候時間,預設值大約是 90 秒。超過這個最大等候時間,systemd 會強制關閉服務並報錯。考慮到你希望在徹底關閉伺服器前給使用者預留幾分鐘的時間,你需要將超時時間提高至 3 分鐘,這樣 systemd 就不會誤認為服務關閉時出現問題。

接下來是關閉服務的具體指令部分。雖然沒有 ExecStopPre 這樣的指令,但你可以透過多次使用 ExecStop 指令實現關閉伺服器前執行操作的目標。多個 ExecStop 指令按從上到下的順序依次執行,這樣你就可以在伺服器真正關閉前向使用者傳送訊息。

透過這個特性,你首先應該向你的朋友發郵件,警告其伺服器即將關閉,然後等待兩分鐘,最後關閉伺服器。可以使用 Ctrl + c 關閉 Minetest 伺服器,該操作會被轉換為一箇中斷訊號(SIGINT);當你執行 kill -2 $MAINPID 時就會傳送該中斷訊號,其中 $MAINPID 是 systemd 變數,用於記錄你服務中主程式的 PID 資訊。

看上去好多了!如果你此時啟動服務:

systemctl --user start minetest

服務會啟動 Minetest 伺服器並向你的使用者傳送郵件。關閉服務的情形基本類似,只不過會額外留給使用者 2 分鐘時間退出登入。

開機自啟動

下一步我們讓你的服務在主機啟動後立即可用,在主機關閉時自動關閉。

我們需要將你的服務檔案移動到系統服務目錄,即 /etc/systemd/system/

sudo mv /home/<username>/.config/systemd/user/minetest.service /etc/systemd/system/

如果你希望此時啟動該服務,你需要擁有超級使用者許可權:

sudo systemctl start minetest

另外,可以使用如下命令檢查服務狀態:

sudo systemctl status minetest

你會發現服務很糟糕地處於失敗狀態,這是因為 systemd 不能透過上下文資訊、特徵、配置檔案得知具體使用哪個使用者執行該服務。在單元檔案中增加 User 指令可以解決這個問題。

# minetest.service

[Unit]
Description= Minetest server
Documentation= https://wiki.minetest.net/Main_Page

[Service]
Type= simple
User= <username>

ExecStart= /usr/games/minetest --server
ExecStartPost= /home/<username>/bin/mtsendmail.sh "Ready to rumble?"
  "Minetest Starting up"

TimeoutStopSec= 180
ExecStop= /home/<username>/bin/mtsendmail.sh "Off to bed. Nightie night!"
  "Minetest Stopping in 2 minutes"
ExecStop= /bin/sleep 120
ExecStop= /bin/kill -2 $MAINPID

systemd 從 User 指令中得知應使用哪個使用者的環境變數來正確執行該服務。你可以使用 root 使用者,但這可能產生安全風險;使用你的個人使用者會好一些,但不少管理員的做法是為服務單獨建立一個使用者,這樣可以有效地將服務與其它使用者和系統元件相互隔離。

下一步我們讓你的服務在系統啟動時自動啟動,系統關閉時自動關閉。要達到這個目的,你需要 啟用 你的服務;但在這之前,你還需要告知 systemd 從哪裡 安裝 它。

對於 systemd 而言,安裝 意味著告知 systemd 在系統啟動的具體哪個步驟啟用你的服務。以通用 Unix 列印系統(cups.service)為例,它的啟動在網路框架啟動之後、其它列印服務啟動之前。又如,minetest.server 需要使用使用者郵件(及其它元件),需要等待網路和普通使用者對應的服務就緒後才可啟動。

你只需要在單元檔案中新增一個新段和新指令:

...
[Install]
WantedBy= multi-user.target

你可以將其理解為“等待多使用者系統的全部內容就緒”。systemd 中的“目標”類似於舊系統中的執行級別,可以用於將主機轉移到一個或另一個狀態,也可以像本例中這樣讓你的服務等待指定狀態出現後執行。

你的最終 minetest.service 檔案如下:

# minetest.service
[Unit]
Description= Minetest server
Documentation= https://wiki.minetest.net/Main_Page

[Service]
Type= simple
User= <username>

ExecStart= /usr/games/minetest --server
ExecStartPost= /home/<username>/bin/mtsendmail.sh "Ready to rumble?"
  "Minetest Starting up"

TimeoutStopSec= 180
ExecStop= /home/<username>/bin/mtsendmail.sh "Off to bed. Nightie night!"
  "Minetest Stopping in 2 minutes"
ExecStop= /bin/sleep 120
ExecStop= /bin/kill -2 $MAINPID

[Install]
WantedBy= multi-user.target

在嘗試新的服務之前,你還需要對郵件指令碼做一些調整:

#!/bin/bash
# mtsendmail

sleep 20
echo $1 | mutt -F /home/<username>/.muttrc -s "$2" my_minetest@mailing_list.com
sleep 10

這是因為系統需要一定的時間啟動郵件系統(這裡等待 20 秒),也需要一定時間完成郵件傳送(這裡等待 10 秒)。注意指令碼中的等待時間數值適用於我的系統,你可能需要針對你的系統調整數值。

大功告成啦。執行如下操作:

sudo systemctl enable minetest

你的 Minetest 服務將在系統啟動時自動啟動,在系統關閉時友好關閉並通知你的使用者。

總結

事實上 Debian、 Ubuntu 和一些族類的發行版提供了 minetest-server 這個特別的軟體包,可以提供一部分上述功能,(但不包括郵件通知功能)。即使如此,你還是可以建立你獨有的自定義服務;事實上,你目前建立的服務比 Debian 預設提供的服務更加通用,可以提供更多功能。

更進一步的說,我們這裡描述的流程可以讓你將大多數簡單伺服器轉換為服務,型別可以是遊戲、網站應用或其它應用。同時,這也是你名副其實地踏入 systemd 大師殿堂的第一步。


via: https://www.linux.com/blog/learn/2018/5/systemd-services-beyond-starting-and-stopping

作者:Paul Brown 選題:lujun9972 譯者:pinewall 校對:wxy

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

相關文章