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 failed 或 exit 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>
再依照以下順序判斷:
- 入口命令:檢查容器實際執行的命令是否存在,並確認應用不是啟動後立即結束。
- 監聽地址:應用通常需要監聽容器內的
0.0.0.0,只監聽本機回送地址時,外部路由可能無法連入。 - 連接埠:比較程式實際監聽的連接埠、容器設定與 OpenShip 路由設定,不要只看 Dockerfile 的
EXPOSE。 - 健康檢查:查看健康檢查命令、回應路徑、等待時間和失敗次數。Docker 官方文件說明,健康檢查可以透過
docker inspect讀取狀態與失敗輸出。(docs.docker.com) - 資源狀態:檢查記憶體、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 成功但回應是
404、502或504:問題多半已進入邊緣路由或應用上游,不要再修改 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.