# Systemd
- d for daemon
- systemd也是一種**init系統**,所謂的init系統就是kernel啟動以後,會執行的**pid 1號程式**
- OS啟動後需要啟動很多服務,最後才能進入可提供服務的狀態
* 如何知道有哪些服務?How to List/Enable/Disable/Start/Stop/Restart/Reload
* 我們想確認一個服務是否正常查詢服務的log
* 假設我自己想架一個簡單的網站,如何寫一個服務設定檔,在開機時自動啟動。假設啟動HTTP伺服器需要等網路連上線,我該怎麼寫相依性
* 如何使用systemctl 關機/重新開機
* systemd裡面那麼多的Target有什麼用?他們跟以前的runlevel有什麼關係,什麼是runlevel
systemd還包含了一些服務,比較會用到的像是
- systemd-networkd (他和NetworkManager和netplan的關係是什麼)
- systemd-resolved
- systemd-timesyncd
- systemd-hostnamed
- systemd-journald

systemd也提供了對cgroup接口的封裝。cgroup是kernel提供用於控制資源和程式隔離的功能,透過cgroup才真正實現了容器化。無論是docker/containerd追溯到最後都是cgroup。你可以使用systemd-cgls或systemd-cgtop來觀察由systemd管理的容器
## Systemd
Linux 系統開機流程的最後(kernel 載入後),會啟動 Systemd,並由它啟動系統上的其他必要的 process,例如:
- 開機後必須執行的程式(初始化系統資源、裝置)、系統必要的背景程式以及使用者安裝的
- 會持續執行的應用程式 (Server 端的軟體或是背景服務)
Systemd 在開機流程結束後,會持續在背景執行,並監控、管理所有由它管理的系統資源,使用者也可以透過 Systemd 的 `systemctl` 指令操作、控制這些系統資源
- **優點**
- 提供統一的管理介面和日誌系統
- **會管理相依性**,會依照正確順序啟動服務
- E.g., HTTP Server 要在 Network 之後啟動
- 可以**並行(concurrency)啟動多個服務**,當服務間沒有相依性,則可以同時啟動,加快啟動速度
- 使用 [cgroup](https://access.redhat.com/documentation/zh-tw/red_hat_enterprise_linux/6/html/resource_management_guide/ch01) 管理 process,可以控制所有分支出來的 process
- 整合常用的服務和功能,使用者不需要自己安裝和設定,例如時間同步、日誌系統 (logger)、服務分析工具...
- **缺點**
- Systemd 相當龐大且複雜
- 高度依賴 Linux 上的某些功能,無法移植到其他平台
- cgroup
- dBus
- ...
### Unit
- Systemd 管理的資源或服務稱為 unit
- 每一個 unit 都有一個對應的 **unit file** 作為設定檔
- Systemd 會根據 unit file 的內容啟動每個 unit
Unit 分為多個類型 (括號中的是對應的 unit file 副檔名)
- **Service Units** (.service): 系統上持續運行的服務或應用程式
- SSH Server: `sshd.service`
- Apache Server: `httpd.service`
- **Socket Units** (.socket): 管理系統上的 socket,用於 process 之間的通信
- 本身不會啟動服務,而是監聽特定的 port,並轉發給對應的 service unit
- **Target Units** (.target): 定義一組相關聯的 unit,並且可以一次啟動/停止這組 units
- 將某些 unit 組合成一個群組,以便於管理
- 用來當作 synchronization point
- **Path Units** (.path): 監控檔案、目錄的變化,並觸發其他的 unit
- **Timer Units** (.timer): 定時執行的任務或服務
- **Device Units** (.device): 監控、管理硬體裝置 (*/dev* 底下的裝置)
- **Mount Units** (.mount): 定義和管理掛載點,
- **Automount Units** (.automount): 管理自動掛載的文件系統,只有在必要時才會掛載系統,減少資源使用
- **Swap Units** (.swap): 管理 swap 磁區
- **Slice Units** (.slice): 管理系統的資源
- **Scope Units** (.scope): 由 Systemd 自動產生的檔案
- **Snapshot Units** (.snapshot): 儲存 Systemd 本身的快照,讓 Systemd 可以隨時復原回某狀態
:::warning
在不同發行版上,支援的類型可能不同,執行
```
systemctl -t help
```
查看系統上支援的 unit 類型
:::
### Unit File
- Unit File 就是 unit 的設定檔
- 每個 unit file 對應一個由 Systemd 管理的 unit,其中包含一個 unit 的所有設定和資訊
- Unit file 的副檔名表示該 unit 的類型
- Unit file 通常放在兩個目錄中
- */usr/lib/systemd/system*
- 通常是安裝 package 時,自動建置的 unit file
- Package 在安裝時會把預設的 unit file 複製到該目錄中
- */etc/systemd/system*
- 自訂服務的 unit file
- 通常是一些由使用者手動建立 unit file
## systemctl 管理系統
- 重啟系統 (reboot)
```
systemctl reboot
```
- 關閉電源 (poweroff)
```
systemctl poweroff
```
- 會正確的執行關機流程
- 通常關機時用 `poweroff`,而不用 `halt`
- 停止系統 (halt)
```
systemctl halt
```
- 通常來說,`halt` 的行為會和 `poweroff` 一樣
- 但實際的行為取決於系統的設定
- 進入睡眠模式
```
systemctl suspend
```
- 目前系統的狀態會記錄在 RAM 中,並進入較低的功耗 (不會斷電)
- 按下電源鍵,或是觸動鍵盤、滑鼠喚醒系統
- 喚醒速度快
- 但睡眠期間如果斷電或記憶體發生錯誤,無法恢復紀錄的系統狀態 (必須重新開機)
- 進入休眠模式
```
systemctl hibernate
```
- 目前系統的狀態會記錄在硬碟中,並且系統會斷電
- 按下電源鍵喚醒系統
- 喚醒速度慢
- 進入混和休眠模式
```
systemctl hybrid-sleep
```
- 系統狀態會同時紀錄在硬碟和記憶體中,並進入低功耗模式
- 按下電源鍵,或是觸動鍵盤、滑鼠喚醒系統
- 一般情況喚醒速度快
- 如果發生過斷電或記憶體錯誤,仍可以從硬碟恢復系統狀態
- 進入救援模式 (單使用者模式)
```
systemctl rescue
```
## systemctl 管理 Unit
### 列出所有 Unit
- 列出已啟動的 Unit
```bash
systemctl list-units
# 或是
systemctl
```
- 列出所有 Unit
```
systemctl list-units --all
```
- 列出特定狀態的 Unit
```
systemctl list-units --all --state=<unit state>
```
- 可用的 `state`: active, inactive, activating, deactivating, failed
- 列出某特定類型的所有 unit
```
systemctl list-units --type=<unit type>
```
- `<unit type>` 就是前面提到的 service、socket、target ...
輸出是以下格式
```
UNIT LOAD ACTIVE SUB DESCRIPTION
proc-sys-fs-binfmt_misc.automount loaded active running Arbitrary Executable File Formats File System Automount Point
sys-devices-platform-serial8250-tty-ttyS0.device loaded active plugged /sys/devices/platform/serial8250/tty/ttyS0
sys-devices-platform-serial8250-tty-ttyS1.device loaded active plugged /sys/devices/platform/serial8250/tty/ttyS1
docker.service loaded active running Docker Application Container Engine
finalrd.service loaded active exited Create final runtime dir for shutdown pivot root
⋮
```
- **UNIT**
- Unit 的名稱
- **LOAD**
- 設定檔是否已載入
- **ACTIVE**
- 啟動狀態
- **SUB**
- 詳細的執行狀態,各個服務有自訂的數值
- **DESCRIPTION**
- 服務的描述
### 管理服務
管理服務的指令通常是以下格式
```
systemctl <operation> <service name>.service
```
- 其中 `.service` 可以省略
#### 管理服務運行
- 啟動服務
```
systemctl start <service>
```
- 停止服務
```
systemctl stop <service>
```
- 重新啟動服務
```
systemctl restart <service>
```
- 重新載入服務
```
systemctl reload <service>
```
#### 管理開機設定
- 設定服務在開機時自動啟動
```
systemctl enable <service>
```
- 取消服務在開機時啟動
```
systemctl disable <service>
```
#### 查看服務狀態
- 查看服務的狀態
```
systemctl status <service>
```
- 查看服務是否運作中、已啟動或是啟動失敗
```
systemctl is-active <service>
systemctl is-enabled <service>
systemctl is-failed <service>
```
## Systemd 內建的背景服務
- 屬於 Systemd 的一部分,並且以 **systemd unit** 的形式存在
- 可以用 `systemctl` 指令執行基本的管理
- 查看執行狀態、關閉、啟動、重啟 ...
- 這些服務也都有自己對應的指令,讓使用者可以存取這些服務
### systemd-networkd
- 用於**控制網路介面和網路設定**的背景服務
- DHCP、靜態 IP 設定
- 虛擬網路設定
- 網卡設定
- 查看運行狀態
```bash
systemctl status systemd-networkd
```
- `networkctl` 是 systemd-networkd 的指令,用來執行網路相關的操作
```bash
# 查看 networkctl 的完整使用方式
networkctl --help
# 啟用網卡 (等同 ifconfig <interface> up)
networkctl up <interface>
# 關閉網卡 (等同 ifconfig <interface> down)
networkctl down <interface>
# 向 DHCP server 取得新的 IP
networkctl renew <interface>
# 查看目前網路狀態
networkctl status
```
- systemd-networkd 的設定檔放在目錄 */etc/systemd/network* 底下
- 此目錄底下**可以有多個設定檔**,systemd-networkd 啟動時會**載入所有檔案**
- 載入時會依照**檔名的字母順序排序**
- 後載入的設定會**覆蓋之前的設定**
- 設定檔的名稱可以自訂,但副檔名有三種
- `.link`: 網卡設定
- `.netdev`: 虛擬網卡設定
- `.network`: 網路連線設定
- [設定檔語法文件](https://systemd.network/systemd.network.html)
- DPCH 設定範例 (.network)
```
[Match]
Name=eth0
[Network]
DHCP=yes
```
- `eth0` 是介面卡的名稱,設定時記得替換成要使用的介面
- 靜態 IP 設定範例 (.network)
```
[Match]
Name=eth0
[Network]
Address=192.168.10.50/24
Gateway=192.168.10.1
DNS=8.8.8.8
```
- 重啟服務,載入新的設定
```bash
systemctl restart systemd-networkd
# 或是
networkctl reload
```
:::success
**netplan 和 systemd-networkd**
- netplan 是**較高階的網路管理工具**
- 用來產生對應目前系統的網路設定檔
- 使用 YAML 格式的設定檔案
- 支援 systemd-networkd、network manager 和 ifupdown
- 當使用 netplan 設定網路時
- netplan 會自動把 YAML 格式的設定檔,轉換成對應底層的設定檔
- 會自動判斷使用的是 network manager、networkd 或是 ifupdown
:::
:::info
**Network Manager 和 systemd-networkd**
- 兩者都是常見的網路管理工具,直接控制系統的網路介面
- 都可以 Systemd unit 的形式存在
- Network Manager
- 支援圖形介面,常用在 desktop 環境
- 整合較多高階的功能
- WIFI、VPN ...
- systemd-networkd
- Systemd 環境中的網路管理工具,是 Systemd 的一部分
- 較輕量,常用在伺服器或是嵌入式環境
:::
### systemd-resolved
- 用來管理域名解析設定的服務
- 取代傳統 Linux 上的 NSS (name service switch)
- DNS 相關的設定檔
- */etc/hosts*
- 設定 hostname 和 IP 的對應關係
- 優先級較高
- 格式為 `<IP Address> <hostname>`
```
127.0.0.1 localhost
127.0.1.1 ubuntu
```
- */etc/resolv.conf*
- DNS server 的設定檔
- 在 */etc/hosts* 中找不到對應的 IP 時,會透過這些 DNS Server 查詢
- */etc/systemd/resolved.conf*
- systemd-resolved 的設定檔
- systemd-resolved 的操作指令是 `resolvectl`
```bash
# 查看完整使用方式
resolvectl --help
# 查看目前 DNS 設定
resolvectl status
# 查詢對應的 IP
resolvectl query <domain name>
# 反向查詢對應的 domain name
resolvectl query <IP Address>
# 清除快取
resolvectl flush-caches
```
### systemd-timesyncd
- 用來進行網路時間同步的服務
- timesyncd 的指令是 `timedatectl`
```bash
# 顯示完整使用方式
timedatectl --help
# 顯示目前時間資訊
timedatectl
timedatectl status
# 顯示完整資訊
timedatectl timesync-status
# 設定系統時間
timedatectl set-time <TIME>
# 設定系統時區
timedatectl set-timezone <ZONE>
```
### systemd-hostnamed
- 用來管理和存取 hostname、以及其他機器相關的 meta data 的服務
- 只在需要的時候被啟動
- hostnamed 的指令是 `hostnamectl`
```bash
# 查看完整使用方式
hostnamectl --help
# 顯示系統的相關資訊
hostnamectl
hostnamectl status
# 顯示 host name
hostnamectl hostname
# 設定 hostname
hostnamectl hostname <NAME>
hostnamectl set-hostname <NAME>
```
:::warning
***/etc/hosts* 不會被自動修改**
- */etc/hosts* 中應該會有 hostname 對應 loopback IP 的設定
- 使用 `hostnamectl` 設定 hostname 時,*/etc/hosts* 的設定不會跟著修改,需要手動修改
:::
### systemd-journald
- Systemd 環境中的日誌服務,會記錄系統上的所有 log
- 包含來自早期開機階段、kernel、系統背景程式的標準I/O、以及 syslog 的所有紀錄
- journald 的日誌檔案以壓縮過的 binary 格式儲存,只能用它的指令查看
- 預設設定下,紀錄只會保留一個月
- journald 的指令是 `journalctl`
```bash
# 查看所有紀錄
journalctl
```
- `-f`: 只顯示最近的紀錄
- `-r`: 倒序顯示紀錄 (新的優先)
- `-k`: 只顯示 kernel 的紀錄
- 透過 PID 可以查詢特定服務的 log
- 先用 `systemctl status <UNIT>` 查詢 unit 的 PID
- 用 `journalctl _PID=<PID>` 查詢