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

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

來自專欄 Linux11 人贊了文章

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

# minetest.service[Unit]Description= Minetest serverDocumentation= https://wiki.minetest.net/Main_Page[Service]Type= simpleExecStart= /usr/games/minetest --serverExecStartPost= /home/<username>/bin/mtsendmail.sh "Ready to rumble?" "Minetest Starting up"TimeoutStopSec= 180ExecStop= /home/<username>/bin/mtsendmail.sh "Off to bed. Nightie night!" " Minetest Stopping in 2 minutes"ExecStop= /bin/sleep 120ExecStop= /bin/kill -2 $MAINPID

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

#!/bin/bash# mtsendmailecho $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 serverDocumentation= https://wiki.minetest.net/Main_Page[Service]Type= simpleUser= <username>ExecStart= /usr/games/minetest --serverExecStartPost= /home/<username>/bin/mtsendmail.sh "Ready to rumble?" "Minetest Starting up"TimeoutStopSec= 180ExecStop= /home/<username>/bin/mtsendmail.sh "Off to bed. Nightie night!" "Minetest Stopping in 2 minutes"ExecStop= /bin/sleep 120ExecStop= /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 serverDocumentation= https://wiki.minetest.net/Main_Page[Service]Type= simpleUser= <username>ExecStart= /usr/games/minetest --serverExecStartPost= /home/<username>/bin/mtsendmail.sh "Ready to rumble?" "Minetest Starting up"TimeoutStopSec= 180ExecStop= /home/<username>/bin/mtsendmail.sh "Off to bed. Nightie night!" "Minetest Stopping in 2 minutes"ExecStop= /bin/sleep 120ExecStop= /bin/kill -2 $MAINPID[Install]WantedBy= multi-user.target

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

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

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

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

sudo systemctl enable minetest

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

總結

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

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


via: linux.com/blog/learn/20

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

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


推薦閱讀:

TAG:科技 | 信息技術IT | Linux開發 |