[轉]Systemd 入門教程:命令篇

dirgo發表於2024-06-12

原文地址:Systemd 入門教程:命令篇 - 阮一峰的網路日誌

Systemd 是 Linux 系統工具,用來啟動守護程序,已成為大多數發行版的標準配置。

本文介紹它的基本用法,分為上下兩篇。今天介紹它的主要命令,下一篇介紹如何用於實戰。

一、由來

歷史上,Linux 的啟動一直採用init程序。

下面的命令用來啟動服務。


$ sudo /etc/init.d/apache2 start
# 或者
$ service apache2 start

這種方法有兩個缺點。

一是啟動時間長。init程序是序列啟動,只有前一個程序啟動完,才會啟動下一個程序。

二是啟動指令碼複雜。init程序只是執行啟動指令碼,不管其他事情。指令碼需要自己處理各種情況,這往往使得指令碼變得很長。

二、Systemd 概述

Systemd 就是為了解決這些問題而誕生的。它的設計目標是,為系統的啟動和管理提供一套完整的解決方案。

根據 Linux 慣例,字母d是守護程序(daemon)的縮寫。 Systemd 這個名字的含義,就是它要守護整個系統。

(上圖為 Systemd 作者 Lennart Poettering

使用了 Systemd,就不需要再用init了。Systemd 取代了initd,成為系統的第一個程序(PID 等於 1),其他程序都是它的子程序。


$ systemctl --version

上面的命令檢視 Systemd 的版本。

Systemd 的優點是功能強大,使用方便,缺點是體系龐大,非常複雜。事實上,現在還有很多人反對使用 Systemd,理由就是它過於複雜,與作業系統的其他部分強耦合,違反"keep simple, keep stupid"的Unix 哲學

(上圖為 Systemd 架構圖)

三、系統管理

Systemd 並不是一個命令,而是一組命令,涉及到系統管理的方方面面。

3.1 systemctl

systemctl是 Systemd 的主命令,用於管理系統。


# 重啟系統
$ sudo systemctl reboot

# 關閉系統,切斷電源
$ sudo systemctl poweroff

# CPU停止工作
$ sudo systemctl halt

# 暫停系統
$ sudo systemctl suspend

# 讓系統進入冬眠狀態
$ sudo systemctl hibernate

# 讓系統進入互動式休眠狀態
$ sudo systemctl hybrid-sleep

# 啟動進入救援狀態(單使用者狀態)
$ sudo systemctl rescue

3.2 systemd-analyze

systemd-analyze命令用於檢視啟動耗時。


# 檢視啟動耗時
$ systemd-analyze                                                                                       

# 檢視每個服務的啟動耗時
$ systemd-analyze blame

# 顯示瀑布狀的啟動過程流
$ systemd-analyze critical-chain

# 顯示指定服務的啟動流
$ systemd-analyze critical-chain atd.service

3.3 hostnamectl

hostnamectl命令用於檢視當前主機的資訊。


# 顯示當前主機的資訊
$ hostnamectl

# 設定主機名。
$ sudo hostnamectl set-hostname rhel7

3.4 localectl

localectl命令用於檢視本地化設定。


# 檢視本地化設定
$ localectl

# 設定本地化引數。
$ sudo localectl set-locale LANG=en_GB.utf8
$ sudo localectl set-keymap en_GB

3.5 timedatectl

timedatectl命令用於檢視當前時區設定。


# 檢視當前時區設定
$ timedatectl

# 顯示所有可用的時區
$ timedatectl list-timezones                                                                                   

# 設定當前時區
$ sudo timedatectl set-timezone America/New_York
$ sudo timedatectl set-time YYYY-MM-DD
$ sudo timedatectl set-time HH:MM:SS

3.6 loginctl

loginctl命令用於檢視當前登入的使用者。


# 列出當前session
$ loginctl list-sessions

# 列出當前登入使用者
$ loginctl list-users

# 列出顯示指定使用者的資訊
$ loginctl show-user ruanyf

四、Unit

4.1 含義

Systemd 可以管理所有系統資源。不同的資源統稱為 Unit(單位)。

Unit 一共分成12種。

  • Service unit:系統服務
  • Target unit:多個 Unit 構成的一個組
  • Device Unit:硬體裝置
  • Mount Unit:檔案系統的掛載點
  • Automount Unit:自動掛載點
  • Path Unit:檔案或路徑
  • Scope Unit:不是由 Systemd 啟動的外部程序
  • Slice Unit:程序組
  • Snapshot Unit:Systemd 快照,可以切回某個快照
  • Socket Unit:程序間通訊的 socket
  • Swap Unit:swap 檔案
  • Timer Unit:定時器

systemctl list-units命令可以檢視當前系統的所有 Unit 。


# 列出正在執行的 Unit
$ systemctl list-units

# 列出所有Unit,包括沒有找到配置檔案的或者啟動失敗的
$ systemctl list-units --all

# 列出所有沒有執行的 Unit
$ systemctl list-units --all --state=inactive

# 列出所有載入失敗的 Unit
$ systemctl list-units --failed

# 列出所有正在執行的、型別為 service 的 Unit
$ systemctl list-units --type=service

4.2 Unit 的狀態

systemctl status命令用於檢視系統狀態和單個 Unit 的狀態。


# 顯示系統狀態
$ systemctl status

# 顯示單個 Unit 的狀態
$ sysystemctl status bluetooth.service

# 顯示遠端主機的某個 Unit 的狀態
$ systemctl -H root@rhel7.example.com status httpd.service

除了status命令,systemctl還提供了三個查詢狀態的簡單方法,主要供指令碼內部的判斷語句使用。


# 顯示某個 Unit 是否正在執行
$ systemctl is-active application.service

# 顯示某個 Unit 是否處於啟動失敗狀態
$ systemctl is-failed application.service

# 顯示某個 Unit 服務是否建立了啟動連結
$ systemctl is-enabled application.service

4.3 Unit 管理

對於使用者來說,最常用的是下面這些命令,用於啟動和停止 Unit(主要是 service)。


# 立即啟動一個服務
$ sudo systemctl start apache.service

# 立即停止一個服務
$ sudo systemctl stop apache.service

# 重啟一個服務
$ sudo systemctl restart apache.service

# 殺死一個服務的所有子程序
$ sudo systemctl kill apache.service

# 重新載入一個服務的配置檔案
$ sudo systemctl reload apache.service

# 過載所有修改過的配置檔案
$ sudo systemctl daemon-reload

# 顯示某個 Unit 的所有底層引數
$ systemctl show httpd.service

# 顯示某個 Unit 的指定屬性的值
$ systemctl show -p CPUShares httpd.service

# 設定某個 Unit 的指定屬性
$ sudo systemctl set-property httpd.service CPUShares=500

4.4 依賴關係

Unit 之間存在依賴關係:A 依賴於 B,就意味著 Systemd 在啟動 A 的時候,同時會去啟動 B。

systemctl list-dependencies命令列出一個 Unit 的所有依賴。


$ systemctl list-dependencies nginx.service

上面命令的輸出結果之中,有些依賴是 Target 型別(詳見下文),預設不會展開顯示。如果要展開 Target,就需要使用--all引數。


$ systemctl list-dependencies --all nginx.service

五、Unit 的配置檔案

5.1 概述

每一個 Unit 都有一個配置檔案,告訴 Systemd 怎麼啟動這個 Unit 。

Systemd 預設從目錄/etc/systemd/system/讀取配置檔案。但是,裡面存放的大部分檔案都是符號連結,指向目錄/usr/lib/systemd/system/,真正的配置檔案存放在那個目錄。

systemctl enable命令用於在上面兩個目錄之間,建立符號連結關係。


$ sudo systemctl enable clamd@scan.service
# 等同於
$ sudo ln -s '/usr/lib/systemd/system/clamd@scan.service' '/etc/systemd/system/multi-user.target.wants/clamd@scan.service'

如果配置檔案裡面設定了開機啟動,systemctl enable命令相當於啟用開機啟動。

與之對應的,systemctl disable命令用於在兩個目錄之間,撤銷符號連結關係,相當於撤銷開機啟動。


$ sudo systemctl disable clamd@scan.service

配置檔案的字尾名,就是該 Unit 的種類,比如sshd.socket。如果省略,Systemd 預設字尾名為.service,所以sshd會被理解成sshd.service

5.2 配置檔案的狀態

systemctl list-unit-files命令用於列出所有配置檔案。


# 列出所有配置檔案
$ systemctl list-unit-files

# 列出指定型別的配置檔案
$ systemctl list-unit-files --type=service

這個命令會輸出一個列表。


$ systemctl list-unit-files

UNIT FILE              STATE
chronyd.service        enabled
clamd@.service         static
clamd@scan.service     disabled

這個列表顯示每個配置檔案的狀態,一共有四種。

  • enabled:已建立啟動連結
  • disabled:沒建立啟動連結
  • static:該配置檔案沒有[Install]部分(無法執行),只能作為其他配置檔案的依賴
  • masked:該配置檔案被禁止建立啟動連結

注意,從配置檔案的狀態無法看出,該 Unit 是否正在執行。這必須執行前面提到的systemctl status命令。


$ systemctl status bluetooth.service

一旦修改配置檔案,就要讓 SystemD 重新載入配置檔案,然後重新啟動,否則修改不會生效。


$ sudo systemctl daemon-reload
$ sudo systemctl restart httpd.service

5.3 配置檔案的格式

配置檔案就是普通的文字檔案,可以用文字編輯器開啟。

systemctl cat命令可以檢視配置檔案的內容。


$ systemctl cat atd.service

[Unit]
Description=ATD daemon

[Service]
Type=forking
ExecStart=/usr/bin/atd

[Install]
WantedBy=multi-user.target

從上面的輸出可以看到,配置檔案分成幾個區塊。每個區塊的第一行,是用方括號表示的區別名,比如[Unit]。注意,配置檔案的區塊名和欄位名,都是大小寫敏感的。

每個區塊內部是一些等號連線的鍵值對。


[Section]
Directive1=value
Directive2=value

. . .

注意,鍵值對的等號兩側不能有空格。

5.4 配置檔案的區塊

[Unit]區塊通常是配置檔案的第一個區塊,用來定義 Unit 的後設資料,以及配置與其他 Unit 的關係。它的主要欄位如下。

  • Description:簡短描述
  • Documentation:文件地址
  • Requires:當前 Unit 依賴的其他 Unit,如果它們沒有執行,當前 Unit 會啟動失敗
  • Wants:與當前 Unit 配合的其他 Unit,如果它們沒有執行,當前 Unit 不會啟動失敗
  • BindsTo:與Requires類似,它指定的 Unit 如果退出,會導致當前 Unit 停止執行
  • Before:如果該欄位指定的 Unit 也要啟動,那麼必須在當前 Unit 之後啟動
  • After:如果該欄位指定的 Unit 也要啟動,那麼必須在當前 Unit 之前啟動
  • Conflicts:這裡指定的 Unit 不能與當前 Unit 同時執行
  • Condition...:當前 Unit 執行必須滿足的條件,否則不會執行
  • Assert...:當前 Unit 執行必須滿足的條件,否則會報啟動失敗

[Install]通常是配置檔案的最後一個區塊,用來定義如何啟動,以及是否開機啟動。它的主要欄位如下。

  • WantedBy:它的值是一個或多個 Target,當前 Unit 啟用時(enable)符號連結會放入/etc/systemd/system目錄下面以 Target 名 + .wants字尾構成的子目錄中
  • RequiredBy:它的值是一個或多個 Target,當前 Unit 啟用時,符號連結會放入/etc/systemd/system目錄下面以 Target 名 + .required字尾構成的子目錄中
  • Alias:當前 Unit 可用於啟動的別名
  • Also:當前 Unit 啟用(enable)時,會被同時啟用的其他 Unit

[Service]區塊用來 Service 的配置,只有 Service 型別的 Unit 才有這個區塊。它的主要欄位如下。

  • Type:定義啟動時的程序行為。它有以下幾種值。
  • Type=simple:預設值,執行ExecStart指定的命令,啟動主程序
  • Type=forking:以 fork 方式從父程序建立子程序,建立後父程序會立即退出
  • Type=oneshot:一次性程序,Systemd 會等當前服務退出,再繼續往下執行
  • Type=dbus:當前服務透過D-Bus啟動
  • Type=notify:當前服務啟動完畢,會通知Systemd,再繼續往下執行
  • Type=idle:若有其他任務執行完畢,當前服務才會執行
  • ExecStart:啟動當前服務的命令
  • ExecStartPre:啟動當前服務之前執行的命令
  • ExecStartPost:啟動當前服務之後執行的命令
  • ExecReload:重啟當前服務時執行的命令
  • ExecStop:停止當前服務時執行的命令
  • ExecStopPost:停止當其服務之後執行的命令
  • RestartSec:自動重啟當前服務間隔的秒數
  • Restart:定義何種情況 Systemd 會自動重啟當前服務,可能的值包括always(總是重啟)、on-successon-failureon-abnormalon-aborton-watchdog
  • TimeoutSec:定義 Systemd 停止當前服務之前等待的秒數
  • Environment:指定環境變數

Unit 配置檔案的完整欄位清單,請參考官方文件

六、Target

啟動計算機的時候,需要啟動大量的 Unit。如果每一次啟動,都要一一寫明本次啟動需要哪些 Unit,顯然非常不方便。Systemd 的解決方案就是 Target。

簡單說,Target 就是一個 Unit 組,包含許多相關的 Unit 。啟動某個 Target 的時候,Systemd 就會啟動裡面所有的 Unit。從這個意義上說,Target 這個概念類似於"狀態點",啟動某個 Target 就好比啟動到某種狀態。

傳統的init啟動模式裡面,有 RunLevel 的概念,跟 Target 的作用很類似。不同的是,RunLevel 是互斥的,不可能多個 RunLevel 同時啟動,但是多個 Target 可以同時啟動。


# 檢視當前系統的所有 Target
$ systemctl list-unit-files --type=target

# 檢視一個 Target 包含的所有 Unit
$ systemctl list-dependencies multi-user.target

# 檢視啟動時的預設 Target
$ systemctl get-default

# 設定啟動時的預設 Target
$ sudo systemctl set-default multi-user.target

# 切換 Target 時,預設不關閉前一個 Target 啟動的程序,
# systemctl isolate 命令改變這種行為,
# 關閉前一個 Target 裡面所有不屬於後一個 Target 的程序
$ sudo systemctl isolate multi-user.target

Target 與 傳統 RunLevel 的對應關係如下。


Traditional runlevel      New target name     Symbolically linked to...

Runlevel 0           |    runlevel0.target -> poweroff.target
Runlevel 1           |    runlevel1.target -> rescue.target
Runlevel 2           |    runlevel2.target -> multi-user.target
Runlevel 3           |    runlevel3.target -> multi-user.target
Runlevel 4           |    runlevel4.target -> multi-user.target
Runlevel 5           |    runlevel5.target -> graphical.target
Runlevel 6           |    runlevel6.target -> reboot.target

它與init程序的主要差別如下。

(1)預設的 RunLevel(在/etc/inittab檔案設定)現在被預設的 Target 取代,位置是/etc/systemd/system/default.target,通常符號連結到graphical.target(圖形介面)或者multi-user.target(多使用者命令列)。

(2)啟動指令碼的位置,以前是/etc/init.d目錄,符號連結到不同的 RunLevel 目錄 (比如/etc/rc3.d/etc/rc5.d等),現在則存放在/lib/systemd/system/etc/systemd/system目錄。

(3)配置檔案的位置,以前init程序的配置檔案是/etc/inittab,各種服務的配置檔案存放在/etc/sysconfig目錄。現在的配置檔案主要存放在/lib/systemd目錄,在/etc/systemd目錄裡面的修改可以覆蓋原始設定。

七、日誌管理

Systemd 統一管理所有 Unit 的啟動日誌。帶來的好處就是,可以只用journalctl一個命令,檢視所有日誌(核心日誌和應用日誌)。日誌的配置檔案是/etc/systemd/journald.conf

journalctl功能強大,用法非常多。


# 檢視所有日誌(預設情況下 ,只儲存本次啟動的日誌)
$ sudo journalctl

# 檢視核心日誌(不顯示應用日誌)
$ sudo journalctl -k

# 檢視系統本次啟動的日誌
$ sudo journalctl -b
$ sudo journalctl -b -0

# 檢視上一次啟動的日誌(需更改設定)
$ sudo journalctl -b -1

# 檢視指定時間的日誌
$ sudo journalctl --since="2012-10-30 18:17:16"
$ sudo journalctl --since "20 min ago"
$ sudo journalctl --since yesterday
$ sudo journalctl --since "2015-01-10" --until "2015-01-11 03:00"
$ sudo journalctl --since 09:00 --until "1 hour ago"

# 顯示尾部的最新10行日誌
$ sudo journalctl -n

# 顯示尾部指定行數的日誌
$ sudo journalctl -n 20

# 實時滾動顯示最新日誌
$ sudo journalctl -f

# 檢視指定服務的日誌
$ sudo journalctl /usr/lib/systemd/systemd

# 檢視指定程序的日誌
$ sudo journalctl _PID=1

# 檢視某個路徑的指令碼的日誌
$ sudo journalctl /usr/bin/bash

# 檢視指定使用者的日誌
$ sudo journalctl _UID=33 --since today

# 檢視某個 Unit 的日誌
$ sudo journalctl -u nginx.service
$ sudo journalctl -u nginx.service --since today

# 實時滾動顯示某個 Unit 的最新日誌
$ sudo journalctl -u nginx.service -f

# 合併顯示多個 Unit 的日誌
$ journalctl -u nginx.service -u php-fpm.service --since today

# 檢視指定優先順序(及其以上級別)的日誌,共有8級
# 0: emerg
# 1: alert
# 2: crit
# 3: err
# 4: warning
# 5: notice
# 6: info
# 7: debug
$ sudo journalctl -p err -b

# 日誌預設分頁輸出,--no-pager 改為正常的標準輸出
$ sudo journalctl --no-pager

# 以 JSON 格式(單行)輸出
$ sudo journalctl -b -u nginx.service -o json

# 以 JSON 格式(多行)輸出,可讀性更好
$ sudo journalctl -b -u nginx.serviceqq
 -o json-pretty

# 顯示日誌佔據的硬碟空間
$ sudo journalctl --disk-usage

# 指定日誌檔案佔據的最大空間
$ sudo journalctl --vacuum-size=1G

# 指定日誌檔案儲存多久
$ sudo journalctl --vacuum-time=1years

(完)

相關文章