Ubuntu 22.04 Systemd 服務管理進階

Ubuntu 22.04 Advanced Systemd Service Management

Systemd 概述

Systemd 是現代 Linux 發行版中最常見的初始化系統(init system),負責管理系統啟動流程、服務管理、日誌記錄等核心功能。在 Ubuntu 22.04 中,systemd 已成為預設的服務管理工具,取代了傳統的 SysVinit。

Systemd 的主要特點包括:

  • 平行化啟動:加快系統開機速度
  • 按需啟動:服務可在需要時才啟動
  • 統一管理介面:透過 systemctl 統一管理所有服務
  • 依賴關係追蹤:自動處理服務間的依賴
  • 日誌整合:透過 journald 統一管理系統日誌

Unit 檔案結構與類型

Systemd 使用 Unit 檔案來定義各種系統資源。常見的 Unit 類型包括:

類型副檔名說明
Service.service定義系統服務
Timer.timer定時任務(類似 cron)
Socket.socketSocket 啟動服務
Target.target服務群組
Mount.mount掛載點
Path.path檔案系統路徑監控

Unit 檔案的存放位置:

  • /etc/systemd/system/:系統管理員自訂的 Unit 檔案(優先權最高)
  • /run/systemd/system/:執行時期產生的 Unit 檔案
  • /lib/systemd/system/:套件安裝的 Unit 檔案

建立自訂服務單元

以下是一個完整的自訂服務範例,用於執行一個 Python Web 應用程式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# /etc/systemd/system/myapp.service
[Unit]
Description=My Python Web Application
Documentation=https://example.com/docs
After=network.target postgresql.service
Wants=postgresql.service

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
Environment="PATH=/opt/myapp/venv/bin"
Environment="PYTHONUNBUFFERED=1"
ExecStartPre=/opt/myapp/venv/bin/python manage.py migrate
ExecStart=/opt/myapp/venv/bin/gunicorn -w 4 -b 0.0.0.0:8000 myapp.wsgi
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -TERM $MAINPID
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

建立服務後,執行以下指令啟用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 重新載入 systemd 設定
sudo systemctl daemon-reload

# 啟動服務
sudo systemctl start myapp

# 設定開機自動啟動
sudo systemctl enable myapp

# 檢查服務狀態
sudo systemctl status myapp

Service 區段詳細設定

Service 區段是服務 Unit 的核心,以下是常用的設定選項:

Type 類型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[Service]
# simple:預設值,ExecStart 啟動的程序為主程序
Type=simple

# forking:程序會 fork 子程序後退出
Type=forking
PIDFile=/run/myapp.pid

# oneshot:一次性執行,執行完畢後退出
Type=oneshot
RemainAfterExit=yes

# notify:服務準備好後會發送通知
Type=notify
NotifyAccess=main

執行身份與權限

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[Service]
User=appuser
Group=appgroup
# 禁止取得新權限
NoNewPrivileges=true
# 設定私有 /tmp
PrivateTmp=true
# 唯讀檔案系統
ProtectSystem=strict
# 允許寫入的目錄
ReadWritePaths=/var/lib/myapp /var/log/myapp

服務依賴關係設定

Systemd 提供多種方式來定義服務間的依賴關係:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[Unit]
# 在這些服務啟動後才啟動(順序)
After=network.target mysql.service

# 在這些服務啟動前啟動(順序)
Before=nginx.service

# 需要這些服務同時執行(相依)
Requires=mysql.service

# 希望這些服務同時執行(弱相依)
Wants=redis.service

# 與這些服務衝突,無法同時執行
Conflicts=apache2.service

依賴關係範例圖:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
                    ┌─────────────┐
                    │   network   │
                    └──────┬──────┘
              ┌────────────┼────────────┐
              ▼            ▼            ▼
        ┌─────────┐  ┌─────────┐  ┌─────────┐
        │  mysql  │  │  redis  │  │ mongodb │
        └────┬────┘  └────┬────┘  └────┬────┘
             │            │            │
             └────────────┼────────────┘
                    ┌─────────────┐
                    │   myapp     │
                    └─────────────┘

資源限制(CPU、記憶體)

Systemd 整合了 cgroups,可以輕鬆限制服務的資源使用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
[Service]
# 記憶體限制
MemoryMax=512M
MemoryHigh=400M

# CPU 限制(百分比,100% = 1 核心)
CPUQuota=50%

# CPU 權重(預設 100)
CPUWeight=50

# I/O 權重(預設 100)
IOWeight=50

# 檔案描述符限制
LimitNOFILE=65535

# 程序數量限制
LimitNPROC=4096

# 核心檔案大小限制
LimitCORE=infinity

完整的資源限制範例:

1
2
3
4
5
6
7
# /etc/systemd/system/myapp.service.d/resources.conf
[Service]
MemoryMax=1G
MemoryHigh=800M
CPUQuota=200%
IOWeight=100
LimitNOFILE=65535

自動重啟策略

設定服務在異常退出時自動重啟:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
[Service]
# 重啟條件
# no:不重啟(預設)
# on-success:正常退出時重啟
# on-failure:異常退出時重啟
# on-abnormal:被訊號終止時重啟
# on-abort:被 abort 訊號終止時重啟
# on-watchdog:watchdog 逾時時重啟
# always:總是重啟
Restart=on-failure

# 重啟前等待秒數
RestartSec=5

# 重啟次數限制(在 StartLimitIntervalSec 秒內)
StartLimitBurst=5
StartLimitIntervalSec=60

# 達到重啟次數限制後的動作
StartLimitAction=reboot-force

Timer 定時任務

Systemd Timer 可以取代傳統的 cron,提供更靈活的定時任務管理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily Backup Timer

[Timer]
# 每天凌晨 2 點執行
OnCalendar=*-*-* 02:00:00
# 如果錯過執行時間,開機後立即執行
Persistent=true
# 隨機延遲 0-30 分鐘,避免多服務同時執行
RandomizedDelaySec=30min

[Install]
WantedBy=timers.target

對應的服務檔案:

1
2
3
4
5
6
7
8
9
# /etc/systemd/system/backup.service
[Unit]
Description=Daily Backup Service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
Group=backup

OnCalendar 語法範例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 每小時
OnCalendar=hourly

# 每天午夜
OnCalendar=daily

# 每週一早上 6 點
OnCalendar=Mon *-*-* 06:00:00

# 每月 1 日和 15 日
OnCalendar=*-*-1,15 00:00:00

# 每 15 分鐘
OnCalendar=*:0/15

啟用 Timer:

1
2
sudo systemctl enable --now backup.timer
sudo systemctl list-timers

journalctl 日誌管理

Systemd 的 journald 提供強大的日誌管理功能:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看特定服務的日誌
sudo journalctl -u myapp.service

# 即時追蹤日誌
sudo journalctl -u myapp.service -f

# 查看最近 100 行
sudo journalctl -u myapp.service -n 100

# 查看特定時間範圍
sudo journalctl -u myapp.service --since "2024-02-01" --until "2024-02-02"

# 查看特定優先級(0=emerg 到 7=debug)
sudo journalctl -u myapp.service -p err

# 輸出為 JSON 格式
sudo journalctl -u myapp.service -o json-pretty

# 查看開機日誌
sudo journalctl -b

# 查看核心訊息
sudo journalctl -k

日誌保留設定(/etc/systemd/journald.conf):

1
2
3
4
5
6
7
8
9
[Journal]
# 日誌最大佔用空間
SystemMaxUse=500M
# 單一日誌檔案最大大小
SystemMaxFileSize=50M
# 日誌保留時間
MaxRetentionSec=1month
# 壓縮日誌
Compress=yes

參考資料

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy