這是 Go 語言從零到 Web 應用系列的第十八篇,也是本系列的最終篇。我們將把前面建構的 Go 應用部署到 Kubernetes 上,學習容器編排的核心概念。
Kubernetes 基礎概念
核心元件
┌─────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌────────────────────────────────────┐ │
│ │ Namespace: default │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │Deployment│ │ Service │ │ │
│ │ │ │ │(ClusterIP│ │ │
│ │ │ ┌──────┐ │ │ or LB) │ │ │
│ │ │ │ Pod │ │←──│ │ │ │
│ │ │ │┌────┐│ │ └──────────┘ │ │
│ │ │ ││容器││ │ │ │
│ │ │ │└────┘│ │ │ │
│ │ │ └──────┘ │ │ │
│ │ └──────────┘ │ │
│ └────────────────────────────────────┘ │
└─────────────────────────────────────────┘
| 元件 | 說明 |
|---|---|
| Pod | 最小部署單位,一個或多個容器的組合 |
| Deployment | 管理 Pod 的副本數、更新策略 |
| Service | 為 Pod 提供穩定的網路端點 |
| Namespace | 資源的邏輯隔離 |
| ConfigMap | 配置資料(非機密) |
| Secret | 機密資料(密碼、token) |
本地環境設定
使用 minikube
# 安裝
brew install minikube
# 啟動叢集
minikube start
# 確認狀態
kubectl cluster-info
kubectl get nodes
使用 kind(Kubernetes in Docker)
# 安裝
brew install kind
# 建立叢集
kind create cluster --name bookstore
# 確認
kubectl cluster-info --context kind-bookstore
kind 更輕量,適合 CI/CD 和快速測試。
Go 應用的 Dockerfile 最佳化
Multi-stage Build
# ===== Build Stage =====
FROM golang:1.22-alpine AS builder
WORKDIR /app
# 先複製 go.mod 和 go.sum(利用 Docker 快取層)
COPY go.mod go.sum ./
RUN go mod download
# 複製原始碼並編譯
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server
# ===== Production Stage =====
FROM scratch
# 從 builder 階段複製執行檔
COPY --from=builder /app/server /server
# 如果需要 TLS 憑證(呼叫外部 HTTPS API)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]
映像大小對比
| Base Image | 大小 |
|---|---|
golang:1.22 |
~800MB |
golang:1.22-alpine |
~250MB |
alpine:3.19 |
~5MB + 你的 binary |
scratch |
0MB + 你的 binary |
gcr.io/distroless/static |
~2MB + 你的 binary |
建議:
scratch:最小,但沒有 shell,debug 困難distroless/static:比 scratch 多一些基本檔案,推薦用於生產alpine:需要 debug 工具時使用
建構與推送
# 建構映像
docker build -t bookstore-api:v1.0.0 .
# 如果用 minikube,先載入映像
minikube image load bookstore-api:v1.0.0
# 如果用 kind
kind load docker-image bookstore-api:v1.0.0 --name bookstore
Kubernetes 資源定義
Deployment
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: bookstore-api
labels:
app: bookstore-api
spec:
replicas: 3
selector:
matchLabels:
app: bookstore-api
template:
metadata:
labels:
app: bookstore-api
spec:
containers:
- name: api
image: bookstore-api:v1.0.0
ports:
- containerPort: 8080
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: bookstore-secrets
key: database-url
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: bookstore-config
key: log-level
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 500m
memory: 128Mi
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
Service
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: bookstore-api
spec:
type: ClusterIP # 叢集內部存取
selector:
app: bookstore-api
ports:
- port: 80
targetPort: 8080
protocol: TCP
---
# 如果需要對外暴露
apiVersion: v1
kind: Service
metadata:
name: bookstore-api-lb
spec:
type: LoadBalancer
selector:
app: bookstore-api
ports:
- port: 80
targetPort: 8080
ConfigMap
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: bookstore-config
data:
log-level: "info"
server-port: "8080"
cors-origin: "https://bookstore.example.com"
Secret
# 用 kubectl 建立 Secret(不要把 Secret 明文存在 YAML 中)
kubectl create secret generic bookstore-secrets \
--from-literal=database-url='postgres://app:secret@postgres:5432/bookstore' \
--from-literal=jwt-secret='your-jwt-secret-key'
部署
# 部署所有資源
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
# 查看狀態
kubectl get pods
kubectl get services
kubectl logs -f deployment/bookstore-api
健康檢查與滾動更新
Go 中實作 Health Endpoint
package main
import (
"encoding/json"
"net/http"
"sync/atomic"
)
var ready atomic.Bool
func main() {
mux := http.NewServeMux()
// Liveness:應用是否還活著
mux.HandleFunc("/health/live", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"status": "alive"})
})
// Readiness:應用是否準備好接收流量
mux.HandleFunc("/health/ready", func(w http.ResponseWriter, r *http.Request) {
if !ready.Load() {
w.WriteHeader(http.StatusServiceUnavailable)
json.NewEncoder(w).Encode(map[string]string{"status": "not ready"})
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"status": "ready"})
})
// 應用啟動完成後設定 ready
go func() {
// 初始化資料庫連線、快取等...
initDatabase()
ready.Store(true)
}()
mux.HandleFunc("/books", handleBooks)
http.ListenAndServe(":8080", mux)
}
Liveness vs Readiness:
- Liveness Probe:失敗時,K8s 會重啟 Pod
- Readiness Probe:失敗時,K8s 會從 Service 移除該 Pod(不接收流量)
滾動更新
# 在 Deployment spec 中設定更新策略
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # 更新時最多有 1 個 Pod 不可用
maxSurge: 1 # 更新時最多多出 1 個 Pod
# 觸發更新(修改映像版本)
kubectl set image deployment/bookstore-api api=bookstore-api:v1.1.0
# 查看更新進度
kubectl rollout status deployment/bookstore-api
# 回滾到上一版
kubectl rollout undo deployment/bookstore-api
# 查看更新歷史
kubectl rollout history deployment/bookstore-api
Helm 基礎
Helm 是 Kubernetes 的套件管理工具,用 Chart 打包 K8s 資源。
建立 Chart
helm create bookstore
產生的結構:
bookstore/
├── Chart.yaml # Chart 的中繼資料
├── values.yaml # 預設配置值
├── templates/
│ ├── deployment.yaml # Deployment 模板
│ ├── service.yaml # Service 模板
│ ├── configmap.yaml
│ ├── _helpers.tpl # 模板函式
│ └── NOTES.txt # 安裝後的提示訊息
└── charts/ # 依賴的其他 Chart
Chart.yaml
apiVersion: v2
name: bookstore
description: Bookstore API service
type: application
version: 0.1.0 # Chart 版本
appVersion: "1.0.0" # 應用版本
values.yaml
replicaCount: 3
image:
repository: bookstore-api
tag: "v1.0.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 100m
memory: 64Mi
config:
logLevel: info
serverPort: "8080"
模板中使用 values
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "bookstore.fullname" . }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ include "bookstore.name" . }}
template:
spec:
containers:
- name: api
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: 8080
resources:
{{- toYaml .Values.resources | nindent 12 }}
使用 Helm
# 安裝
helm install bookstore ./bookstore
# 用自訂值安裝
helm install bookstore ./bookstore --set replicaCount=5
# 用自訂 values 檔案
helm install bookstore ./bookstore -f production-values.yaml
# 更新
helm upgrade bookstore ./bookstore --set image.tag=v1.1.0
# 回滾
helm rollback bookstore 1
# 移除
helm uninstall bookstore
系列回顧
恭喜你完成了 Go 語言從零到 Web 應用完整系列!18 篇文章走過了一條完整的學習路徑:
基礎篇(一至五)
| 篇 | 主題 | 核心收穫 |
|---|---|---|
| 1 | 為什麼學 Go | 設計哲學、適用場景 |
| 2 | 環境設定 | 安裝、VS Code、Go Modules |
| 3 | 基礎語法 | 變數、型別、流程控制、函式 |
| 4 | 資料結構 | Slice、Map、Struct、Interface |
| 5 | 錯誤處理與併發 | error、Goroutine、Channel |
實戰篇(六至十)
| 篇 | 主題 | 核心收穫 |
|---|---|---|
| 6 | HTTP 基礎 | net/http、Handler、JSON |
| 7 | 專案結構 | 目錄結構、中間件、路由 |
| 8 | 資料庫 | database/sql、SQLite、CRUD |
| 9 | 認證 | bcrypt、JWT、保護端點 |
| 10 | 測試與部署 | testing、httptest、Docker |
面試與陷阱篇(十一至十二)
| 篇 | 主題 | 核心收穫 |
|---|---|---|
| 11 | 面試必問問題 | GMP、Interface、Slice 底層 |
| 12 | 常見陷阱 | nil interface、closure 捕獲、map 併發 |
進階篇(十三至十八)
| 篇 | 主題 | 核心收穫 |
|---|---|---|
| 13 | Web 框架 | Gin vs Echo 對比 |
| 14 | ORM 工具 | GORM vs sqlx 對比 |
| 15 | PostgreSQL | pgx、Migration、JSONB |
| 16 | gRPC | protobuf、Streaming、攔截器 |
| 17 | 微服務 | 拆分策略、通訊模式、設計模式 |
| 18 | Kubernetes | Docker 最佳化、K8s 部署、Helm |
從一行 fmt.Println("Hello, World!") 到在 Kubernetes 上部署微服務,你已經掌握了 Go 開發的完整技能樹。
接下來的建議:
- 動手做一個真實的 side project
- 閱讀優秀的開源 Go 專案程式碼
- 參與 Go 社群,關注 Go Blog 和 Release Notes
- 持續練習,保持寫程式的手感
Happy Coding with Go!