iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0
Software Development

用 Golang + Elasticsearch + Kubernetes 打造雲原生搜尋服務系列 第 24

Day 24 - 容器化:用多階段 Dockerfile 打造雲原生的第一步

  • 分享至 

  • xImage
  •  

到這裡,我們的 Go + Elasticsearch 搜尋服務已經能在本機上穩定運作。

但要讓它成為「雲原生服務」,第一步就是——容器化(Containerization)

這代表,我們要讓整個應用能被 Docker 這類容器引擎「包起來」,

能在任何地方(本機、CI/CD、Kubernetes)都以相同方式執行。

而這天的任務,就是寫出一個乾淨又高效的 多階段 Dockerfile

並加上 健康檢查(probe) 讓 Kubernetes 能掌握服務狀態。


Step 1:多階段 Dockerfile

在 Golang 世界裡,容器化的最佳實踐之一就是「多階段構建(multi-stage build)」:

讓第一階段專門做編譯(帶 SDK、compiler),

第二階段只留下最小可執行檔(乾淨、輕量、安全)。

以下是一個範例:

# -------- Stage 1: Build --------
FROM golang:1.23 AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o search-service ./cmd/server

# -------- Stage 2: Run --------
FROM gcr.io/distroless/base-debian12

WORKDIR /app
COPY --from=builder /app/search-service .
EXPOSE 8080

USER nonroot:nonroot
ENTRYPOINT ["./search-service"]

🔍 分析:

  • 第一階段(builder):使用官方 golang image 進行編譯。
    • 設定 CGO_ENABLED=0 代表不使用 C 介面,產出靜態執行檔。
    • 指定 GOOS=linuxGOARCH=amd64,確保相容於 Kubernetes 節點。
  • 第二階段(run):使用無 shell、無 package manager 的極小 base image(Distroless)。
    • 避免攻擊面過大,確保映像乾淨。
    • 只帶入編譯好的二進位檔與必要資源。
  • USER nonroot:讓容器以非 root 身份執行,這是安全最佳實踐之一。

這樣打包出來的 image,大小通常不到 30MB,非常適合自動部署。


Step 2:本地測試容器啟動

建立 Dockerfile 後,我們先在本機驗證能否成功啟動。

docker build -t search-service:latest .
docker run -p 8080:8080 search-service:latest

接著用 curl 測試:

curl localhost:8080/healthz

應該會看到:

{"status": "ok"}

這代表服務已經能在容器中正常運行。


Step 3:加入健康檢查(Probe)

在進入 Kubernetes 之前,

你必須讓系統知道「什麼時候服務是健康的、可用的」。

這就需要 LivenessProbeReadinessProbe

在 Deployment manifest ,

可先定義如下範例:

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10

而你的程式要對應這兩個路由,例如:

http.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(`{"status":"alive"}`))
})

http.HandleFunc("/ready", func(w http.ResponseWriter, _ *http.Request) {
	if esClient == nil {
		http.Error(w, "not ready", http.StatusServiceUnavailable)
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(`{"status":"ready"}`))
})

  • /healthz:只檢查服務進程是否存活。
  • /ready:檢查外部依賴(如 Elasticsearch)是否能連線。

這兩者的差別很關鍵。

healthz 讓 Kubernetes 知道「容器掛了沒」,

ready 則讓它知道「可不可以分流流量進來」。


小結

到目前為止,我們完成了以下幾件重要的里程碑:

步驟 成果
多階段構建 產出輕量、安全的映像
本地測試 驗證容器可正常啟動
健康檢查 提供 Kubernetes 判斷健康與可用性的依據

上一篇
Day 23 - 多階段查詢:讓搜尋更懂人話
系列文
用 Golang + Elasticsearch + Kubernetes 打造雲原生搜尋服務24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言