今天這篇文章,我想要介紹一個如果是使用需要編譯的專案的話應該會蠻常使用到的一個功能multi stage build
而我會使用到這個功能,主要是下列兩種情境
而你就可以使用到這個技巧
一樣附上前一篇的腳本
FROM golang:1.23-bookworm AS builder <--
WORKDIR /app
COPY go.mod ./go.mod
COPY go.sum ./go.sum
RUN go mod download
COPY . .
RUN GOOS=linux go build -o /app/backend
FROM ubuntu:22.04 <--
WORKDIR /app
COPY --from=builder /app/backend /backend <--
EXPOSE 5000
CMD ["/backend"]
在上方的Dockerfile中,可以看到有兩個FROM,我在第一個FROM這邊編譯我的專案,最後產出一個執行檔,並放在/app/backend
,接著看到第二個FROM這邊的COPY,把第一階段compile好的執行檔複製到第二個階段裡面執行
而最終使用者在使用的環境,就會是ubuntu:22.04
為基底下運行的image,裡面有一個執行檔,放在/backend
,如此一來,你原本的go的專案檔就不會出現在這個image裡面,可以讓這個image檔更加的單純
除了在Go/C#等會編譯出執行檔的語言之外,在前端也很常會用上,我們可以在第一個stage使用node
的環境將專案打包,而第二個階段則是使用nginx
將靜態的html/css/js放進去,這樣就可以完成一個很小的image裡面也有你的網站
# --- STAGE 1: The Builder ---
# 使用一個包含 Node.js 的官方映像作為建構環境
# 選擇 alpine 版本可以讓這個建構階段的基礎映像更小
# 為這個階段命名為 "builder",方便後續引用
FROM node:20-alpine AS builder
# 在容器內建立一個工作目錄
WORKDIR /app
# 複製 package.json 和 package-lock.json (或 yarn.lock)
# 這是為了最大化利用 Docker 的快取機制。只要這兩個檔案不變,
# 下面的 npm ci 指令就不會重新執行,節省大量時間。
COPY package*.json ./
# 使用 npm ci 而不是 npm install。
# ci (clean install) 會嚴格按照 package-lock.json 安裝依賴,
# 確保建構環境的確定性,這在 CI/CD 和 Docker 建構中是最佳實踐。
RUN npm ci
# 複製應用程式的所有原始碼到工作目錄
COPY . .
# 執行建構指令,這會生成最終的靜態檔案 (通常在 build 或 dist 目錄下)
RUN npm run build
# --- STAGE 2: The Production Server ---
# 使用一個輕量的 Nginx 官方映像作為最終的運行環境
FROM nginx:stable-alpine
# 從 "builder" 階段複製建構好的靜態檔案
# 這裡假設你的框架將檔案建構到 /app/build 目錄下
# 如果是 Vue 或其他框架,可能是 /app/dist,請依實際情況修改
COPY --from=builder /app/build /usr/share/nginx/html
# (可選但建議) 複製自訂的 Nginx 設定檔
# 這對於處理單頁應用 (SPA) 的路由至關重要
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 向外部暴露容器的 80 port
EXPOSE 80
# 容器啟動時執行的指令:以 foreground 模式運行 Nginx
CMD ["nginx", "-g", "daemon off;"]