先前我有介紹過 systemd 的常用指令。接著在這篇文章,我要介紹怎麽寫出一個服務檔案。首先我會介紹服務檔案大概長怎樣,接著介紹常用的設定選項,最後列出一個範例做結尾。
架構
服務檔案由 section 組成,類似這樣:
section 由用中括號包起來的英文單字開頭,下面接著若干個設定。
每一行有一筆設定,包含設定選項跟設定值,中間以 =
隔開,左邊是選項的名稱,右邊是設定值。
服務檔案可以有註解。以 #
或 ;
開頭的一行會被當做註解被忽略。
服務有哪些 section?
服務檔案是 unit 檔案的一種,unit 檔案可以有 [Unit]
跟 [Install]
section。不同種類的 unit 檔案可以有它們專屬的 section,所以服務檔案還可以有 [Service]
section。因此服務檔案可能有 3 個 section:[Unit]
、[Install]
與 [Service]
。
常用的設定選項
下面介紹一些常用的設定選項。
接下來我在提及設定選項的名稱的時候,會比照 systemd 的文件在後面加一個 =
,像這樣:Key=
。
說明
在 [Unit]
section 可以加入這些選項來說明 unit 檔案的用途:
Description=
:unit 檔案的一句簡短的介紹。Documentation=
:unit 檔案的文件位置。
相依性
systemd 其中一個功能就是管理服務的開始順序。
systemd 有分兩種相依性,一種是順序,另一種是需求。
順序
我們可以在 [Unit]
section 用 Before=
與 After=
指定服務啟動的順序。
例如有兩個服務 a.service
、b.service
,要讓 a.service
比 b.service
早啟動的話,可以在 a.service
的 [Unit]
section 加上 Before=b.service
。
需求
這種相依性代表的意思是當某個服務啟動了,需要那個服務的服務就會啟動。這種相依性不一定會有時間差,有設定這種相依性的服務雙方有可能會同時啟動。
我們可以用幾種選項指定需求的相依性,不過我只會提到常用的兩類:Wants=
/ WantedBy=
、Requires=
/ RequiredBy=
。
如果 a.service
的 [Unit]
section 有 Wants=b.service
,代表 b.service
啟動了 a.service
才會啟動。
Requires=
跟 Wants=
有類似的意義,不過在啟動失敗的時候有差別。如果 Wants=
指定的服務啟動失敗了,這個服務還是會照樣執行。而如果 Requires=
指定的服務啟動失敗了,這個服務會停止;如果上述這個 Requires=
指定的服務也有在 After=
被指定,這個服務不會啟動。
這些設定選項放置的位置也需要注意。Wants=
跟 Requires=
這種主動型態的設定選項要放置在 [Unit]
section 下,而 WantedBy=
跟 RequiredBy=
這種被動型態的設定選項要放置在 [Install]
section 下。systemd 文件有一張表整理了這類關係,大家可以看看。
服務類型
服務的類型可以在 [Service]
section 下的 Type=
設定,一共有 8 種,但是我只會提到常用的:simple
、exec
跟 oneshot
。
simple
跟 exec
的不同點是服務被視為「已啟動」的時間點不一樣。simple
類型的服務在被 fork 出去的當下就視為已啟動,而 exec
類型的服務則是在服務的執行檔(指的大概是 ExecStart=
的指令)已經執行的時候才視為已啟動。
oneshot
跟 simple
對於啟動的時間點是一樣的,不一樣的是 oneshot
在主行程結束時就會被視為已結束。
執行指令
要指令服務啟動要執行的指令,可以在 [Service]
section 下的 ExecStart=
設定。除了 oneshot
類型的服務外,一個服務只能有一個 ExecStart=
設定。
oneshot
類型的服務可以有多個 ExecStart=
設定,它們會照順序執行。也可以沒有 ExecStart=
設定,但是需要在同個 section 有 RemainAfterExit=yes
以及至少一個 ExecStop=
設定,代表這個服務是設定在要停止時執行指令的。
我們也可以用 ExecStartPre=
在 ExecStart=
指定的指令前執行指令,或是用 ExecStartPost=
在 ExecStart=
指定的指令後執行指令。它們可以有複數個,一樣會照順序執行。
範例
下面用一個簡單的服務檔案做範例,這個檔案是在這篇介紹 Podman 自動更新的文章中,我有提到過的:
首先我們看這個服務會執行什麼東西?/usr/bin/podman auto-update
也就是 Podman 自動更新 image 的指令。再來會接著執行 /usr/bin/podman image prune -f
把不會用到的 image 刪掉。
因為它是 oneshot
類型的服務,所以在第一個指令執行完成後,服務就會停止。
最後,這個服務依賴什麼東西?network-online.target
跟 default.target
。這兩個都是 systemd 內建的特殊 unit,表示系統中的某件事情已經完成,default.target
表示開機完成,network-online.target
則表示網路已經可以使用。
WantedBy=default.target
是很常用的設定,代表開機時要啟動這個服務。不過官方現在不推薦用 default.target
,而應該改用其他類似的 target,如 multi-user.target
,詳情可以看這邊。
Wants=network-online.target
跟 After=network-online.target
也是很常用的設定,代表開機後要到網路可以用時才能啟動這個服務。如果網路不是 systemd 服務管的,這個設定等於沒有用,除非更改 network-online.target
的意涵。
結論
寫一個服務檔案要知道執行什麼指令以及要依賴哪些其他服務,套用上面的設定選項後,就可以做出簡單的服務了。
文件裡還有很多別的設定選項是我沒有提到的,如果這篇文章介紹的設定選項不夠用,可以看文件找找有沒有合適的設定選項。