← 返回技術實踐

CI/CD 實踐

OpenShip 部署失敗:2026 構建與上線排障指南

約 11 分鐘閱讀

OpenShip 部署失敗:2026 構建與上線排障指南

OpenShip 官方快速入門把首次部署整理成 3 個主要步驟:安裝、初始化專案、執行部署;但「顯示部署完成」不代表域名、容器和資料庫真的可用。(openship.io)

症狀: OpenShip 顯示部署完成,但域名打不開、容器反覆重啟,或後台無法連線。
最快解法: 先按「本地構建 → 傳輸連線 → 容器啟動 → 域名路由 → 依賴服務」五層取證,不要一遇到 OpenShip 部署失敗就重裝平台。

這篇適合首次使用 OpenShip 部署 AI SaaS、後端服務或帶資料庫應用的獨立開發者;也適合維護自有伺服器、需要縮短故障定位時間的技術人員。若你從本地 Mac 發起部署,但受到資源不足或無法長時間保持連線,文末會說明何時應改用持續在線的遠端構建環境。

先建立故障時間線

排障時不要同時修改 Dockerfile、DNS、環境變數和伺服器防火牆。每改一個變數,就可能失去原本的錯誤證據。你應先記錄部署編號、Git commit、開始時間、失敗階段和最後一行完整日誌。

OpenShip 官方說明的典型路徑是:程式碼先在本地或雲端構建成映像檔,再透過 SSH 傳送至目標,啟動新的容器,最後交由邊緣路由處理域名與 TLS。官方首頁也提到,上一個版本會保留以便回滾。(openship.io)

觀察到的現象 優先檢查層 第一份證據 暫時不要做的事
沒有產出映像檔或構建中止 本地構建 完整構建日誌、退出碼、構建設定 不要只看最後一行錯誤
SSH 連不上或傳輸中斷 傳輸連線 目標地址、連接埠、指紋、詳細 SSH 輸出 不要直接關閉所有防火牆
部署完成但服務不可用 容器啟動 容器狀態、啟動日誌、健康檢查 不要只相信部署介面顯示成功
本機可訪問、公開域名不可訪問 域名路由 公開 DNS、TLS 狀態、HTTP 回應 不要同時改 DNS 和應用連接埠
應用啟動但資料庫錯誤 依賴服務 服務狀態、連線字串、資料卷 不要把刪除資料卷當成通用解法

這張表的用途不是猜答案,而是決定你下一個要收集的證據。完成第一輪取證後,才進入修復。

構建階段:先看完整日誌與退出碼

當 OpenShip 構建失敗,先把完整構建日誌保存下來,至少包括依賴安裝、編譯、測試、映像檔產出和結束狀態。只複製 command failedexit code 1,通常不足以判斷原因。

OpenShip 構建失敗應先看哪段日誌

先找第一個真正改變狀態的錯誤,而不是最後的連鎖失敗。常見判斷方式如下:

  • 依賴安裝階段出現套件不存在、版本衝突或鎖定檔不一致:優先檢查 package.json、鎖定檔、Python 依賴檔或專案指定的套件管理器。
  • 編譯階段出現找不到模組、型別錯誤或語法錯誤:回到本地相同 commit 執行構建,確認是否為程式碼問題。
  • 啟動前才出現環境變數缺失:核對 OpenShip 專案設定與程式實際讀取的變數名稱,注意大小寫和前後空白。
  • 構建程序被作業系統終止、程序突然消失或出現記憶體相關訊息:才考慮本地構建資源不足。

你可以在本地以相同版本、相同構建指令重現:

git checkout <COMMIT>
<YOUR_PACKAGE_MANAGER> install
<YOUR_BUILD_COMMAND>
echo $?

如果本地構建也失敗,問題較可能在程式碼、依賴或設定;如果本地成功,但 OpenShip 構建失敗,則要比較執行時版本、CPU 架構、環境變數和可用資源。不要把兩者混為一談。

OpenShip 官方文件指出,初始化時會偵測常見框架與語言,並產生部署設定;因此你仍應檢查自動產生的設定是否符合專案真正的啟動指令,而不是假設偵測結果一定正確。(openship.io)

處理結論: 保存完整日誌、退出碼和專案設定,先確認失敗發生在依賴、編譯、環境變數還是資源層。
修復後驗證: 同一個 commit 在本地完成構建,OpenShip 也產出可部署映像檔,並記下兩邊使用的執行時版本。

SSH 與鏡像傳輸:把連線問題拆成三類

OpenShip 的自有伺服器部署路徑依賴 SSH;官方說明也指出,控制端會透過 SSH 將構建好的映像檔送往目標。(openship.io)

OpenShip 無法透過 SSH 連線伺服器怎麼辦

先不要重建金鑰。你應把問題分成首次連線失敗、傳輸中斷,以及已連線但沒有執行權限三類。

問題類型 核驗指令 需要留下的證據 常見處理方向
首次連線失敗 ssh -vvv -p <PORT> -i <KEY> <USER>@<HOST> 解析結果、連接埠回應、主機指紋、驗證錯誤 修正地址、連接埠、金鑰或主機指紋
傳輸途中中斷 ssh -vvv ...、檢查伺服器磁碟與連線記錄 中斷時間、最後傳輸階段、伺服器資源 檢查頻寬、磁碟空間、閒置逾時
連線成功但無法部署 ssh ... "id; docker version; command -v docker" 登入帳號、群組、Docker 權限、命令輸出 修正帳號權限或改用具備必要權限的部署帳號

金鑰檔案應只由使用者讀取:

chmod 600 <KEY>
ssh-keygen -lf <KEY>.pub
ssh-keygen -F <HOST>

如果伺服器位於公開網路,最小暴露原則是:只開放必要的 SSH 連接埠,優先使用金鑰驗證,限制來源 IP 或透過 VPN,並讓應用容器維持在內部網路。不要為了測試而長期開放所有來源和所有連接埠。

處理結論: 只有在目標地址、連接埠、金鑰權限、主機指紋及登入帳號都能產生正確證據後,才進一步判斷 OpenShip 或容器問題。
修復後驗證: 先完成一個不涉及正式部署的遠端命令,再執行小型映像檔傳輸,最後才重試完整部署。

容器啟動:不要把「部署完成」當成「服務可用」

容器能建立,不代表應用已經監聽正確連接埠。入口命令錯誤、應用只綁定 127.0.0.1、容器內外連接埠不一致、環境變數缺失或記憶體不足,都可能讓部署介面顯示完成,但實際請求失敗。

OpenShip 應用啟動後反覆重啟怎麼排查

先取得三份資料:

docker ps -a
docker logs --tail 200 <CONTAINER>
docker inspect <CONTAINER>

再依照以下順序判斷:

  1. 入口命令:檢查容器實際執行的命令是否存在,並確認應用不是啟動後立即結束。
  2. 監聽地址:應用通常需要監聽容器內的 0.0.0.0,只監聽本機回送地址時,外部路由可能無法連入。
  3. 連接埠:比較程式實際監聽的連接埠、容器設定與 OpenShip 路由設定,不要只看 Dockerfile 的 EXPOSE
  4. 健康檢查:查看健康檢查命令、回應路徑、等待時間和失敗次數。Docker 官方文件說明,健康檢查可以透過 docker inspect 讀取狀態與失敗輸出。(docs.docker.com)
  5. 資源狀態:檢查記憶體、CPU、磁碟和程序數量;若容器被 OOM 終止,單純重啟只會重複失敗。

Docker 的重啟政策會在容器退出後自動重新啟動,但它不是修復機制;官方文件也提醒,錯誤的重啟策略會讓問題看起來像「服務一直自己恢復」,實際上只是反覆啟動。(docs.docker.com)

驗證項目 通過條件 不通過時的判斷
容器狀態 Up 且沒有持續增加重啟次數 先查入口命令與退出碼
本機請求 curl http://127.0.0.1:<PORT>/health 有預期回應 查監聽地址與應用啟動日誌
健康檢查 狀態為 healthy 或平台定義的通過狀態 查檢查路徑、依賴服務與啟動等待
實際請求 使用測試帳號完成一個真實 API 請求 查環境變數、資料庫與路由
回滾能力 舊版本仍可啟動 不要先刪除舊映像檔或資料卷

修復後先保留舊版本,再以測試請求驗證新版本。OpenShip 官方首頁將不可變部署快照與一鍵回滾列為功能之一;即使如此,你仍應確認實際環境中的舊版本可用,而不是只依賴介面文字。(openship.io)

域名與 HTTPS:按照公開路徑逐段確認

當 OpenShip 顯示部署成功,但域名仍打不開,先不要修改應用程式。按 DNS、證書、邊緣路由、應用回應的順序測試。

OpenShip 部署成功後域名為什麼打不開

從公開網路執行:

dig +short <YOUR_DOMAIN> A
dig +short <YOUR_DOMAIN> AAAA
curl -Ivv https://<YOUR_DOMAIN>
openssl s_client -connect <YOUR_DOMAIN>:443 -servername <YOUR_DOMAIN>

每項結果代表不同問題:

  • DNS 沒有回應或指向舊地址:先修正 A、AAAA 或 CNAME 記錄。
  • DNS 正確但 TLS 失敗:檢查域名是否已被正確路由、證書是否簽發,以及伺服器是否能完成驗證。
  • TLS 成功但回應是 404502504:問題多半已進入邊緣路由或應用上游,不要再修改 DNS。
  • 伺服器本地 curl 成功,外部請求失敗:優先檢查防火牆、公開連接埠、雲端安全群組、IPv6 設定和邊緣路由。

Let’s Encrypt 的 DNS-01 驗證需要在指定域名下建立 TXT 記錄;因此證書問題不能只靠「伺服器本地能開」來判斷。(cp.letsencrypt.org) Google 的網站搬遷文件也建議使用不同公開 DNS 來源確認記錄是否已更新,避免只看單一網路環境的快取結果。(developers.google.com)

處理結論: 一次只改 DNS、證書或路由其中一項,並保存修改前後的公開查詢結果。
修復後驗證: 從伺服器外部完成 HTTPS 請求,再以瀏覽器、API 客戶端和一個不在伺服器內的測試網路重複確認。

資料庫與後台服務:先保護資料再修復

資料庫連線失敗通常有三種來源:應用程式碼使用錯誤的連線方式、憑據不正確,或資料庫服務根本沒有可用。它們的修復動作完全不同。

先核對以下項目:

docker ps -a
docker logs --tail 200 <DB_CONTAINER>
docker volume ls
nc -vz <DB_HOST> <DB_PORT>
證據 可能原因 處理方式
資料庫容器未啟動 入口命令、資源或初始化失敗 查資料庫日誌與容器退出狀態
連接逾時 網路隔離、主機名稱錯誤或防火牆 確認同一內部網路與正確服務名稱
驗證失敗 使用者、密碼、資料庫名稱錯誤 比對目標環境密鑰,不要把密碼寫進程式碼
欄位或資料表不存在 Migration 未執行或環境指錯 先確認資料庫環境,再執行可回復的 Migration
重建後資料消失 資料卷未掛載或指向錯誤 先備份,再核對資料卷與掛載路徑

不要把刪除資料卷當成「重新初始化」的第一選項。先匯出資料、保存連接字串和資料卷資訊,再決定是否重建服務。若應用能連到資料庫但讀寫失敗,問題可能在權限、Migration 或程式碼,不一定是資料庫服務本身。

修復後至少完成一次真實讀寫:建立測試資料、讀取、更新,再刪除或標記清理。只有 nc 能連上連接埠,只能證明網路通,不代表帳號、Schema 和應用查詢都正常。

重新上線:用里程碑完成驗收

修復完成後,不要只重新執行一次 openship deploy。建議把驗收拆成以下時間線,讓其他團隊成員日後可以重複使用。

里程碑 驗收內容 必留記錄
M1 構建完成 指定 commit 構建成功,無未處理錯誤 commit、執行時版本、退出碼
M2 服務啟動 容器保持運行,入口命令正確 容器狀態、啟動日誌
M3 健康檢查 健康檢查通過,重啟次數沒有持續增加 健康狀態、檢查輸出
M4 域名可用 公開 DNS、TLS 和 HTTPS 回應均正常 dig、證書狀態、HTTP 回應
M5 資料可讀寫 應用完成測試資料的讀取與寫入 測試結果、資料庫日誌
M6 回滾完成 舊版本能恢復服務,新版本可再次部署 回滾時間、服務回應、版本標籤

以下情況適合繼續修復:錯誤可以穩定重現、證據指向專案設定或單一服務,而且舊版本仍能正常運行。以下情況則應考慮更換構建節點或部署環境:本地記憶體長期不足、設備無法持續在線、網路頻繁中斷,或團隊沒有可重複使用的遠端交付節點。

如果你要把部署記錄整理成團隊文件,可參考 Kvmkit 幫助中心 的支援入口,並將以下欄位固定化:

事件時間:
專案與環境:
Git commit:
OpenShip 部署編號:
失敗層級:
完整錯誤日誌位置:
退出碼:
修復前狀態:
執行的修復動作:
修復後驗證:
是否完成回滾演練:
下一步與負責人:

從本地 Mac 直接部署的方案,優點是不用額外準備長時間在線的構建節點;但它也有三個實際限制:本機記憶體與硬碟資源會與開發工作競爭、睡眠或網路切換可能中斷傳輸,而且故障日誌未必能由團隊成員持續取得。相較之下,持續在線的遠端 Mac 環境更適合需要保留構建記錄、重試部署和多人接手的團隊,但你仍要先確認它是否符合專案的 Linux 伺服器、容器與資料庫架構。

因此,只有當證據明確指向本地構建資源不足、開發設備不能持續在線,或團隊缺少穩定交付節點時,才值得把構建流程搬到遠端環境,而不是因為一次 OpenShip 部署失敗就更換平台。若你正在評估這條路線,可以先查看 雲端 Mac 構建環境配置,再按專案的構建需求決定是否需要租用 Kvmkit 的 Mac 環境。若只是在做一次性測試、需要物理介面,或長期重負載成本更適合自購設備,租用就未必是最佳選擇。

把 CI/CD 放在 M4 Mac mini 上,才算真正省心

Xcode, Fastlane, CocoaPods, and SPM are first-class on macOS. Mac mini M4 unified memory keeps signing and archiving smooth; ~4W standby power suits 24/7 build nodes.

View Kvmkit plans

需要技術支援或選型建議?

在使用 Mac 實例或 CI/CD 流水線過程中遇到問題,可先查看幫助中心;下單與計價見定價頁。