Docker Compose 執行流程

當執行 docker-compose up 命令時,Docker Compose 會依序執行以下操作:

  1. 檢查是否需要建置映像檔:Docker Compose 會查看 docker-compose.yml 中每個服務的 build 設定。

  2. 建置映像檔:如果服務配置了 build 選項(指向一個包含 Dockerfile 的目錄),Docker 會使用該目錄中的 Dockerfile 來建置映像檔。建置過程會執行 Dockerfile 中的所有指令。

  3. 建立和啟動容器:一旦映像檔建置完成,Docker Compose 會使用這些映像檔建立容器,並根據 docker-compose.yml 中的設定啟動它們。

所以,你不需要自己手動執行 Dockerfile 中的指令。當你執行 docker-compose up 時,整個過程會自動進行:

docker-compose up

這個命令會:

  • 建置所有需要的映像檔(根據 Dockerfile)

  • 建立並啟動所有服務的容器

  • 顯示所有容器的日誌輸出

如果你只想建置映像檔但不啟動容器,可以使用:

docker-compose build

如果你修改了 Dockerfile 並需要重新建置映像檔,可以使用:

docker-compose up --build

問題排解記錄

問題一:Angular 建構預算超出限制

在執行 docker-compose up 時,前端的建構過程發生了錯誤。主要問題是 Angular 應用打包時超出了預設的大小限制。這是一個常見的問題,與安全性無關,只是 Angular 的建構預算限制。

錯誤訊息顯示:

✘ [ERROR] bundle initial exceeded maximum budget. Budget 1.00 MB was not met by 21.09 kB with a total of 1.02 MB.

這表示應用打包後大小是 1.02MB,而設定的預算上限是 1.00MB。

解決方案

修改前端的 Angular 配置:

  1. 在前端專案中找到 angular.json 檔案

  2. 找到 budgets 部分,應該看起來像這樣:

    "budgets": [  { "type": "initial", "maximumWarning": "500kb", "maximumError": "1mb" },  { "type": "anyComponentStyle", "maximumWarning": "2kb", "maximumError": "4kb" }]
  3. maximumError 的值從 1mb 增加到 2mb,以允許更大的套件大小

修改後再次執行 docker-compose up 就能解決這個問題。

問題二:容器命名衝突

遇到了容器命名衝突的問題。錯誤訊息表示名為 "/voting_db" 的容器已經存在,所以 Docker 無法建立一個同名的新容器。

這通常發生在你之前已經執行過 Docker Compose,但容器沒有被正確移除的情況下。

解決方案

有兩種方式可以解決這個問題:

  1. 移除現有的容器

    docker rm voting_db

    這會移除已存在的容器,讓你可以重新建立一個同名的容器。

  2. 使用 down 命令(建議使用這種方式):

    docker-compose down

    這個命令會停止並移除所有由 docker-compose.yml 定義的容器、網路和磁碟區,讓你可以重新開始。

然後,再次執行:

docker-compose up

人為中斷或不正常關閉 Docker 容器時,常會發生這種情況。使用 docker-compose down 是一個好習慣,它能確保所有資源被正確清理。

問題三:後端缺少 nodemon 套件

前端和資料庫容器成功啟動,但後端容器一直報錯:

sh: 1: nodemon: not found

這表示後端容器中沒有安裝 nodemon 套件,而 docker-compose.yml 中的命令卻嘗試使用它。

解決方案一:在啟動時安裝 nodemon

參考:Stack Overflow - Docker /bin/bash: nodemon: command not found

修改 docker-compose.yml 中後端服務的 command 設定,確保在執行程式之前先安裝所需的套件:

command: sh -c "npm install -g nodemon && npm install && npm run dev"

這個命令會:

  1. 全域安裝 nodemon 套件

  2. 安裝專案的相依套件

  3. 執行 package.json 中定義的 dev 腳本

解決方案二:移除卷掛載

另一個解決方案是移除 volumes 設定:

# 移除這一行
# volumes:
#   - ./voting-system-be:/app

當使用 volumes 將本地目錄掛載到容器的 /app 目錄時,它會覆蓋容器內的檔案,包括在 Dockerfile 建構過程中安裝的相依套件。移除 volumes 設定後,容器會使用在建構階段安裝好的相依套件,而不是使用掛載進來的可能不完整的本地目錄。

為什麼會發生這個問題?

原本 be 裡就有主管建置好的 Dockerfiledocker-compose.yml,be 可以單獨啟動 server 跟 db,那為什麼在父容器啟動時就會報錯沒有找到 nodemon 呢?

這是因為:

  1. 卷掛載覆蓋了容器內容:當使用 volumes: - ./voting-system-be:/app 時,它會覆蓋容器內的 /app 目錄,包括在 Dockerfile 建構階段安裝的 nodemon。

  2. 環境差異:在 Docker 環境中,容器啟動後的執行環境與建構環境不完全相同。

  3. 路徑問題:全域安裝的套件可能在不同的路徑,而 PATH 環境變數設定可能不同。

解決方案比較

方案一:在啟動時安裝套件

volumes:
  - ./voting-system-be:/app
command: sh -c "npm install -g nodemon && npm install && npm run dev"

優點:

  • 保留卷掛載,可以實時更新程式碼

  • 確保所有必要的套件都已安裝

缺點:

  • 每次啟動容器都需要重新安裝套件,啟動較慢

  • 不是最佳實踐,尤其對於生產環境

方案二:移除卷掛載

# 不使用 volumes
command: npm run dev  # 甚至可以移除此行,使用 Dockerfile 中的 CMD

優點:

  • 更可靠的啟動流程

  • 使用 Dockerfile 中已經安裝好的套件

  • 啟動速度更快

缺點:

  • 修改程式碼後需要重新建置容器,無法即時反映變更

  • 失去開發環境中的「熱更新」優勢

在你的情境中,使用移除卷掛載的方式更合適。實際上,如果 Dockerfile 中已經定義了 CMD 指令,docker-compose.yml 中的 command 行也可以移除,讓容器使用 Dockerfile 中定義的預設命令。

經驗總結

  1. 建立 Docker 容器時常會遇到各種問題,保持耐心並分析錯誤訊息是解決問題的關鍵。

  2. 使用 docker-compose down 而不是直接關閉終端機,可以避免容器命名衝突的問題。

  3. 容器內的環境和本地開發環境可能不同,需要確保所有相依套件都在容器中正確安裝。

  4. 理解卷掛載(volumes)的工作原理很重要:它會覆蓋容器內的檔案,這可能會導致在 Dockerfile 中安裝的套件「消失」。

  5. 在開發環境中需要權衡「即時程式碼更新」與「可靠啟動」之間的取捨。

  6. Angular 的建構預算設定可能需要根據專案的實際大小來調整。

  7. 編寫 docker-compose.yml 時,要考慮各服務啟動的順序和相依關係,特別是後端服務可能依賴資料庫服務。

Last updated