1. 程式人生 > 其它 >博主我配置了開機自啟動vino-server_systemd service之:服務配置檔案編寫(1)

博主我配置了開機自啟動vino-server_systemd service之:服務配置檔案編寫(1)

技術標籤:博主我配置了開機自啟動vino-server

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)。

9e40baa9bc83fc47e199460b8e1e593a.png