筆者在日常的開發和使用中,經常需要寫大量的本地服務並且進行管理,例如定時執行,定時查錯,非同步排隊以及資料庫的備份歸檔工作。因此systemd作為一個服務管理神器就成了必備工具。今天就寫一篇小工具文來介紹一下這東西的用法。
一、SYSTEMD基本工具
監控和控制systemd主要使用的指令是systemctl。主要是從來看系統狀態、服務狀態,以及管理系統和服務。
同時systemctl可以通過ssh連線遠端控制其他主機,使用 systemctl -H <username>@<URL>
格式。
1、分析系統狀態
顯示系統狀態: $ systemctl status
輸出啟用的單元列表: $systemctl
或者 $systemctl list-units
輸出執行失敗的單元: $ systemctl —failed
所有可用的單元都在 /etc/systemd/system/
(優先度高) 和 /usr/lib/systemd/system/
(優先度低)。
檢視所有已安裝的服務:$ systemctl list-unit-files
2、使用單元
一個單元配置檔案可以描述如下內容之一:系統服務(.service)、掛載點(.mount)、sockets(.sockets) 、系統裝置(.device)、交換分割槽(.swap)、檔案路徑(.path)、啟動目標(.target)、由 systemd 管理的計時器(.timer)。
我們通常在用systemctl呼叫單元的時候一般要單元檔案的全名。也就是帶上述字尾的那些。
如果不帶副檔名的話systemctl會預設成是.service
檔案,所以為了不發生意外一般還是推薦把名字打全了。
掛載點和裝置會自動轉化為對應的字尾單元,比如/home就等價於home.mount
, /dev/sda等價於dev-sda.device
。
systemctl在enable、disable、mask子命令裡面增加了--now
選項,可以啟用同時啟動服務,啟用同時停止服務等。
立刻啟用單元:$ systemctl start <unit>
立刻停止單元:$ systemctl stop <unit>
重啟單元:$ systemctl restart <unit>
重新載入配置:$ systemctl reload <unit>
輸出單元執行的狀態:$ systemctl status <unit>
檢測單元是否為自動啟動:$ systemctl is-enabled <unit>
設定為開機自動啟用單元:$ systemctl enable <unit>
設定為開機自動啟用單元並現在立刻啟動:$ systemctl enable --now <unit>
取消開機自動啟用單元:$ systemctl disable <unit>
禁用一個單元:$ systemctl mask <unit>
取消禁用一個單元:$ systemctl unmask <unit>
顯示單元的手冊頁(前提是由unit提供):$ systemctl help <unit>
重新載入整個systemd的系統配置並掃描unit檔案的變動:$ systemctl daemon-reload
3、電源管理
注意:需要安裝了polkit以後才能用普通使用者身份進行電源管理。
重啟:$ systemctl reboot
退出系統並關閉電源:$ systemctl poweroff
待機:$ systemctl suspend
休眠:$ systemctl hibernate
混合休眠模式:$ systemctl hybrid-sleep
二、編寫unit檔案
systemd的unit書寫語法來源於XDG桌面配置檔案,最初則來自於ini檔案。
通過指令 $ systemctl show --property=UnitPath
可以按照優先順序檢視unit載入目錄。
/usr/lib/systemd/system/
:軟體包安裝的單元
/etc/systemd/system/
:系統管理員安裝的單元
1、處理依賴關係
主要有三類以外關係處理配置:
Requires=B
:啟動必須依賴單元B
Wants=B
:啟動依賴單元B(可選)
After=B
:本單元在B執行後再執行
注意的是Requires和Want並沒有包含After屬性,如果不宣告After則預設同時啟動。
2、服務型別
Type=simple
:(預設值) systemd認為該服務將立即啟動。服務程式不會 fork 。
Type=forking
:systemd認為當該服務程式fork,且父程式退出後服務啟動成功。使用此啟動型別應同時指定 PIDFile=
,以便 systemd 能夠跟蹤服務的主程式。
Type=oneshot
:這一選項適用於只執行一項任務、隨後立即退出的服務。可能需要同時設定 RemainAfterExit=yes
使 systemd 在服務程式退出之後仍然認為服務處於啟用狀態。
Type=notify
:與 Type=simple
相同,但約定服務會在就緒後向 systemd 傳送一個訊號。
Type=dbus
:若以此方式啟動,當指定的 BusName 出現在DBus系統匯流排上時,systemd認為服務就緒。
Type=idle
:systemd會等待所有任務處理完成後,才開始執行 idle 型別的單元。其他行為與 Type=simple 類似。
3、修改現有的unit
直接修改檔案並載入:$ systemctl daemon-reload
如果需要替換unit檔案,則重新啟用單元:$ systemctl reenable <unit>
或者執行:$ systemctl edit --full unit
附加配置片段:$ systemctl edit <unit>
這個操作會在編輯器裡面開啟/etc/systemd/system/<unit>.d/override.conf
重置到軟體包版本:$ systemctl revert <unit>
三、目標 Target
目標是一個類似於執行界別的概念。一些目標能繼承其他目標的服務並且啟動新的服務,systemd預設提供了一部分類似於sysvinit執行級別的target。
獲取當前的target:$ systemctl list-units --type=target 建立自定義target:我們可以新建一個/etc/systemd/system/.target。然後再建立目錄/etc/systemd/system/.want,並且在裡面加入需要啟動的伺服器連線(指向/usr/lib/systemd/system/)
SysV Level | 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) |
切換當前執行目標:$ systemctl isolate <target>.target
這個指令僅僅改變當前執行的target,不會對啟動有影響
更改開機預設啟動target:
檢查當前的啟動target:$ systemctl get-default
用systemctl修改default.target來變更開機啟動target:$ systemctl set-default multi-user.target
四、臨時檔案
/usr/lib/tmpfiles.d/
和 /etc/tmpfiles.d/
中的檔案描述了 systemd-tmpfiles 如何建立、清理、刪除臨時檔案和目錄,這些檔案和目錄通常存放在 /run
和 /tmp
中。配置檔名稱為 /etc/tmpfiles.d/<program>.conf
。此處的配置能覆蓋 /usr/lib/tmpfiles.d/
目錄中的同名配置。
臨時檔案一般和服務檔案同時提供,來生成守護程式需要的檔案和目錄。也可以在開機的時候往特定的檔案寫入一些內容。
五、定時器
1、服務單元
定時器是一個以.timer
為結尾的unit配置檔案,包含了systemd控制和監督的資訊。很多時候可以替代crontab並且有更強的功能。
每一個.timer
檔案在同一個目錄都有一個對應的.service
檔案。.timer用來啟用並控制.service
檔案。.service
檔案不需要有[Install]
,這部分由.timer
單元管理。
2、管理
使用.timer
的方法也和其他unit一樣,enable或者start即可。
檢視已有的定時器使用:$ systemctl list-timers
檢視所有的定時器(包括非活動的):$ systemctl list-timers --all
3、示例
下面是兩個timer單元的例子。
單調定時器:
/etc/systemd/system/example.timer
----------------------------------
[Unit]
Description=Run example weekly and on boot ## 檔案描述
[Timer]
OnBootSec=15min ## 開機後多久啟動
OnUnitActiveSec=1w ## 執行間隔t
[Install]
WantedBy=timers.target ## 目標service單元
複製程式碼
實時定時器: 定義一個定時執行,且上次未執行就立刻執行的timer
/etc/systemd/system/foo.timer
-----------------------------------
[Unit]
Description=Run foo weekly
[Timer]
OnCalendar=weekly
Persistent=true
[Install]
WantedBy=timers.target
複製程式碼
4、替代crontab探討
優勢主要在於,每個任務都有自己的systemd服務。從而:
- 任務可以獨立於定時器,便於測試。
- 任務可以執行在特殊情況下。
- 任務可以使用cgroups的特性。
- 任務可以依賴於其他systemd unit。
- 任務記錄在systemd日誌裡,便於除錯排查。
同時也可以和crontab一樣在unit失效的時候傳送一個email。不過現在一般都用其他開源監控工具,更好用。
六、掛載
因為systemd也負責按 /etc/fstab 掛在目錄,所以在系統重啟或者重新載入系統管理器的時候,systemd-fstab-generator會把/etc/fstab裡面的配置轉化為systemd unit。這些設定具體來說由 x-systemd元件來配置。
七、日誌
systemd提供了自己的日至系統。
讀取日誌:$ journalctl
預設情況下(當 Storage= 在檔案 /etc/systemd/journald.conf
中被設定為 auto),日誌記錄將被寫入 /var/log/journal/
。該目錄是 systemd 軟體包的一部分。
1、優先順序
日誌系統內有日誌的優先順序區分,下面為對應介紹:
優先順序 | 介紹 |
---|---|
優先順序0 | Emergency(emerg) |
優先順序1 | Alert(alert) |
優先順序2 | Critical(crit) |
優先順序3 | Error(err) |
優先順序4 | Warning(warning) |
優先順序5 | Notice(notice) |
優先順序6 | Informational(info) |
優先順序7 | Debug(debug) |
2、功能
在日誌系統中,各個日誌都會用編碼來指代一定的功能區域。下面為對應介紹:
編碼 | 內容(縮寫) |
---|---|
功能編碼0 | kernel-message(kern) |
功能編碼1 | user-level-message(user) |
功能編碼2 | mail-system(mail) |
功能編碼3 | system-daemon(daemon) |
功能編碼4 | authorization-messages(auth) |
功能編碼5 | syslog(syslog) |
功能編碼6 | line-printer-subsystem(lpr) |
功能編碼7 | network-news-subsystem(news) |
功能編碼8 | uucp-subsystem(uucp) |
功能編碼9 | clock-daemon |
功能編碼10 | authpriv(authpri) |
功能編碼11 | ftp-daemon(ftp) |
功能編碼12 | tp-subsyst |
功能編碼13 | log-audit |
功能編碼14 | log-alert |
功能編碼15 | crontab(cron) |
功能編碼16-23 | local0-7 |
3、過濾輸出
Journalctl可以過濾欄位輸出,下面為常用操作。
顯示本次啟動後所有日誌:$ journalctl -b
或者 $ journalctl -b -0
顯示上次啟動後的日誌:$ journalctl -b -1
顯示上上次啟動後的日誌:$ journalctl -b -2
只顯示錯誤衝突和重要告警資訊:$ journalctl -p err..alert
或者用編號表示 $ journalctl -p 3..1
顯示某個時間開始的訊息:$ journalctl --since="2019-01-01 00:00:00"
顯示最新的訊息:$ journalctl -f
顯示特定程式的所有訊息:$ journalctl /usr/lib/systemd/systemd
顯示某程式的所有資訊:$ journalctl _PID=12345
顯示指定unit的所有資訊:$ journalctl -u mydumper-archive.service
指定核心快取訊息:$ journalctl -k
顯示auth.log當前量:$ journalctl -f -1 SYSLOG_FACILITY=10
4、日誌大小限制
預設情況下,systemd日誌的最大限制是所在FS容量的10%。但是我們可以通過修改來改變最大限制:
/etc/systemd/journald.conf
--------------------------------
SystemMaxUse=50M
複製程式碼
同時也可以通過配置片段而非全域性來進行設定:
/etc/systemd/journald.conf.d/00-journal-size.conf
-----------------------------------
[Journal]
SystemMaxUse=50M
複製程式碼
5、配合syslog
systemd提供了socket:/run/systemd/journal/syslog
,來相容傳統日誌服務。如果要讓傳統的日誌服務工作,則要用這個socket來替代/dev/log。如果是使用rsyslog,則不用更改。
設定開機啟動syslog-ng:$ systemctl enable syslog-ng
6、清理日誌
所有的日誌都存放在/var/log/journal
,這個目錄下其實rm也可以用來清理,但是不推薦。
下面的方法比較推薦。
清理日誌到小於100MB:$ journalctl --vacuum-size=100M
清理最早兩週前的日誌:$ journalctl --vacuum-time=2weeks
八、疑難和差錯
1、尋找錯誤
這個案例中我們以mydumper-ro.service為例
通過systemd尋找失敗的服務:$ systemctl --state=failed
或者用systemd訊息:$ journalctl -fp err
找到了錯誤unit後檢視更多資訊:$ systemctl status mydumper-ro
查到這個unit的PID是12345之後:$ journalctl -b _PID=12345
發現部分核心模組的配置檔案有問題:$ ls -Al /etc/mydumper-ro.d
修復錯誤,最後重新啟動服務即可。
2、診斷啟動問題
在配置中使用核心引數引導:systemd.log_level=debug systemd.log_target=kmsg log_buf_len=1M
3、診斷一個服務
在服務的配置檔案中加入診斷選項。
[Service]
Environment=SYSTEMD_LOG_LEVEL=debug
複製程式碼
4、短執行時間程式無日誌
有時候某些短時間程式沒有任何輸出,沒有日誌。這個時候應該使用PID來查詢。
5、禁止程式崩潰是轉儲記憶體
需要使用舊的記憶體轉儲則在對應的服務的配置檔案中加入下面的設定:
/etc/sysctl.d/49-coredump.conf
-----------------------------------
kernel.core_pattern = core
kernel.core_uses_pid = 0
複製程式碼