Docker
建置 Docker 鏡像是部署各種應用程式的常見方式。然而,從 Monorepo 執行此操作會帶來一些挑戰。
须知:
本指南假設您正在使用 create-turbo 或具有相似結構的儲存庫。問題
在 Monorepo 中,不相關的變更可能會在部署應用程式時,導致 Docker 執行不必要的工作。
假設您有一個如下所示的 Monorepo
您想要使用 Docker 部署 apps/api
,因此您建立一個 Dockerfile
這會將根目錄的 package.json
和根鎖定檔複製到 Docker 映像檔。然後,它會安裝依賴項,複製應用程式原始碼並啟動應用程式。
您也應該建立一個 .dockerignore
檔案,以防止 node_modules 與應用程式原始碼一起複製進來。
鎖定檔變更頻繁
Docker 在部署您的應用程式方面非常聰明。就像 Turborepo 一樣,它會盡可能減少工作量。
在我們的 Dockerfile 案例中,只有當其映像檔中的檔案與上次執行不同時,它才會執行 npm install
。否則,它會還原之前擁有的 node_modules
目錄。
這表示每當 package.json
、apps/api/package.json
或 package-lock.json
變更時,Docker 映像檔都會執行 npm install
。
這聽起來很棒 - 直到我們意識到一件事。`package-lock.json` 對於 monorepo 而言是全域的。這表示如果我們在 apps/web
內部安裝一個新套件,我們將導致 apps/api
重新部署。
在大型 Monorepo 中,這可能會導致大量時間損失,因為對 Monorepo 鎖定檔的任何變更都會連鎖反應到數十甚至數百次的部署。
解決方案
解決方案是修剪 Dockerfile 的輸入,使其僅包含絕對必要的內容。Turborepo 提供了一個簡單的解決方案 - turbo prune
。
執行此命令會在 `./out` 目錄中建立 Monorepo 的修剪版本。它僅包含 api
依賴的工作區。它還會修剪鎖定檔,以便僅下載相關的 node_modules
。
--docker
旗標
預設情況下,`turbo prune` 會將所有相關檔案放入 `./out` 中。但為了最佳化 Docker 的快取,我們理想情況下希望分兩個階段複製檔案。
首先,我們只想複製安裝套件所需的內容。執行 `--docker` 時,您會在 `./out/json` 內部找到它。
之後,您可以複製 `./out/full` 中的檔案以新增原始碼檔案。
以這種方式分隔依賴項和原始碼檔案,讓我們僅在依賴項變更時才執行 npm install
- 從而大幅提升速度。
如果沒有 `--docker`,所有修剪後的檔案都會放置在 `./out` 內部。
範例
我們詳細的 with-docker
範例深入探討了如何充分利用 prune
。這是 Dockerfile,為了方便起見複製過來。
從 Monorepo 的根目錄建置 Dockerfile
此 Dockerfile 是為使用 standalone
輸出模式的 Next.js 應用程式編寫的。
遠端快取
為了在 Docker 建置期間利用遠端快取,您需要確保您的建置容器具有存取您的遠端快取的憑證。
在 Docker 映像檔中處理密碼有很多種方法。我們將在此使用一個簡單的策略,使用多階段建置,並將密碼作為建置引數,這些引數將在最終映像檔中隱藏。
假設您正在使用類似於上面範例的 Dockerfile,我們將在 turbo build
之前立即從建置引數中引入一些環境變數
turbo
現在將能夠命中您的遠端快取。若要查看非快取 Docker 建置映像檔的 Turborepo 快取命中,請從您的專案根目錄執行類似於以下的命令