博主我配置了開機自啟動vino-server_systemd service之:服務配置檔案編寫(1)
systemd service:簡介
Systemd Service是systemd提供的用於管理服務啟動、停止和相關操作的功能,它極大的簡化了服務管理的配置過程,使用者只需要配置幾項指令即可。相比於SysV的服務管理指令碼,使用者不需要去編寫服務的啟動、停止、重啟、狀態檢視等等一系列複雜且有重複造輪子嫌疑的指令碼程式碼了,相信寫過SysV服務管理指令碼的人都深有體會。所以,Systemd Service是面向所有使用者的,即使對於新手使用者來說,配置門檻也非常低。
systemd service是systemd所管理的其中一項內容。實際上,systemd service是Systemd Unit的一種,除了Service,systemd還有其他幾種型別的unit,比如socket、slice、scope、target等等。在這裡,暫時瞭解兩項內容:
- Service型別,定義服務程式的啟動、停止、重啟等操作和程序相關屬性
- Target型別,主要目的是對Service(也可以是其它Unit)進行分組、歸類,可以包含一個或多個Service Unit(也可以是其它Unit)
此外,Systemd作為管家,還將一些功能整合到了Systemd Service中,個人覺得比較出彩的兩個整合功能是:
- 使用者可以直接在Service配置檔案中定義CGroup相關指令來對該服務程式做資源限制。在以前,對服務程式做CGroup資源控制的步驟是比較繁瑣的
- 使用者可以選擇Journal日誌而非採用rsyslog,這意味著使用者可以不用單獨去配置rsyslog,而且可以直接通過systemctl或journalctl命令來檢視某服務的日誌資訊。當然,該功能並不適用於所有情況,比如使用者需要管理日誌時
Systemd Service還有其它一些特性,比如可以動態修改服務管理配置檔案,比如可以並行啟動非依賴的服務,從而加速開機過程,等等。例如,使用systemd-analyze blame
可分析開機啟動各服務佔用的時長:
$ systemd-analyze blame
3.557s network.service
1.567s [email protected]:2.service
1.060s lvm2-monitor.service
1.046s dev-mapper-centos\x2droot.device
630ms cgconfig.service
581ms tuned.service
488ms mysqld.service
270ms postfix.service
138ms auditd.service
91ms polkit.service
66ms boot.mount
43ms systemd-logind.service
......
# 從核心啟動開始至開機結束所花時間
$ systemd-analyze time
Startup finished in 818ms (kernel) + 2.228s (initrd) + 3.325s (userspace) = 6.372s
multi-user.target reached after 2.214s in userspace
systemd服務配置檔案存放路徑
Systemd Service配置檔案的檔名均以.service
為字尾,這些服務配置檔案的存放路徑依然遵循Systemd管理配置檔案的風格:將服務管理配置檔案統一『隱藏』在/usr/lib/systemd/system目錄下,同時暴露一個可供使用者存放服務配置檔案的目錄/etc/systemd/system。
如果使用者需要,可以將服務配置檔案手動存放至使用者配置目錄/etc/systemd/system下。該目錄下的服務配置檔案可以是普通.service
檔案,也可以是連結至/usr/lib/systemd/system目錄下服務配置檔案的軟連結。
例如:
# 位於/usr/lib/systemd/system下的服務配置檔案
# 注意帶有`@`符號的檔名,它有特殊意義
$ ls -1 /usr/lib/systemd/system/*.service | head
/usr/lib/systemd/system/arp-ethers.service
/usr/lib/systemd/system/auditd.service
/usr/lib/systemd/system/[email protected]
/usr/lib/systemd/system/blk-availability.service
/usr/lib/systemd/system/brandbot.service
/usr/lib/systemd/system/cgconfig.service
/usr/lib/systemd/system/cgred.service
/usr/lib/systemd/system/console-getty.service
/usr/lib/systemd/system/console-shell.service
/usr/lib/systemd/system/[email protected]
# 下面這些目錄(*.target.wants)定義各種型別下需要執行的服務
# 如:
# sysinit.target.wants目錄下的是系統初始化過程執行的服務
# getty.target.wants目錄下的是開機後啟動虛擬終端時執行的服務
# multi-user.target.wants目錄下的是多使用者模式(對應執行級別2、3、4)下執行的服務
$ ls -1dF /etc/systemd/system/*
/etc/systemd/system/basic.target.wants/
/etc/systemd/system/default.target
/etc/systemd/system/default.target.wants/
/etc/systemd/system/getty.target.wants/
/etc/systemd/system/local-fs.target.wants/
/etc/systemd/system/multi-user.target.wants/
/etc/systemd/system/nginx.service.d/
/etc/systemd/system/sockets.target.wants/
/etc/systemd/system/sysinit.target.wants/
/etc/systemd/system/system-update.target.wants/
# /etc/systemd/system/multi-user.target.wants下的服務配置檔案,幾乎都是軟連結
$ ls -l /etc/systemd/system/multi-user.target.wants/ | awk '{print $9,$10,$11}'
auditd.service -> /usr/lib/systemd/system/auditd.service
crond.service -> /usr/lib/systemd/system/crond.service
irqbalance.service -> /usr/lib/systemd/system/irqbalance.service
mysqld.service -> /usr/lib/systemd/system/mysqld.service
postfix.service -> /usr/lib/systemd/system/postfix.service
remote-fs.target -> /usr/lib/systemd/system/remote-fs.target
rhel-configure.service -> /usr/lib/systemd/system/rhel-configure.service
rsyslog.service -> /usr/lib/systemd/system/rsyslog.service
sshd.service -> /usr/lib/systemd/system/sshd.service
tuned.service -> /usr/lib/systemd/system/tuned.service
systemd service檔案格式說明
一個Systemd Service的服務配置檔案大概長這樣:
[Unit]
Description = some descriptions
Documentation = man:xxx(8) man:xxx_config(5)
Requires = xxx1.target xxx2.target
After = yyy1.target yyy2.target
[Service]
Type =
ExecStart =
ExecStop =
ExecReload =
Restart =
RestartSec =
[Install]
WantedBy = xxx.target yy.target
一個.Service
配置檔案分為三部分:
- Unit:定義該服務作為Unit角色時相關的屬性
- Service:定義本服務相關的屬性
- Install:定義本服務在設定服務開機自啟動時相關的屬性。換句話說,只有在建立/移除服務配置檔案的軟連結時,Install段才會排上用場。這一配置段不是必須的,當未配置
[Install]
時,設定開機自啟動或禁止開機自啟動的操作將無任何效果
[Unit]
和[Install]
段的配置指令都來自於man systemd.unit
,這些指令都用於描述作為Unit時的屬性,[Service]
段則專屬於.Service
服務配置檔案。
這裡先介紹一些常見的[Unit]
和[Install]
相關的指令(雖然支援的配置指令很多,但只需熟悉幾個即可),之後再專門介紹Service段落的配置指令。
[Unit]段落指令
- DescriptionUnit的描述資訊
- Documentation本Unit的man文件路徑
- After本服務在哪些服務啟動之後啟動,僅定義啟動順序,不定義服務依賴關係,即使要求先啟動的服務啟動失敗,本服務也依然會啟動
- Before本服務在哪些服務啟動之前啟動,僅定義啟動順序,不定義服務依賴關係。通常用於定義在關機前要關閉的服務,如Before=shutdown.target
- Wants本服務在哪些服務啟動之後啟動,定義服務依賴關係,不定義服務啟動順序。啟動本服務時,如果被依賴服務未啟動,則也會啟動被依賴服務。如果被依賴服務啟動失敗,本服務不會受之影響,因此本服務會繼續啟動。如果未結合After使用,則本服務和被依賴服務同時啟動。
當配置在[Install]
段落中時,systemctl enable操作將會將本服務安裝到對應的.wants
目錄下(在該目錄下建立一個軟連結),在開機自啟動時,.wants
目錄中的服務會被隱式新增至目標Unit的Wants
指令後。 - Requires本服務在哪些服務啟動之後啟動,定義服務強依賴關係,不定義服務啟動順序。啟動本服務時,如果被依賴服務未啟動,則也會啟動被依賴服務。如果結合了After,當存在非active狀態的被依賴服務時,本服務不會啟動。且當被依賴服務被手動停止時,本服務也會被停止,但有例外。如果要保證兩服務之間狀態必須一致,使用BindsTo指令。
當配置在[Install]
段落中時,systemctl enable操作將會將本服務安裝到對應的.requires
目錄下(在該目錄下建立一個軟連結),在開機自啟動時,.requires
目錄中的服務會被隱式新增至目標Unit的Requires
指令後。 - Requisite本服務在哪些服務啟動之後啟動,定義服務依賴關係,不定義服務啟動順序。啟動本服務時,如果被依賴服務處於尚未啟動狀態,則不會主動去啟動這些服務,所以本服務直接啟動失敗。該指令一般結合After一起使用,以便保證啟動順序。
- BindsTo繫結兩個服務,兩服務的狀態保證一致。如服務1為active,則本服務也一定為active。
- PartOf本服務是其它服務的一部分,定義了單向的依賴關係,且只對stop和restart操作有效。當被依賴服務執行stop或restart操作時,本服務也會執行操作,但本服務執行這些操作,不會影響被依賴服務。一般用於組合target使用,比如a.service和b.service都配置PartOf=c.target,那麼stop c的時候,也會同時stop a和b。
- Conflicts定義衝突的服務,本服務和被衝突服務的狀態必須相反。當本服務要啟動時,將會停止目標服務,當啟動目標服務時,將會停止本服務。啟動和停止的操作同時進行,所以,如果想要讓本服務在目標服務啟動之前就已經處於停止狀態,則必須定義After/Before。
- OnFailure當本服務處於failed時,將啟動目標服務。如果本服務配置了Restart重啟指令,則在耗盡重啟次數之後,本服務才會進入failed。
有時候這是非常有用的,一個典型用法是本服務失敗時呼叫定義了郵件傳送功能的service來發送郵件,特別地,可以結合systemd.timer定時任務實現cron的MAILTO功能。 - RefuseManualStart, RefuseManualStop本服務不允許手動啟動和手動停止,只能被依賴時的啟動和停止,如果手動啟動或停止,則會報錯。有些特殊的服務非常關鍵,或者某服務作為一個大服務的一部分,為了保證安全,都可以使用該特性。例如,系統審計服務auditd.service中配置了不允許手動停止指令RefuseManualStop,network.target中配置了不允許手動啟動指令RefuseManualStart。
- AllowIsolated允許使用systemctl isolate切換到本服務,只配置在target中。一般來說,使用者服務是絕不可能用到這一項的。
- ConditionPathExists, AssertPathExists要求給定的絕對路徑檔案已經存在,否則不做任何事(condition)或進入failed狀態(assert),可在路徑前使用
!
表示條件取反,即不存在時才啟動服務。 - ConditionPathIsDirectory, AssertPathIsDirectory如上,路徑存在且是目錄時啟動。
- ConditionPathIsReadWrite, AssertPathIsReadWrite如上,路徑存在且可讀可寫時啟動
- ConditionDirectoryNotEmpty, AssertDirectoryNotEmpty如上,路徑存在且是非空目錄時啟動
- ConditionFileNotEmpty, AssertFileNotEmpty如上,路徑存在且是非空檔案時啟動
- ConditionFileIsExecutable, AssertFileIsExecutable如上,路徑存在且是可執行普通檔案時啟動
對於自定義的服務配置檔案來說,需要定義的常見指令包括Description、After、Wants及可能需要的條件判斷類指令。所以,Unit段落是非常簡單的。
[Install]段落指令
下面是[Install]
段落相關的指令,它們只在systemctl enable/disable
操作時有效。如果期望服務開機自啟動,一般只配置一個WantedBy指令,如果不期望服務開機自啟動,則Install段落通常省略。
WantedBy
本服務設定開機自啟動時,在被依賴目標的
.wants
目錄下建立本服務的軟連結。例如WantedBy = multi-user.target
時,將在/etc/systemd/multi-user.target.wants目錄下建立本服務的軟連結。RequiredBy
類似WantedBy,但是是在
.requires
目錄下建立軟連結。Alias
指定建立軟連結時連結至本服務配置檔案的別名檔案。例如reboot.target中配置了Alias=ctrl-alt-del.target,當執行enable時,將建立/etc/systemd/system/ctrl-alt-del.service軟連結並指向reboot.target。
DefaultInstance
當是一個模板服務配置檔案時(即檔名為
[email protected]
),該指令指定該模板的預設例項。例如[email protected]中配置了DefaultInstall=server時,systemctl enable [email protected]時將建立名為[email protected]的軟連結。
例如,下面是sshd的服務配置檔案/usr/lib/systemd/system/sshd.service,只看Unit段落和Install段落,是否很簡單?
$ cat /usr/lib/systemd/system/sshd.service
[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.service
Wants=sshd-keygen.service
[Service]
......
[Install]
WantedBy=multi-user.target
再來一個auditd.service的配置檔案示例:
$ cat /usr/lib/systemd/system/auditd.service
[Unit]
Description=Security Auditing Service
DefaultDependencies=no
After=local-fs.target systemd-tmpfiles-setup.service
Before=sysinit.target shutdown.target # 關機前先停止服務
Conflicts=shutdown.target # 確保關機時,auditd已停止成功
RefuseManualStop=yes # 禁止手動systemctl stop auditd操作
ConditionKernelCommandLine=!audit=0
Documentation=man:auditd(8)
[Service]
......
[Install]
WantedBy=multi-user.target
[Service]段配置
Systemd Service配置檔案中的[Service]
段落可配置的指令很多,可配置在此段落中的指令來源有多處,包括:
man systemd.service
中描述的專屬於Service Unit的指令,一般是定義服務程序啟動、停止、重啟等管理行為的指令。比如ExecStart指令指定服務啟動時執行的命令man systemd.exec
中描述的指令,一般是定義服務程序的啟動環境、執行環境的指令。例如指定工作目錄、指定服務程序的執行使用者、指定環境變數、指定服務OOM相關的屬性,等。特別地,指定標準輸出/標準錯誤的目標時可以設定日誌,比如設定為journal時可設定該服務使用journal日誌系統man systemd.kill
中描述的指令,一般是定義終止服務程序相關的指令,比如終止服務時傳送什麼訊號、服務程序沒殺死時如何處理,等man resource-control
中描述的指令,一般是定義服務程序關於資源控制相關的指令,即配置服務程序的CGroup。比如該服務程序最多允許使用多少記憶體、使用哪些CPU、最多佔用多少百分比的CPU時間,等
例如,/usr/lib/systemd/system/rsyslog.service檔案的內容:
[Service]
EnvironmentFile=-/etc/sysconfig/rsyslog # 來自systemd.exec
UMask=0066 # 來自systemd.exec
StandardOutput=null # 來自systemd.exec
Type=notify # 來自systemd.service
ExecStart=/usr/sbin/rsyslogd -n $SYSLOGD_OPTIONS # 來自systemd.service
Restart=on-failure # 來自systemd.service
再比如,想要限制一個服務最多允許使用300M記憶體(比如512M的vps主機執行一個比較耗記憶體的部落格系統時,可設定記憶體使用限制),最多30%CPU時間:
[Service]
MemoryLimit=300M
CPUQuota=30%
ExecStart=xxx
此外還需要了解systemd的一項功能,systemctl set-property
,它可以線上修改已啟動服務的屬性。例如
# 直接限制,且寫入配置檔案,所以下次啟動服務也會被限制
systemctl set-property nginx MemoryLimit=100M
# 直接限制,不寫入配置檔案,所以下次啟動服務不會被限制
systemctl set-property nginx MemoryLimit=100M --runtime
目前來說,可以不用過多關注來自其它位置的指令,應該給予重點關注的是來自systemd.service自身的指令,比如:
- Type:指定服務的管理型別
- ExecStart:指定啟動服務時執行的命令列
- ExecStop:指定停止服務時執行的命令
- ExecReload:指定過載服務程序時執行的命令
- Restart:指定systemd是否要自動重啟服務程序以及什麼情況下重啟
特別是Type指令,它直接影響[Service]
段中的多項配置方式。
下面將從Type指令開始引入Service段的配置方式。
根據man systemd.service
,Type指令支援多種值:
- simple
- exec
- forking
- oneshot
- dbus
- notify
- idle
如果配置的是服務程序,Type的值很可能是forking或simple,如果是普通命令的程序,Type的值可能是simple、oneshot。而dbus型別一般情況下用不上,notify要求服務程式中使用程式碼對systemd notify進行支援,所以多數情況下可能也用不上。
關於Type,內容較長,見下一篇文章systemd service之:服務配置檔案編寫(2)。