我有一部主機放在防火牆裡面,外部無法直接連上。只能透過某部電腦(abc.abc.abc) 以 ssh 反向連接方式跳進去。
因此,我得先登入放在防火牆裡面的那部主機,執行以下命令:
/usr/bin/ssh -NfR 3270:localhost:22 username01@abc.abc.abc
現在我想把以上命令改寫成 systemctl 的 service ,且可以開機時自動啟動,斷線時,也能自動偵測並自動啟動。
透過與 Google Gemini 問答後,結果如下:
Description=SSH 反向通道 (3270 -> localhost:22)
After=network-online.target
[Service]
#指定遠端主機使用者帳號
Environment=”Remote_host_account=(ex:username01)”
#指定連線遠端主機
Environment=”Remote_host=(ex:abc.abc.abc)”
#定義遠端主機對外打開的埠
Environment=”Remote_host_port=(ex:3270)”
# 使用 simple 類型。ExecStart 啟動的程序是主程序。
Type=simple
# 確保 SSH 在背景執行,並使用標準的 -N -R 參數
# -N: 不執行遠端指令
# -R: 反向通道 (Remote Port Forwarding)
# -o ExitOnForwardFailure=yes: 若通道建立失敗則 SSH 退出,以便 Systemd 重新啟動
# -o ServerAliveInterval=60: 每隔 60 秒發送一個心跳包給遠端伺服器
# -o ServerAliveCountMax=3: 若連續 3 次心跳包無回應,則斷線並退出
ExecStart=/usr/bin/ssh -N -R ${Remote_host_port}:localhost:22 ${Remote_host_account}@${Remote_host} \
-o ExitOnForwardFailure=yes \
-o ServerAliveInterval=60 \
-o ServerAliveCountMax=3
# 關鍵:設定在 SSH 斷線退出(非正常退出)時自動重啟服務
# on-failure:僅在非零退出碼時重啟,這符合 SSH 因斷線而退出的情況
Restart=on-failure
# 設定重啟延遲,避免短時間內重複失敗
RestartSec=5s
# 建議設定使用者,以避免使用 root 權限執行 SSH
#這裡不接受變數,只能輸入該主機裡真實的帳號名稱
User=(ex:username01)
[Install]
# 服務會在多用戶環境(即正常開機完成)時啟用
WantedBy=multi-user.target
##將以上內容存成 my-tunnel.service 後,放到 /etc/systemd/system/ 目錄下
sudo cp my-tunnel.service /etc/systemd/system/
##讓該檔案屬性成為可執行
sudo chmod 755 /etc/systemd/system/my-tunnel.service
##將該檔案設定成開機自動啟動
sudo systemctl enable my-tunnel.service
##現在立刻啟動。
sudo systemctl start my-tunnel.service
Systemd 的補充說明
Type 參數用來指示 Systemd 如何管理和判斷服務的生命週期。以下針對幾個主要的 Type 舉例說明其適用的情境:
- Type=simple (簡單/基本類型)
- Type=forking (背景化類型)
- Type=oneshot (單次執行類型)
- Type=notify (通知類型)
情境:
啟動一個常駐性 (Always Running) 的應用程式,該程式啟動後會持續在前台運行,且不會自行轉為背景程序(Daemonize)。
範例:
啟動一個由 Python 撰寫的 Echo 網路伺服器 或一個簡單的 API 服務。
Systemd 行為:
一旦 ExecStart 執行的程序開始運行,Systemd 就認為服務啟動完成。如果該程序結束,Systemd 會根據 Restart= 設定來決定是否重啟。
情境:
啟動一個傳統的 Linux 背景服務(Daemon),該服務在啟動後會執行自身背景化的操作(父程序退出,子程序在背景運行)。
範例:
傳統的 httpd 網頁伺服器(舊版 Apache)或某些舊式的資料庫服務。
Systemd 行為:
Systemd 會等待父程序退出後,認為服務已啟動。此類型通常需要使用 PIDFile= 參數來告訴 Systemd 哪個 PID 是真正的背景主程序,以便正確追蹤。
情境:
執行一個一次性的設定、維護、或清理任務,該任務完成後即退出,且不需要常駐。
範例:
在系統啟動時掛載特殊的網路檔案系統或執行防火牆初始化腳本。執行一個排程任務的清理腳本。
Systemd 行為:
Systemd 會等待 ExecStart 指令執行完畢並成功退出後,才認為服務執行成功,然後服務狀態轉為停止。
情境:
啟動一個現代應用程式,該程式具有內建 Systemd 整合,可以在內部確認所有資源(如資料庫連線、網路埠)準備就緒後,再明確通知 Systemd。
範例:
現代版本的 Web 伺服器或需要較長初始化時間的應用程式。
Systemd 行為:
Systemd 會在 ExecStart 啟動後等待,直到程序透過 sd_notify() 函式發送「就緒 (READY=1)」訊號後,才認為服務啟動完成。這提供了最精確的啟動狀態追蹤。
在 [Service] 區塊中,Restart= 參數用於定義在服務程序(Service Process)退出後,Systemd 應採取何種自動重啟策略。
- Restart=on-failure 的意義
- Restart=on-failure:
- 非零狀態碼 (Unclean Exit):
- 正常退出 (Clean Exit):
- Restart= 的主要類型 (值)
表示 Systemd 僅在服務程序以非零狀態碼退出或因為非正常原因(如被未處理的信號終止、逾時、或因為 Watchdog 機制)終止時,才會嘗試重啟該服務。
通常代表應用程式執行過程中發生了錯誤。
如果服務程序是以狀態碼 0 退出,則被視為正常停止,Systemd 不會重啟服務。這符合您所上傳檔案中註釋的意圖:僅在 SSH 因斷線而退出(通常是非零退出碼)時才重啟。
Systemd 提供了多種重啟策略,您可以將 Restart= 設置為以下類型之一:
| 類型(Value) | 意義 (何時重啟) |
|---|---|
| no (或留空) | 不重啟。服務退出後即結束 (這是預設值)。 |
| always(總是重啟) | 無論服務是正常退出 (狀態碼 0) 或非正常退出,Systemd 都會嘗試重啟。 |
| on-success | 僅在正常成功退出時重啟。即服務以狀態碼 0 退出,或被特定的成功信號終止時重啟。 |
| on-failure | 僅在非正常失敗退出時重啟。服務以非零狀態碼退出、被終止信號終止、逾時或 Watchdog 失敗時重啟。 |
| on-abnormal | 僅在非正常終止時重啟。服務被信號(例如 SIGKILL, SIGSEGV)終止、逾時或 Watchdog 失敗時重啟,但不包括狀態碼非零的退出。 |
| on-watchdog | 僅在 Watchdog 逾時時重啟。服務因未能定期向 Systemd 報告其健康狀態而逾時時重啟。 |
| on-abort | 僅在被未捕獲的信號中止時重啟。服務因非正常信號退出時重啟。 |
重要提示:
無論設定為何種重啟策略,Systemd 的重啟嘗試都會受到 StartLimitIntervalSec= 和 StartLimitBurst= 參數設定的重啟頻率限制。
如果服務在短時間內失敗次數過多,Systemd 會停止嘗試重啟,並將服務置於 failed 狀態。
可以使用 RestartSec= 參數指定重啟服務前的等待時間。