工程筆記

雲端 Mac 遠端連線中斷後持續執行 SSH 工作

遠端 Mac ·約 7 分鐘閱讀

雲端 Mac 遠端連線中斷後持續執行 SSH 工作

透過 SSH 在雲端 Mac 啟動一項耗時較長的建置工作後,如果闔上筆電,稍後重新連線時原本的終端機可能已經消失。真正棘手的並不是重新執行,而是無法判斷工作究竟已失敗、已完成,還是仍在背景執行。可靠的做法不是一味拉長連線逾時,而是把網路連線、終端機工作階段與實際工作拆成三層:SSH 負責進入節點,tmux 保留終端機上下文,日誌與結束碼則用來證明執行結果。

先釐清斷線影響的是哪一層

遠端桌面或 SSH 中斷,只代表用戶端與雲端 Mac 之間的互動通道失去連線,不表示實體節點已停止運作。不過,直接掛在 SSH 虛擬終端機上的前景程序,可能在工作階段關閉時收到掛斷訊號。透過圖形介面開啟的終端機也不適合當作無人值守工作的入口,因為關閉視窗或登出桌面都可能改變其生命週期。

先用一項執行十分鐘的小型工作驗證環境,不要直接拿正式建置來試錯:

mkdir -p "$HOME/jobs/session-check"
cd "$HOME/jobs/session-check"
date -u +"start=%Y-%m-%dT%H:%M:%SZ" > run.log
sleep 600
date -u +"finish=%Y-%m-%dT%H:%M:%SZ" >> run.log

執行後主動中斷連線,再重新連入並檢查 run.log。這項基準測試可以確認節點本身是否持續運作,但不能取代工作階段代管。

SSH 保活機制處理的是「多久後判定連線失效」,tmux 處理的則是「連線失效後,工作是否仍保有可恢復的終端機」。兩者無法互相取代。

設定 SSH 保活與明確的逾時條件

在本機 Mac 的 ~/.ssh/config 中為節點建立獨立設定。位址與使用者名稱應替換為管理主控台提供的實際值:

Host minid-node
    HostName <node-address>
    User <system-user>
    ServerAliveInterval 30
    ServerAliveCountMax 3
    TCPKeepAlive yes
    ConnectTimeout 10

ServerAliveInterval 30 表示用戶端每三十秒傳送一次應用層探測;若連續三次未收到回應,SSH 就會結束已失效的連線,而不是讓終端機無限期卡住。這項設定不會自動重新連線,也無法保證前景程序繼續存活。

連線前可以先檢查最終生效的設定:

ssh -G minid-node | grep -E 'serveralive|tcpkeepalive|connecttimeout'
ssh minid-node

如果網路經常切換,不建議把逾時時間拉長到數十分鐘。越快確認連線已失效,反而越容易重新進入 tmux 查看實際進度。

使用 tmux 代管長時間工作

先確認系統是否已有 tmux;只有在找不到時,才透過目前環境的套件管理方式安裝:

command -v tmux || brew install tmux
tmux new-session -s ios-build

工作階段名稱應清楚表達用途,例如 ios-buildintegration-testasset-export,不要長期重複使用語意模糊的 work。進入工作階段後建立獨立目錄,並讓輸出同時顯示在終端機與寫入日誌:

job_dir="$HOME/jobs/ios-build-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$job_dir"
cd "$job_dir"

set -o pipefail
caffeinate -i /path/to/run-build.sh 2>&1 | tee build.log
status=${PIPESTATUS[0]}
printf '%s
' "$status" > exit-code.txt
date -u +"%Y-%m-%dT%H:%M:%SZ" > finished-at.txt

按下 Control-b,再按 d,即可脫離工作階段而不終止工作。重新連上節點後執行:

tmux list-sessions
tmux attach-session -t ios-build

caffeinate -i 只會在命令執行期間宣告需要避免因閒置而進入睡眠。它應與特定工作綁定,不要為了方便而永久執行。若是純命令列建置,也不需要強制讓顯示器保持喚醒。

避免重複啟動

網路中斷後,先執行 tmux list-sessionspgrep -fl run-build。只有確認舊工作已不存在,才能重新啟動。否則兩個建置程序可能同時寫入相同的衍生資料目錄、快取或產出路徑,最後留下難以重現的混合結果。

以日誌與結束碼作為驗收依據

「重新連線後看到終端機仍持續輸出」並不等於驗收完成。每次長時間工作至少都應留下開始時間、結束時間、完整日誌與結束碼。只有結束碼為 0,才表示指令稿依約成功結束;日誌末尾出現某個單字,不能取代結束碼。

可以依照以下順序快速判斷狀態:

檢查項目 命令 結論
工作階段是否存在 tmux list-sessions 存在不代表工作仍在執行
程序是否存在 pgrep -fl run-build 確認目前的執行個體
日誌是否持續增加 tail -n 30 build.log 判斷進度或停滯位置
是否正常結束 cat exit-code.txt 0 表示指令稿執行成功
結束時間 cat finished-at.txt 判斷結果是否屬於本次執行

指令稿也應為每次執行建立新目錄,不要覆蓋上一輪日誌。需要清理時,應依日期刪除已確認完成歸檔的目錄,而不是對整個 ~/jobs 執行遞迴刪除。

上線前進行一次主動斷線演練

正式使用前,先啟動一項可以安全重複執行的測試工作,再依序驗證 SSH 中斷、遠端桌面關閉與本機網路切換。每次重新連線後,都要檢查 tmux 工作階段、目標程序、日誌是否持續增加,以及結束碼檔案。

如果工作依賴圖形介面,應另外驗證遠端桌面中斷後是否仍會繼續執行;命令列工具與圖形應用程式的生命週期並不相同。工作提前結束時,先查看指令稿的結束碼與日誌,再確認終端機是否遭到關閉,最後檢查儲存空間與程序資源,不要把所有失敗都歸因於網路。

一份可交付的檢查清單應包含:

  • SSH 設定可透過 ssh -G 驗證;
  • 每一類長時間工作都使用獨立的 tmux 工作階段;
  • 工作目錄依時間建立,不覆蓋舊結果;
  • 標準輸出與標準錯誤寫入同一份可搜尋的日誌;
  • 結束碼與 UTC 結束時間分開儲存;
  • 斷線後先恢復原有執行現場,不重複啟動工作;
  • 完成後主動關閉不再使用的工作階段,並將產出歸檔。

常見問題

SSH 中斷後,雲端 Mac 上的工作一定會停止嗎?

不一定。直接依附 SSH 終端機的程序可能收到中斷訊號,而在 tmux 工作階段中啟動的工作通常會繼續執行;重新連線後應檢查工作階段與日誌。

只設定 ServerAliveInterval 就能保護長時間工作嗎?

不能。它只負責偵測失效連線,不會保存終端機中的程序。應使用 tmux 執行工作,並把標準輸出、錯誤輸出和結束碼寫入檔案。

MiniD 雲端 Mac

按日、週或月租用獨享實體 Mac mini

整機獨享的實體 Mac mini,可透過遠端桌面與 SSH 連線;實際機型、區域與租期以訂購頁目前資訊為準。

查看訂購選項