Linux Systemd Service 設定與 Journalctl

在上一篇 systemctl 中,我們學會了如何管理現有的服務。 這篇我們將深入探討:如何撰寫自己的 Service 設定檔,讓你的程式 (Python, Go, Node.js 等) 也能像 Nginx 一樣開機自動啟動、掛掉自動重啟。 以及如何使用 journalctl 查看服務的 Log。

撰寫 Systemd Service 設定檔

Systemd 的設定檔通常放在 /etc/systemd/system/ 目錄下,副檔名為 .service

1. 設定檔結構範例

假設我們有一個 Python 程式 /opt/my-app/main.py。 我們建立一個設定檔:sudo nano /etc/systemd/system/my-app.service

[Unit]
Description=My Custom Python App
After=network.target

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/my-app
ExecStart=/usr/bin/python3 /opt/my-app/main.py
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

2. 欄位詳解

[Unit]區塊:基本資訊

  • Description: 服務的描述(顯示在 status 裡)。
  • After: 定義啟動順序。例如 network.target 代表「等網路功能啟動後,才啟動我」。(注意:這不是依賴關係,只是順序)。

[Service]區塊:執行細節 (最重要)

  • Type: 通常設為 simple (預設值)。如果你的程式會 fork 子行程 (如 nginx),則可能要設為 forking
  • User / Group: 指定以哪個使用者身份執行。強烈建議不要用 root,除非必要。
  • WorkingDirectory: 程式的工作目錄 (也就是程式裡 . 代表的地方)。
  • ExecStart: 最重要的欄位。啟動指令。
    • 注意: 必須使用 絕對路徑!例如 /usr/bin/python3 而不是 python3
  • Restart: 定義什麼時候要自動重啟。
    • no (預設): 永不重啟。
    • on-failure: 只有在程式「非正常退出」 (Exit code != 0) 時才重啟。
    • always: 無論如何都重啟 (包含你手動 kill 它)。適合長期運行的 Daemon。
  • RestartSec: 重啟前要等幾秒。

[Install]區塊:安裝設定

  • WantedBy: 指定這個服務掛載在哪個 Target 下。
    • multi-user.target: 最常用,代表「多使用者文字模式」 (類似傳統 Runlevel 3)。
    • 如果沒寫這行,systemctl enable 會無法運作。

3. 套用新設定

寫好設定檔後,必須通知 Systemd 重新讀取設定:

# 1. 重載 Systemd 設定 (只要修改了 .service 檔都要跑這行)
sudo systemctl daemon-reload

# 2. 啟動服務
sudo systemctl start my-app

sudo systemctl enable my-app

User Service (使用者層級服務)

這是一個非常實用但常被忽略的功能。 上述的服務都是 System Service (放在 /etc/systemd/system/),由 root 管理,開機時啟動。

Systemd 也支援 User Service

  • 位置:放在 ~/.config/systemd/user/ 目錄下 (目錄不存在請自己 mkdir)。
  • 權限:不需要 sudo,以該使用者身份執行。
  • 時機:當使用者「登入」時啟動,使用者「完全登出」時停止 (除非啟用 lingering)。

1. 建立 User Service

假設我想跑一個自己的 Python 腳本,不想麻煩管理員。

建立檔案:mkdir -p ~/.config/systemd/user/ 編輯:nano ~/.config/systemd/user/my-bot.service

注意:不需要 User=Group= 欄位(因為就是你本人)。

[Unit]
Description=My Telegram Bot

[Service]
ExecStart=/usr/bin/python3 /home/miko/bot.py
Restart=always

[Install]
WantedBy=default.target

(注意:這裡用 default.target 而不是 multi-user.target)

2. 管理 User Service

指令跟平常一樣,但在 systemctl 後面加上 --user

# 重載設定
systemctl --user daemon-reload

# 啟動服務
systemctl --user start my-bot

# 查看狀態
systemctl --user status my-bot

# 設定登入自啟
systemctl --user enable my-bot

3. 如何讓 User Service 在登出後繼續跑? (Lingering)

預設情況下,當你登出 Server,你的 User Service 就會被殺掉。 如果你希望它像 System Service 一樣一直在背景跑(即使你沒登入),你需要啟用 Lingering

# 請用 sudo 幫該使用者開啟 lingering
sudo loginctl enable-linger <username>

更多 Unit 設定技巧

設定檔還有很多好用的參數:

  • EnvironmentFile: 從檔案讀取環境變數 (例如 .env 檔)。
    [Service]
    EnvironmentFile=/opt/my-app/.env
    
  • Before / After: 控制啟動順序。
    [Unit]
    # 在 nginx 啟動之前,先啟動我
    Before=nginx.service
    # 在 mysql 啟動之後,才啟動我
    After=mysql.service
    
  • Requires / Wants: 定義依賴關係。
    • Requires=mysql.service: 如果 MySQL 掛了或沒開,我也不能開 (強依賴)。
    • Wants=mysql.service: 我希望 MySQL 開,但他沒開我也無所謂 (弱依賴)。

進階 Log 管理 (journalctl)

Systemd 統一管理了所有服務的 Log,我們不需要去 /var/log 翻檔案,直接用 journalctl 指令即可。 對於 User Service,記得加上 --user

常用過濾方式

# 查看特定服務的 Log
journalctl -u nginx

# 持續追蹤 Log (Follow mode)
journalctl -u nginx -f

# 查看某個時間點之後的 Log
journalctl -u nginx --since "2023-12-01 12:00:00"
journalctl -u nginx --since "1 hour ago"

# 只顯示錯誤 (Error) 等級
journalctl -p err

# 以 JSON 格式輸出 (方便程式解析)
journalctl -u nginx -o json-pretty

清理 Log (Vacuum)

Systemd 的 Log 預設會一直存,可能會吃光硬碟。

# 檢查 Log 佔用了多少空間
journalctl --disk-usage

# 清理 Log,只保留最近 1GB
sudo journalctl --vacuum-size=1G

# 清理 Log,只保留最近 1 週
sudo journalctl --vacuum-time=1weeks