透過 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-build、integration-test 或 asset-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-sessions 與 pgrep -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 連線;實際機型、區域與租期以訂購頁目前資訊為準。