K8s 部署實戰:Helm Charts 與服務編排

商業價值:標準化部署讓「17 個通路獨立升級不互相影響」,確保 導讀篇提到的「系統穩定性」——單一通路故障不會拖垮整個系統。

為什麼不用其他方案?

方案 優點 缺點 適用場景
Helm Charts(本文) 標準化、版本控制、可重用 學習曲線 多服務微服務架構
純 YAML 簡單直接 重複多、難維護 單一服務
Kustomize 原生支援 複雜覆寫較難 簡單環境差異
Docker Compose 開發方便 不適合生產環境 本地開發

前言:微服務部署的挑戰

多通路 OMS 系統包含:

服務類型 數量 說明
Consumer Job 17+ 個 每個通路一個
API 服務 10+ 個 訂單、商品、物流等
Web 前端 3 個 商戶、管理、OpenAPI
問題:如何有效管理這麼多服務的部署?

解決方案:Helm Charts

Chart 目錄結構

helm/
└── oms-consumer-shopee/
├── Chart.yaml # Chart 基本資訊
├── values.yaml # 預設變數
├── values-dev.yaml # 開發環境
├── values-prod.yaml # 正式環境
├── config/ # 應用程式配置
│ ├── application.yml
│ └── logback.xml
└── templates/ # K8s 資源模板
├── deployment.yaml
├── service.yaml
└── configmap.yaml

Deployment 設定

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}
namespace: oms
labels:
app: {{ .Chart.Name }}

spec:
replicas: {{ .Values.replicas }}
selector:
matchLabels:
app: {{ .Chart.Name }}

template:
metadata:
annotations:
# ConfigMap 變更時自動觸發滾動更新
checksum/config: {{ include (print $.Template.BasePath “/configmap.yaml”) . | sha256sum }}
# Prometheus 監控
prometheus.io/scrape: “true”
prometheus.io/path: /metrics
prometheus.io/port: “8080”

labels:
app: {{ .Chart.Name }}
version: {{ .Values.image.version }}

spec:
containers:
– name: {{ .Chart.Name }}
image: “{{ .Values.image.repository }}:{{ .Values.image.version }}”
imagePullPolicy: {{ .Values.image.pullPolicy }}

# 資源限制
resources:
requests:
cpu: {{ .Values.resources.requests.cpu }}
memory: {{ .Values.resources.requests.memory }}
limits:
cpu: {{ .Values.resources.limits.cpu }}
memory: {{ .Values.resources.limits.memory }}

# 健康檢查
livenessProbe:
httpGet:
path: /health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10

readinessProbe:
httpGet:
path: /health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 5

# 優雅關閉
lifecycle:
preStop:
exec:
command: [“curl”, “-XPOST”, “http://localhost:8080/shutdown”]


values.yaml:環境變數化

# values.yaml(預設值)
replicas: 2

image:
repository: registry.example.com/oms/consumer-shopee
version: latest
pullPolicy: Always

resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi

# values-prod.yaml(正式環境覆寫)
replicas: 5

image:
version: v1.2.3
pullPolicy: IfNotPresent

resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi

部署指令

# 開發環境
helm upgrade –install consumer-shopee ./helm/oms-consumer-shopee \
-f values-dev.yaml

# 正式環境
helm upgrade –install consumer-shopee ./helm/oms-consumer-shopee \
-f values-prod.yaml \
–set image.version=v1.2.3


Secret 管理

# 敏感資訊不放在 values.yaml
env:
– name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username

– name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password

# 建立 Secret
kubectl create secret generic db-credentials \
–namespace oms \
–from-literal=username=admin \
–from-literal=password=secret123

ConfigMap 管理

# templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Chart.Name }}-config
data:
{{ (.Files.Glob “config/*”).AsConfig | indent 2 }}
效果:config/ 目錄下的檔案會自動打包成 ConfigMap

健康檢查整合

Probe 類型 端點 失敗行為
liveness /health/liveness 重啟 Pod
readiness /health/readiness 從 Service 移除
# 分離 liveness 和 readiness
livenessProbe:
httpGet:
path: /health/liveness
port: 8080
initialDelaySeconds: 30 # 啟動後等待時間
periodSeconds: 10 # 檢查間隔
timeoutSeconds: 5 # 超時時間
failureThreshold: 3 # 失敗幾次後重啟

readinessProbe:
httpGet:
path: /health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 5
failureThreshold: 3


多通路部署策略

17 個通路,每個都有獨立的 Helm Chart:

helm/
├── oms-consumer-shopee/
├── oms-consumer-momo/
├── oms-consumer-yahoo/
├── oms-consumer-pchome/
├── oms-consumer-rakuten/
├── oms-consumer-shopify/
├── oms-consumer-shopline/
└── … (共 17 個)

批次部署腳本

#!/bin/bash

CHANNELS=(
“shopee”
“momo”
“yahoo”
“pchome”
“rakuten”
“shopify”
“shopline”
)

VERSION=$1

for channel in “${CHANNELS[@]}”; do
echo “Deploying $channel…”

helm upgrade –install “consumer-${channel}” \
“./helm/oms-consumer-${channel}” \
–set image.version=$VERSION \
–namespace oms

done

echo “All channels deployed!”


Service 與 Ingress

# templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}
namespace: oms
spec:
type: ClusterIP
ports:
– port: 80
targetPort: 8080
name: http
selector:
app: {{ .Chart.Name }}
# Ingress 設定(API Gateway)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: oms-api-gateway
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
– host: api.example.com
http:
paths:
– path: /orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80

CI/CD 整合

# .gitlab-ci.yml
stages:
– build
– deploy

build:
stage: build
script:
– docker build -t registry.example.com/oms/consumer-shopee:$CI_COMMIT_SHA .
– docker push registry.example.com/oms/consumer-shopee:$CI_COMMIT_SHA

deploy-dev:
stage: deploy
script:
– helm upgrade –install consumer-shopee ./helm/oms-consumer-shopee
–set image.version=$CI_COMMIT_SHA
-f values-dev.yaml
only:
– develop

deploy-prod:
stage: deploy
script:
– helm upgrade –install consumer-shopee ./helm/oms-consumer-shopee
–set image.version=$CI_COMMIT_TAG
-f values-prod.yaml
only:
– tags
when: manual


實戰踩坑

踩坑 1:ConfigMap 改了但 Pod 沒更新
情境:改了 application.yml 但服務行為沒變
原因:K8s 不會自動重啟 Pod,舊的 ConfigMap 還在記憶體中
解法:在 Deployment 加 checksum annotation,ConfigMap 變化時觸發滾動更新
踩坑 2:OOM Killed 但沒收到告警
情境:服務突然重啟,查了半天才發現是記憶體不足
原因:JVM heap 設定超過 container limits,被 K8s 強制 kill
解法:JVM heap 設為 limits 的 70%,並設定 Prometheus 告警監控 OOM 事件
踩坑 3:滾動更新時服務中斷
情境:部署新版本時用戶收到 502 錯誤
原因:新 Pod 還沒 ready 就把舊 Pod 砍掉,或 preStop 沒有 graceful shutdown
解法:設好 readinessProbe + preStop hook,確保流量先切換再關閉

總結

設計 效果
Helm Chart 標準化 每個服務結構一致,易於維護
values 分環境 同一 Chart 部署不同環境
ConfigMap 動態載入 配置與程式碼分離
Secret 管理 敏感資訊安全儲存
健康檢查整合 K8s 自動管理故障
多通路獨立部署 故障隔離,獨立升級

上一篇 系列目錄 完結
OpenTracing分散式追蹤 系列導讀 本篇為系列最終篇

這是「多通路電商 OMS 系統實戰」系列的最終篇。感謝閱讀,希望對你的系統設計有所幫助!

留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *