systemd

出自 Arch Linux 中文维基

摘自項目主頁

systemd 是一個 Linux 系統基礎組件的集合,提供了一個系統和服務管理器,運行為 PID 1 並負責啟動其它程序。功能包括:支持並行化任務;同時採用 socket 式與 D-Bus 總線式啟用服務;按需啟動守護進程(daemon);利用 Linux 的 cgroups 監視進程;支持快照和系統恢復;維護掛載點和自動掛載點;各服務間基於依賴關係進行精密控制。systemd 支持 SysV 和 LSB 初始腳本,可以替代 sysvinit。除此之外,功能還包括日誌進程、控制基礎系統配置,維護登錄用户列表以及系統賬户、運行時目錄和設置,可以運行容器和虛擬機,可以簡單的管理網絡配置、網絡時間同步、日誌轉發和名稱解析等。

在歷史上,systemd中的「服務」(service)被稱作守護進程(daemon),它們在後台運行(即沒有UI、不與終端交互),等待特定事件的發生並提供服務。例如,Web伺服器會等待一個請求以提供相應的頁面,SSH伺服器會等待登錄請求。除了這種提供完整功能的,還有一些守護進程的工作是隱藏在幕後的,如負責向日誌文件寫入消息的syslogmetalog,確保系統時間準確的ntpd。更多信息詳見daemon(7)

注意: Arch Linux 論壇的這篇帖子 詳細地解釋了 Arch Linux 遷移到 systemd 的原因。

systemctl 基本用法[編輯 | 編輯原始碼]

監視和控制 systemd 的主要命令是 systemctl。其用途包括查看系統狀態以及管理系統和服務。詳見 systemctl(1)

提示:
  • systemctl 參數中添加 -H 用户名@主機名 可以遠程控制其他機器。該功能使用 SSH 連接到遠程的 systemd 實例。
  • Plasma 用户可以安裝 systemctl 圖形前端 systemdgenie。安裝後可以在系統管理下找到。

使用單元[編輯 | 編輯原始碼]

單元(unit)通常包括但不限於:服務(.service)、掛載點(.mount)、設備(.device)和套接字(.socket)。

使用 systemctl 時,通常需要使用單元文件的全名,包括擴展名(例如 sshd.socket)。不過在以下 systemctl 命令中可以使用簡寫:

  • 如果無擴展名,systemctl 會假定擴展名為 .service。例如,netctlnetctl.service 是等價的。
  • 掛載點會自動轉化為相應的 .mount 單元。例如,/home 等價於 home.mount
  • 與掛載點類似,設備會自動轉化為相應的 .device 單元,因此 /dev/sda2 等價於 dev-sda2.device

詳見 systemd.unit(5)

注意: 有一些單元的名稱包含一個 @ 符號(例如:name@string.service ),這意味着它是模板單元 name@.service 的一個 實例,模板單元的實際文件名中不包括 string 部分(如 name@.service)。string 被稱作實例標識符,在 systemctl 調用模板單元時,會將其當作一個參數傳給模板單元,模板單元會使用這個傳入的參數代替模板中的 %i 指示符。在啟動單元時,嘗試從模板單元實例化之前,systemd 會先檢查 name@string.suffix 文件是否存在。如果存在,就直接使用這個文件,而不是模板實例化(不過,這種「碰撞」非常少見)。大多數情況下,包含 @ 標記都意味着這個文件是模板。如果一個模板單元被調用時沒有指定實例標識符,該調用通常會失敗,除非是在某些特殊的 systemctl命令(如cat)中使用。

因為調用systemctl時默認了 --system 參數,下列命令默認對系統單元進行操作。若要對(調用用户的)用户單元進行操作,則需在非root身份下執行systemctl --user。參看systemd/用户#基礎設置以為所有用户啟用或禁用單元。

提示:
  • 下面的大部分命令都可以跟多個單元名,詳細信息參見 systemctl(1)
  • --now 選項可與 enabledisablemask 同時使用,可使這些動作立即生效,而非重啟後生效。
  • 一個軟件包可能會為不同的功能提供多個不同的單元。如果你剛安裝了軟件包,可以通過 pacman -Qql package | grep -Fe .service -e .socket 命令檢查這個軟件包提供了哪些單元。
動作 命令 註釋
分析系統狀態
顯示系統狀態 systemctl status
列出正在運行的單元 systemctl
systemctl list-units
列出失敗的單元 systemctl --failed
列出已安裝的單元1 systemctl list-unit-files
顯示特定PID對應進程的狀態 systemctl status pid cgroup slice,內存佔用,父進程
檢查單元狀態
顯示單元的手冊頁 systemctl help 單元 如果單元支持
顯示單元的狀態 systemctl status 單元 包括其是否在運行
檢查單元是否配置為自動啟動(enabled) systemctl is-enabled 單元
啟動(start)、重新啟動和重新加載單元
立即啟動單元 以root身份執行systemctl start 單元
立即停止單元 以root身份執行systemctl stop 單元
重新啟動單元 以root身份執行systemctl restart 單元
重新加載單元及其配置 以root身份執行systemctl reload 單元
重新加載 systemd 配置2 以root身份執行systemctl daemon-reload 掃描單元的變動
啟用(enable)單元
啟用單元:開機時自動啟動該單元 以root身份執行systemctl enable 單元
啟用單元,並立即啟動 以root身份執行systemctl enable --now 單元
取消開機自動啟動單元 以root身份執行systemctl disable 單元
重新啟用單元3 以root身份執行systemctl reenable 單元 先取消啟用,再啟用
屏蔽單元
屏蔽單元,使其無法啟動4 以root身份執行systemctl mask 單元
取消屏蔽單元 以root身份執行systemctl unmask 單元
  1. systemd.unit(5) § UNIT FILE LOAD PATH 中描述了查找單元文件的路徑。
  2. 這不會要求已改變的單元重新加載自己的配置(見重新加載動作)。
  3. 可在單元的[Install]節發生變更(在上一次啟用後)時使用。
  4. 既不能手動啟動,也無法作為依賴啟動。因此屏蔽單元是危險的。 查看已屏蔽的單元:
    # systemctl list-unit-files --state=masked

電源管理[編輯 | 編輯原始碼]

安裝 polkit 後才能以普通用户身份使用電源管理。如果你正登錄在一個本地的 systemd-logind 用户會話,且當前沒有其它活動的會話,那麼以下命令無需 root 權限即可執行。否則(例如,當前有另一個用户登錄在某個 tty),systemd 將會自動請求輸入 root 密碼。

動作 命令
重啟 systemctl reboot
關機 systemctl poweroff
待機 systemctl suspend
休眠 systemctl hibernate
進入混合休眠模式(同時休眠到硬盤並待機) systemctl hybrid-sleep

編寫單元文件[編輯 | 編輯原始碼]

systemd 單元文件的語法來源於XDG 桌面項配置的.desktop文件,最初的源頭則是Microsoft Windows的.ini文件。單元文件可以從多個地方加載,systemctl show --property=UnitPath 可以顯示加載目錄。主要的加載目錄為(按優先級從低到高排列):

  • /usr/lib/systemd/system/ :軟件包安裝的單元
  • /etc/systemd/system/ :系統管理員安裝的單元
注意:
  • systemd 運行在用户模式下時,使用的加載路徑是完全不同的。
  • systemd 單元名僅能包含 ASCII 字符,下劃線和點號以及有特殊意義的字符('@', '-')。其它字符需要用C風格的"\x2d" 替換或使用對應的預定義語法(「@」,「-」)。參閱 systemd.unit(5)systemd-escape(1)

單元文件的語法,可以參考系統已經安裝的單元,也可以參考 systemd.service(5) § EXAMPLES

提示:在單元文件中以#開頭的行將被視作註釋,但#僅能在一行開頭使用。不要在 systemd 的參數後面使用行末註釋,否則該單元將不能正常啟動。

處理依賴關係[編輯 | 編輯原始碼]

使用 systemd 時,可通過正確編寫單元配置文件來解決單元間的依賴關係。典型的情況是,單元A要求單元BA啟動之前運行。在此情況下,可向單元A配置文件中的 [Unit] 段添加 Requires=BAfter=B 即可。若此依賴關係是可選的,可添加 Wants=BAfter=B。請注意 Wants=Requires= 並不暗含 After= ,即如果 After=選項沒有指定,這兩個單元將被並行啟動。

依賴關係通常被用在服務(service)而不是目標(target)上。例如,network.target 一般會被某個配置網絡接口的服務引入作為依賴。所以,若要讓自定義單元在系統到達network.target後再啟動, 將自定義的單元排在該類配置網絡接口的服務之後即可,因為該類服務啟動時 network.target 一定已經啟動。

服務(service)類型[編輯 | 編輯原始碼]

編寫自定義的服務(service)文件時,可以選擇幾種不同的服務啟動方式。啟動方式可通過配置文件 [Service] 段中的 Type= 參數進行設置。

  • Type=simple :(默認值) systemd認為該服務將立即啟動且服務進程不會fork。Do not use this type if other services need to be ordered on this service, unless it is socket activated.
  • Type=forking :systemd認為當該服務進程fork,且父進程退出後服務啟動成功。對於典型的守護進程,除非你確定此啟動方式無法滿足需求,使用此類型啟動即可。使用此啟動類型應同時指定 PIDFile=,以便systemd能夠跟蹤服務的主進程。
  • Type=oneshot :這一選項適用於只執行一項任務、隨後立即退出的服務。可能需要同時設置 RemainAfterExit=yes使得systemd在服務進程退出之後仍然認為服務處於啟用狀態,這對於改變系統狀態(如掛載分區)的單元尤其適用。
  • Type=notify :與 Type=simple 相同,但約定服務會在就緒後向systemd發送一個信號以通知systemd。這一通知的參考實現由 libsystemd-daemon.so 提供。
  • Type=dbus :若以此方式啟動,當指定的 BusName 出現在DBus系統總線上時,systemd認為服務就緒。
  • Type=idlesystemd會等待所有活動任務都完成後再執行服務進程。其他行為與 Type=simple 類似。

type 的更多解釋可以參考 systemd.service(5) § OPTIONS

修改現存單元文件[編輯 | 編輯原始碼]

為了避免和 pacman 衝突,不應該直接編輯軟件包提供的文件。有兩種方法可以不改動原始文件就做到修改單元文件:創建一個優先級更高的本地單元文件以覆蓋原有的單元文件或創建一個片段(drop-in snippets),應用到原始單元文件之上。兩種方法都需要在修改後重新加載單元:用 systemctl edit 編輯單元(會自動重載單元)或通過下面命令重新加載單元:

# systemctl daemon-reload
提示:
  • systemd-delta 命令用來查看哪些單元文件被覆蓋、擴增,哪些被修改。
  • 使用 systemctl cat unit 可以查看單元的內容和所有相關的片段。

替換單元文件[編輯 | 編輯原始碼]

要替換 /usr/lib/systemd/system/unit, 創建文件 /etc/systemd/system/unit重新啟用單元以更新符號連結:

# systemctl reenable unit

或者運行:

# systemctl edit --full unit

這將會在默認編輯器中打開 /etc/systemd/system/unit。如果文件不存在,可以將軟件包安裝的版本複製到這裏,在編輯完成之後,會自動加載新版本。

注意: 即使 Pacman 更新了新的單元文件,軟件包中的版本也不會被使用,所以這個方式會增加系統維護的難度,推薦使用下面一種方法。

附加配置片段[編輯 | 編輯原始碼]

要為單元文件/usr/lib/systemd/system/unit附加配置片段,先創建名為 /etc/systemd/system/unit.d/ 的目錄,然後放入 *.conf 文件,其中可以添加或覆蓋參數。這裏設置的參數優先級高於原來的單元文件。systemd會解析這些參數並將這些文件應用到原單元文件上。

要附加配置片段,最簡單的方法是執行:

# systemctl edit unit --drop-in=drop_in_name

這將會在編輯器中打開文件 /etc/systemd/system/unit.d/drop_in_name.conf,編輯完成之後自動加載。若省略--drop-in=選項,systemd將使用默認文件名:override.conf

注意:
  • 附加配置片段中的鍵(配置項)仍必須置於相應的節(section)下。
  • 並不是所有參數都會被子配置文件覆蓋。例如要修改 Conflicts= 就必須替換原始文件

重置到軟件包的版本[編輯 | 編輯原始碼]

要回退使用systemctl edit對單元進行的所有變更,執行:

# systemctl revert unit

示例[編輯 | 編輯原始碼]

如果想添加一個額外的依賴,創建如下文件即可:

/etc/systemd/system/<unit>.d/customdependency.conf
[Unit]
Requires=<新依賴>
After=<新依賴>

要修改一個單元的 ExecStart 命令,創建下面文件:

/etc/systemd/system/unit.d/customexec.conf
[Service]
ExecStart=
ExecStart=<新命令>

修改 ExecStart 前必須將其置空,參見 [1] 。所有可能多次賦值的變量都需要這個操作,例如定時器的 OnCalendar

下面是自動重啟服務的一個例子:

/etc/systemd/system/unit.d/restart.conf
[Service]
Restart=always
RestartSec=30

目標(target)[編輯 | 編輯原始碼]

systemd使用目標來通過依賴關係將多個單元組織起來。目標還是系統的標準化同步點(確保系統處於特定狀態)。目標runlevels的設計目的相似,但兩者也有些許不同。每一個目標都以名稱而不是數字標識,用以達成特定的目的。多個目標可以同時被激活。有的目標繼承另一個目標的所有服務,同時向其中增添一些服務。有些systemd目標模仿了常見的SystemVinit runlevels,所以仍可用熟悉的telinit RUNLEVEL命令切換目標

獲取當前目標[編輯 | 編輯原始碼]

使用systemd的命令而非runlevel

$ systemctl list-units --type=target

創建自定義目標[編輯 | 編輯原始碼]

在sysvinit中有明確定義的運行級別(如:0、1、3、5、6)與 systemd 中特定的目標存在一一對應的關係。然而,對於用户自定義運行級別(2、4)卻沒有。如需要同樣功能,建議你以原有運行級別所對應的 systemd 目標為基礎,新建一個/etc/systemd/system/<目標名>.target(可參考/usr/lib/systemd/system/graphical.target), 然後創建目錄/etc/systemd/system/<目標名>.wants,並向其中加入需啟用的服務連結(指向/usr/lib/systemd/system/中的對應文件)。

「SysV運行級別」與「systemd目標」對照表[編輯 | 編輯原始碼]

SysV 運行級別 Systemd 目標 註釋
0 runlevel0.target, poweroff.target 關閉系統(halt)
1, s, single runlevel1.target, rescue.target 單用户模式
2, 4 runlevel2.target, runlevel4.target, multi-user.target 用户自定義運行級別,默認與級別3相同。
3 runlevel3.target, multi-user.target 多用户,無圖形界面。用户可以通過終端或網絡登錄。
5 runlevel5.target, graphical.target 多用户,圖形界面。繼承級別3的服務,並啟動圖形界面服務。
6 runlevel6.target, reboot.target 重啟
emergency emergency.target 急救模式(Emergency shell)

切換當前運行目標[編輯 | 編輯原始碼]

systemd中,目標通過「目標單元」訪問。通過如下命令切換:

# systemctl isolate graphical.target

該命令僅更改當前運行目標,對下次啟動無影響。這等價於sysvinit中的telinit 3telinit 5命令。

更改開機默認啟動目標[編輯 | 編輯原始碼]

開機啟動的目標是default.target,默認符號連結到graphical.target(大致相當於原來的運行級別5)。

systemctl 檢查當前的默認啟動目標:

# systemctl get-default

systemctl修改default.target符號連結以變更開機默認啟動目標:

# systemctl set-default multi-user.target
Removed /etc/systemd/system/default.target.
Created symlink /etc/systemd/system/default.target -> /usr/lib/systemd/system/multi-user.target.

另一方法是向bootloader添加內核參數

  • systemd.unit=multi-user.target (大致相當於運行級別3)
  • systemd.unit=rescue.target (大致相當於運行級別1)

默認目標順序[編輯 | 編輯原始碼]

Systemd 根據下面順序選擇 default.target

  1. 上面的內核參數
  2. /etc/systemd/system/default.target 軟連結
  3. /usr/lib/systemd/system/default.target 軟連結

systemd組件[編輯 | 編輯原始碼]

systemd的部分組件如下:

systemd.mount - 掛載[編輯 | 編輯原始碼]

systemd負責掛載/etc/fstab中指定的分區和文件系統。systemd-fstab-generator(8)/etc/fstab中的所有條目翻譯為systemd單元。該過程將在系統啟動或系統管理器的配置被重新加載時執行。

systemd擴展了通常fstab的用法並提供了更多掛載選項。這些掛載選項可影響掛載點單元的依賴關係。例如,它們可以確保某一掛載操作只會在連接網絡或另一分區掛載後進行。systemd的特定掛載選項(大多以x-systemd.開頭)的完整列表見systemd.mount(5) § FSTAB

automounting是這些掛載選項的一個例子。它意味着資源只會在請求時掛載,而非在系統啟動時自動掛載,詳見fstab#通過 systemd 自動掛載

GPT分區自動掛載[編輯 | 編輯原始碼]

在UEFI啟動的系統上,當特定條件滿足時,systemd-gpt-auto-generator(8)會根據Discoverable Partitions Specification自動掛載GPT分區。因此,可在fstab中省略自動掛載的分區。此外,若根分區已被自動掛載,可省去內核命令行中的root=

使用GPT分區自動掛載的先決條件有:

  • 引導加載程序必須設置LoaderDevicePartUUIDEFI變量,以便識別使用的EFI系統分區。systemd-bootsystemd-stub(7)GRUB (使用grub-mkconfig生成grub.cfg),rEFInd(默認未啟用)支持該特性。若使用自定義的grub.cfg,則需要加載bli模塊。 可通過執行bootctl並檢查Boot loader sets ESP information的狀態進行確認,或在用Unified kernel image啟動時檢查Stub sets ESP information的狀態。
  • 根分區與EFI系統分區(ESP)必須在同一塊物理硬盤上。其它自動掛載的分區必須與根分區在同一塊物理硬盤上。這基本上意味着所有自動掛載分區必須和ESP分區在同一塊物理硬盤上。
  • 若需要/efi掛載點,則必須手動創建它。否則systemd-gpt-auto-generator將使用/boot作為掛載點。
警吿: 若使用GPT分區自動掛載,在現有系統上創建/efi時需小心行事。在下次啟動時,/efi將作為默認掛載點,這可能導致/boot目錄為空,使系統陷入不穩定狀態。若出現這種情況,將需要執行重新安裝內核、處理器微碼並重新生成initramfs等操作。
提示:某個分區的自動掛載可通過如下兩種方式關閉:修改分區的類型GUID或設置分區屬性的63"do not automount"位,詳見gdisk#Prevent GPT partition automounting
/var[編輯 | 編輯原始碼]

要自動掛載/var分區,分區的PARTUUID必須與以machine ID為密鑰,分區類型UUID(4d21b016-b534-45c2-a9fb-5c16e091fd2d)為消息的SHA256 HMAC哈希計算結果相同。可通過如下命令得到符合要求的PARTUUID:

$ systemd-id128 -u --app-specific=4d21b016-b534-45c2-a9fb-5c16e091fd2d machine-id
注意: systemd-id128(1)/etc/machine-id讀取machine ID,因此必須在系統安裝後才能得到符合要求的PARTUUID。

systemd-sysvcompat[編輯 | 編輯原始碼]

systemd-sysvcompat(由base需要)的主要工作是提供傳統的Linux init可執行文件。在由systemd控制的系統上,init只是一個到systemd可執行文件的符號連結。

此外,該包還提供了SysVinit用户可能需要的功能的4個快捷方式:halt(8)poweroff(8)reboot(8)shutdown(8)。這4個命令都是到systemctl的符號連結,其行為受systemd控制。因此,在#電源管理中提到的規則對它們同樣適用。

在由systemd控制的系統上,可通過init=內核啟動參數取消對System V的兼容性支持。詳見/bin/init is in systemd-sysvcompat ?

systemd-tmpfiles - 臨時文件[編輯 | 編輯原始碼]

systemd-tmpfiles創建,刪除並清理臨時文件/文件夾。systemd-tmpfiles讀取/etc/tmpfiles.d//usr/lib/tmpfiles.d/中的配置文件(前者比後者優先級高)以確定執行什麼操作。

這些配置文件通常與服務文件一同提供,並以/usr/lib/tmpfiles.d/<程序>.conf風格命名。例如,Samba守護進程需要/run/samba目錄存在且權限設置正確,因此samba中附帶了如下配置:

/usr/lib/tmpfiles.d/samba.conf
D /run/samba 0755 root root

配置文件也可能用於在啟動時向特定文件中寫入值。例如,要禁止系統從USB設備喚醒。在之前可使用/etc/rc.local在啟動時執行echo USBE > /proc/acpi/wakeup,而現在可以這麼做:

/etc/tmpfiles.d/disable-usb-wake.conf
#    Path                  Mode UID  GID  Age Argument
w    /proc/acpi/wakeup     -    -    -    -   USBE

詳見systemd-tmpfiles(8)tmpfiles.d(5)

注意: 若要向/sys中的配置文件寫入值,可能不能使用該方法。因為systemd-tmpfiles-setup服務可能在相關模塊加載前運行。在這種情況下,可先通過modinfo <模塊名>檢查該模塊是否可通過設定模塊參數來修改相應的選項。若有對應的模塊參數,可通過/etc/modprobe.d目錄中的配置文件設定相應參數以達到修改選項的目的。否則,將只能通過udev規則來在設備被識別時設定相應屬性。

小技巧[編輯 | 編輯原始碼]

GUI配置工具[編輯 | 編輯原始碼]

  • systemadm — 用於systemd單元的圖形化瀏覽器。可顯示單元列表,並能按類型篩選。
https://cgit.freedesktop.org/systemd/systemd-ui/ || systemd-ui
  • SystemdGenie — 基於KDE的systemd管理工具。
https://invent.kde.org/system/systemdgenie || systemdgenie

在網絡已連接後再啟動某服務[編輯 | 編輯原始碼]

如果需要將某服務延遲到網絡已連接後再啟動, 直接在 .service 文件中包含以下依賴項:

/etc/systemd/system/<單元名>.service
[Unit]
...
Wants=network-online.target
After=network-online.target
...

要使network-online.target正確反映網絡狀態,必須啟用所使用的網絡管理器的網絡等待服務。

  • 若使用NetworkManager, NetworkManager-wait-online.service應該與NetworkManager.service一同啟用。可通過systemctl is-enabled NetworkManager-wait-online.service進行檢查。若服務沒用啟用,重新啟用NetworkManager.service
  • 若使用netctl, 啟用netctl-wait-online.service (除非使用了netctl-auto; 詳見FS#75836)。
  • 若使用systemd-networkd, systemd-networkd-wait-online.service應該與systemd-networkd.service一同啟用。可通過systemctl is-enabled systemd-networkd-wait-online.service進行檢查。若服務沒用啟用,重新啟用systemd-networkd.service

如果需要更為詳細的解釋,請查看網絡配置同步點中的討論。

若某服務需要執行DNS查詢,其應該被排在nss-lookup.target後:

/etc/systemd/system/<單元名>.service
[Unit]
...
Wants=network-online.target
After=network-online.target nss-lookup.target
...

詳見systemd.special(7) § Special Passive System Units.

nss-lookup.target發揮作用,必須有一個單元通過Wants=nss-lookup.target引入nss-lookup.target,並通過Before=nss-lookup.target排在nss-lookup.target之前。一般情況下,上述配置由本地DNS解析器完成。

檢查哪一個正在運行的單元引入了nss-lookup.target

$ systemctl list-dependencies --reverse nss-lookup.target

默認啟用新安裝的單元[編輯 | 編輯原始碼]

Arch Linux附帶的/usr/lib/systemd/system-preset/99-default.preset包含disable *,這會導致默認情況下的systemctl preset是禁用所有新安裝的的單元。因此某個新包安裝後, 用户必須手動啟用新單元。

若上述行為不符合預期, 創建一個從/etc/systemd/system-preset/99-default.preset/dev/null的符號連結來覆蓋該配置文件,這會導致 systemctl preset無視單元類型直接啟用所有單元,除非systemctl preset的配置目錄中有文件另有聲明。用户單元不受影響。詳見systemd.preset(5)

注意: 若某個軟件包含有多個互相衝突的單元,默認啟用所有單元可能會出現問題。systemctl preset原本旨在供發行版或系統管理員使用。在有兩個衝突單元將被同時啟用的情況下,應明確在systemctl preset的配置文件中禁用其中一個單元,詳見systemd.preset(5)

應用程式環境沙盒[編輯 | 編輯原始碼]

可通過一個單元文件創建一個沙盒以在加固的虛擬環境中隔離應用程式及其進程。systemd充分利用namespaces,一系列允許/拒絕capabilitiescgroups,以通過可拓展的執行環境設置(systemd.exec(5))將進程禁錮在容器中。

將現有systemd單元文件進行沙盒化加固通常需要結合使用stracestderrjournalctl(1)等進行大量試驗。因此,最好先在上游文檔中搜索已完成的測試以作為試驗的基礎。

首先,最好通過以下命令獲得特定單元可能的加固選項作為基礎:

$ systemd-analyze security <单元>

關於如何使用 systemd 部署沙盒的一些示例:

通知失敗的單元[編輯 | 編輯原始碼]

本文或本章節可能需要合併到systemd/Timers#MAILTO

附註: 相同話題,不同解決方案(在 Talk:Systemd 中討論)

要在服務故障時發出通知,可在相應服務的單元文件中添加OnFailure=設置,這可通過#附加配置片段完成。若要在每一個服務的單元文件中添加該設置,可使用一個頂層附加配置片段,詳見systemd.unit(5)

為服務單元創建頂層附加配置片段:

/etc/systemd/system/service.d/toplevel-override.conf
[Unit]
OnFailure=failure-notification@%n

這將向每個服務單元文件中添加OnFailure=failure-notification@%n。若單元甲失敗,會啟動failure-notification@單元甲來遞送相關通知(或執行其它配置的動作)。

創建failure-notification@模板單元:

/etc/systemd/system/failure-notification@.service
[Unit]
Description=Send a notification about a failed systemd unit
After=network.target

[Service]
Type=simple
ExecStart=/path/to/failure-notification.sh %i

接下來可創建failure-notification.sh腳本,定義執行的操作或發送通知的方式(mail,gotify,xmpp等) 。%i將被替換為失敗單元的名稱並作為參數被傳遞給failure-notification.sh腳本。

為防止failure-notification@.service的實例啟動失敗時引發failure-notification@.service實例的遞歸性啟動,在failure-notification@.service單元的配置目錄下創建一個與頂層附加配置片段文件名相同的空的附加配置片段(該空的附加配置片段比全局附加配置片段優先級更高):

# mkdir -p /etc/systemd/system/failure-notification@.service.d
# touch /etc/systemd/system/failure-notification@.service.d/toplevel-override.conf

疑難解答[編輯 | 編輯原始碼]

尋找失敗的單元[編輯 | 編輯原始碼]

要尋找失敗的systemd單元,執行:

$ systemctl --state=failed

可通過它們的日誌查找失敗的原因。詳見systemd/Journal#過濾輸出

診斷系統啟動問題[編輯 | 編輯原始碼]

systemd有許多用於調試系統啟動過程中的問題的選項。要捕獲在systemd接管啟動流程前的日誌,參閱boot debugging。還可參閱freedesktop.org的系統調試文檔

診斷一個服務[編輯 | 編輯原始碼]

如果某個 systemd 服務的工作狀況不合預期或想了解發生了什麼的更多信息,將SYSTEMD_LOG_LEVEL環境變量的值設為debug。例如,要以調試模式運行systemd-networkd守護進程:

在此服務的配置文件中加入如下配置片段

[Service]
Environment=SYSTEMD_LOG_LEVEL=debug

或者手動設置該環境變量以達到相同效果:

# SYSTEMD_LOG_LEVEL=debug /lib/systemd/systemd-networkd

之後,重新啟動systemd-networkd並以-f/--follow啟動journalctl觀察該單元的日誌。

關機/重啟十分緩慢[編輯 | 編輯原始碼]

如果關機十分緩慢(甚至跟死機了一樣),很可能是某個拒不退出的服務在作怪。對於每個服務systemd會等待一段時間,然後再嘗試殺死它。要確定是否受到該問題影響,詳見systemd wiki中的這篇文章

一個常見的原因是存在一個擱置的關機或掛起進程。要確認是否為這種情況,執行下列命令中的任何一個並觀察有無類似的輸出:

# systemctl poweroff
Failed to power off system via logind: There's already a shutdown or sleep operation in progress
# systemctl list-jobs
  JOB UNIT                    TYPE  STATE
...
21593 systemd-suspend.service start running
21592 suspend.target          start waiting
..

該問題的解決方法是取消這些關機/掛起工作:

# systemctl cancel
# systemctl stop systemd-suspend.service

並再次嘗試關機或重啟。

一些短命進程似乎沒有產生任何日誌輸出[編輯 | 編輯原始碼]

對於短命的服務進程(啟動後迅速失敗),若以root身份執行journalctl -u <單元名>可能看不到任何輸出,此時可轉而使用進程的PID。例如,若systemd-modules-load.service單元失敗,通過systemctl status systemd-modules-load得到的對應進程PID為123,以root身份執行journalctl -b _PID=123或許就能得到日誌輸出。上述問題的原因是日誌元數據(如_SYSTEMD_UNIT_COMM)以異步方式被採集,且依賴於/proc中相應進程的目錄。修復該問題需要修改內核以通過socket提供需要的數據(類似於SCM_CREDENTIALS)。簡而言之,該問題是一個bug,只需記住由於systemd的設計,在啟動後迅速失敗的單元可能不會打印任何輸出到系統日誌中。

systemd-tmpfiles-setup.service在系統啟動時失敗[編輯 | 編輯原始碼]

systemd 219開始,/usr/lib/tmpfiles.d/systemd.conf/var/log/journal下的目錄指定了ACL選項。因此存放日誌的文件系統必須啟用ACL支持。

如何在存放/var/log/journal的文件系統上啟用ACL的説明見Access Control Lists#啟用 ACL

在遠程主機上關閉救援(emergency)模式[編輯 | 編輯原始碼]

當救援模式被觸發時,機器不會連接網絡,因此對於託管於Azure或Google Cloud的遠程主機,或許需要關閉救援模式。

要關閉救援模式,屏蔽emergency.serviceemergency.target

參見[編輯 | 編輯原始碼]