CKH 網誌

Ubuntu 24.04 systemd 實作:原生服務、排程與失敗診斷

實測環境與硬體規格

項目實際測試配置
測試日期2026-10-02
作業系統Ubuntu 24.04.5 LTS
主機數量1 台新建隔離 VM;下列資源為每台配置
CPU2 vCPU
記憶體4 GiB
系統磁碟32 GiB;HDD 儲存、VirtIO、ext4
Linux 核心6.8.0-139-generic
systemd255.4-1ubuntu8.17

把腳本交給 systemd 管理,以專用帳號執行、限制可寫路徑,再用 timer 排程。實測成功、結束碼 7 的故障、修復與重新開機後的持續啟用。

本文以全新 Ubuntu 24.04 隔離主機和合成資料實測。套件來自經過簽章驗證、符合 Noble 的套件來源;沒有使用正式資料庫或正式網站的帳號。讀者請依自己的環境確認來源與版本。

圖中是測試主機實際 bash PTY 的執行紀錄,由瀏覽器呈現;提示符統一為 lab$,執行結果未改寫。文字命令另行保留,方便複製。

一、先建立專用執行帳號與目錄

sudo useradd --system --home /var/lib/demo --shell /usr/sbin/nologin demo
sudo install -d -o demo -g demo -m 0750 /opt/demo /var/lib/demo
sudo tee /opt/demo/tick.sh >/dev/null <<'EOF'
#!/bin/sh
set -eu
date -u +%FT%TZ >> /var/lib/demo/runs.log
echo "demo tick completed"
EOF
sudo chown root:root /opt/demo/tick.sh
sudo chmod 0755 /opt/demo/tick.sh

帳號沒有互動登入 shell,腳本由 root 管理,執行結果寫入專用資料目錄。這個例子每次只寫一行時間,沒有網路服務或需要公開的秘密。

二、使用 oneshot 服務與必要的可寫路徑

# /etc/systemd/system/demo.service
[Unit]
Description=Tutorial oneshot task

[Service]
Type=oneshot
User=demo
Group=demo
ExecStart=/opt/demo/tick.sh
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/lib/demo

Type=oneshot 適合執行後結束的任務;這次沒有 RemainAfterExit,成功執行後顯示 inactive 是正常的。判斷成功要看 Result=success 與 ExecMainStatus=0,不能只看是否 active。

三、建立每天的 timer

# /etc/systemd/system/demo.timer
[Unit]
Description=Tutorial timer

[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
Unit=demo.service

[Install]
WantedBy=timers.target
sudo systemd-analyze verify /etc/systemd/system/demo.service /etc/systemd/system/demo.timer
sudo systemctl daemon-reload
sudo systemctl enable --now demo.timer
sudo systemctl start demo.service
systemctl show demo.service -p Result -p ExecMainStatus
systemctl is-enabled demo.timer
sudo journalctl -u demo.service -n 5 --no-pager -o cat
圖1:成功的 oneshot 任務與已啟用 timer
圖1:成功的 oneshot 任務與已啟用 timer。實際 PTY 紀錄,點圖可開啟原尺寸。

日曆時間依主機時區解讀;本次 VM 時區是 Asia/Taipei,腳本紀錄則用 UTC。Persistent=true 可讓符合條件的錯過日曆任務在 timer 再啟動時補做;它不是任務重試或保證每一輪都成功的機制。

四、立即實測觸發,避免只驗證設定存在

sudo systemd-run --unit=demo-trigger-verified --on-active=3s \
  --timer-property=AccuracySec=1ms /usr/bin/systemctl start demo.service
sudo tail /var/lib/demo/runs.log

本次觀察到新的實際寫入時間,證明 timer 真的呼叫服務。加上 AccuracySec=1ms 是短時間測試用,避免預設合併喚醒的容許時間影響判斷;每天的 timer 仍保留一般設定。測試時等到新紀錄出現才判定通過,沒有把設定已啟用當作執行成功。

五、重現失敗並查看真正的結束碼

sudo tee /opt/demo/fail.sh >/dev/null <<'EOF'
#!/bin/sh
exit 7
EOF
sudo chmod 0755 /opt/demo/fail.sh
sudo tee /etc/systemd/system/demo-fail.service >/dev/null <<'EOF'
[Service]
Type=oneshot
User=demo
ExecStart=/opt/demo/fail.sh
EOF
sudo systemctl daemon-reload
sudo systemctl start demo-fail.service
systemctl show demo-fail.service -p Result -p ExecMainStatus
圖2:刻意造成的服務失敗與結束碼 7
圖2:刻意造成的服務失敗與結束碼 7。實際 PTY 紀錄,點圖可開啟原尺寸。

此處啟動服務失敗是預期結果;ExecMainStatus=7 對應腳本結束碼。修復腳本後,再清除失敗狀態並重新啟動:

sudo tee /opt/demo/fail.sh >/dev/null <<'EOF'
#!/bin/sh
echo "repaired task completed"
EOF
sudo chmod 0755 /opt/demo/fail.sh
sudo systemctl reset-failed demo-fail.service
sudo systemctl start demo-fail.service
systemctl show demo-fail.service -p Result -p ExecMainStatus

六、重新開機後檢查兩件事

systemctl is-enabled demo.timer
systemctl is-active demo.timer
systemctl list-timers demo.timer --no-pager
sudo systemctl start demo.service
systemctl show demo.service -p Result -p ExecMainStatus

本次重新開機後 timer 仍 active,任務仍可執行。正式使用時應再加入失敗通知、重試政策與資料備份;日誌如果含憑證或業務資料,不能直接貼到公開網站。

這次驗證了什麼

驗證項目結果
專用帳號的 oneshot 任務成功通過
timer 設定與啟用通過通過
三秒實際觸發有新執行紀錄通過
結束碼 7 的失敗可重現通過
修復後結束碼 0通過
重新開機後 timer 正常通過

實測範圍:真正觸發的是三秒的加速測試 timer,並驗證日曆設定與開機啟用;沒有等到隔天 03:30,也未測長期排程準時性、外部通知或所有 sandbox 選項的攻擊抵抗。

官方參考資料

systemd 255 service、systemd 255 timer