Kubernetes 模式
用於可靠部署、管理和除錯工作負載的生產級 Kubernetes 模式。
啟用時機
- 編寫 Kubernetes 清單(Deployment、Service、Ingress、Job)
- 配置資源請求/限制、存活/就緒探針
- 設定 RBAC、命名空間或 ServiceAccount
- 管理 K8s 中的配置與密碼
- 除錯 CrashLoopBackOff、OOMKilled、待處理 Pod 或映像拉取錯誤
- 配置 HPA(水平 Pod 自動擴展器)或 PodDisruptionBudget
- 審查 K8s YAML 的安全性與正確性
使用時機
與上方啟用時機相同。此別名符合 repo 技能格式約定。每當您編寫、審查或除錯 Kubernetes YAML 與工作負載時,均可使用此技能。
運作方式
此技能按任務分類,提供可直接複製貼上的生產級 YAML 模式與 kubectl 除錯指令:
- Deployment 範本 — 完整配置的生產
Deployment,包含安全上下文、滾動更新策略、三種探針類型、資源限制及 ConfigMap/Secret 環境注入。 - 探針 — 啟動、存活與就緒探針的決策表,並附正確的
failureThreshold × periodSeconds計算。 - Service 與 Ingress — ClusterIP、LoadBalancer 及 TLS Ingress 模式,附 cert-manager 註解。
- ConfigMap 與 Secret —
envFrom、檔案掛載及外部密碼指引。 - 資源管理 — 依工作負載類型(Web API、JVM、Worker、Sidecar)的請求與限制經驗法則。
- RBAC — 最小權限 ServiceAccount → Role → RoleBinding 鏈。
- HPA 與 PDB — 自動擴展和節點排空安全配置。
- Job 與 CronJob — 一次性與排程工作負載模式,附正確的
restartPolicy。 - kubectl 速查表 — 日誌、執行、回滾、連接埠轉發、乾執行及常見錯誤診斷指令。
- 反模式與檢查清單 — 不該做的事項,以及安全/可靠性/可觀測性檢查清單。
範例
請參閱下方各節以取得完整的可執行範例。快速參考:
| 任務 | 跳轉至 |
|---|---|
| 完整生產 Deployment YAML | 核心工作負載模式 |
| 探針配置 | 探針 |
| RBAC 最小權限設定 | RBAC |
| 除錯 CrashLoopBackOff | kubectl 除錯速查表 |
| 自動擴展 | HPA |
核心工作負載模式
Deployment — 生產範本
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: my-namespace
labels:
app: my-app
version: "1.0.0"
spec:
replicas: 3
selector:
matchLabels:
app: my-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 更新期間允許 1 個額外 Pod
maxUnavailable: 0 # 絕不低於所需數量
template:
metadata:
labels:
app: my-app
version: "1.0.0"
spec:
# Pod 層級的安全上下文
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
# 優雅關閉
terminationGracePeriodSeconds: 30
containers:
- name: my-app
image: ghcr.io/org/my-app:1.0.0 # 絕不使用 :latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
protocol: TCP
# 資源請求與限制皆為必要
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
# 容器安全上下文
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# 探針(請參閱下方探針章節)
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 0
periodSeconds: 30
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 2
# 來自 ConfigMap 和 Secret 的環境變數
envFrom:
- configMapRef:
name: my-app-config
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-app-secrets
key: db-password
# 當 readOnlyRootFilesystem: true 時可寫入的暫存目錄
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
探針 — 存活、就緒、啟動
了解何時使用每種探針至關重要:
| 探針 | 失敗動作 | 用途 |
|---|---|---|
startupProbe | 若啟動緩慢則終止容器 | 啟動緩慢的應用程式(JVM、Python) |
livenessProbe | 重新啟動容器 | 死結 / 掛起程序偵測 |
readinessProbe | 從 Service 端點移除 | 暫時性不可用(資料庫重新連線) |
# 正確模式:startupProbe 涵蓋緩慢啟動,
# 然後 liveness/readiness 接手
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30 # 30 * 5s = 150s 最大啟動時間
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 30
failureThreshold: 3 # 3 * 30s = 90s 後重新啟動
readinessProbe:
httpGet:
path: /ready # 獨立端點:檢查資料庫、快取等
port: 8080
periodSeconds: 10
failureThreshold: 2
# 錯誤:沒有 startupProbe 就使用 initialDelaySeconds
# 如果應用程式需要 60 秒啟動,請改用 startupProbe
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60 # 不好:任意等待,競態條件
Service 與 Ingress
Service 類型
# ClusterIP(預設)— 僅限內部使用
apiVersion: v1
kind: Service
metadata:
name: my-app
namespace: my-namespace
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: ClusterIP
# LoadBalancer — 外部流量(雲端供應商)
spec:
type: LoadBalancer
ports:
- port: 443
targetPort: 8080
含 TLS 的 Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
namespace: my-namespace
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: my-app-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
ConfigMap 與 Secret
ConfigMap — 非敏感配置
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-config
namespace: my-namespace
data:
LOG_LEVEL: "info"
APP_ENV: "production"
MAX_CONNECTIONS: "100"
# 對於複雜配置,掛載為檔案
app.yaml: |
server:
port: 8080
timeout: 30s
# 將 ConfigMap 掛載為檔案
volumes:
- name: config
configMap:
name: my-app-config
items:
- key: app.yaml
path: app.yaml
volumeMounts:
- name: config
mountPath: /etc/app
readOnly: true
Secret — 敏感資料
# 從字面值建立 Secret(CLI,然後儲存在 Vault/SOPS 中)
kubectl create secret generic my-app-secrets \
--from-literal=db-password='s3cr3t' \
--namespace=my-namespace \
--dry-run=client -o yaml | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: my-app-secrets
namespace: my-namespace
type: Opaque
# 值為 base64 編碼(未加密 — 請使用 Sealed Secrets 或 ESO 實現真正加密)
data:
db-password: czNjcjN0 # 's3cr3t' 的 base64
重要: 原始 Kubernetes Secret 僅是 base64 編碼,除非您的叢集已配置加密,否則不會在靜態時加密。請使用 Sealed Secrets 或 External Secrets Operator 進行生產部署。
資源請求與限制
resources:
requests: # 排程器使用此設定放置 Pod
cpu: "100m" # 100 毫核 = 0.1 CPU
memory: "128Mi"
limits: # 容器超過此設定將被終止/節流
cpu: "500m"
memory: "256Mi"
經驗法則:
| 工作負載類型 | CPU 請求 | 記憶體請求 | 備註 |
|---|---|---|---|
| Web API | 100–250m | 128–256Mi | 限制設為請求的 2-4 倍 |
| Worker/消費者 | 250–500m | 256–512Mi | 為可預測性,記憶體限制 = 請求 |
| JVM 應用程式 | 500m–1 | 512Mi–2Gi | 為 JVM 開銷在 -Xmx 之上預留空間 |
| Sidecar | 10–50m | 32–64Mi | 保持最小 |
# 錯誤:沒有請求或限制 — 不可預測的排程、OOM 驅逐
containers:
- name: app
image: myapp:latest
# 遺漏 resources: {} — 這在生產環境中是危險的
# 錯誤:有限制但無請求 — 請求預設為限制,過度保留容量
resources:
limits:
cpu: "2"
memory: "1Gi"
# 遺漏 requests — 將預設為限制值
RBAC — 角色與 ServiceAccount
最小權限原則
兩種模式取決於應用程式是否呼叫 Kubernetes API:
模式 A — 應用程式不需要 Kubernetes API(大多數應用程式)
在 ServiceAccount 上停用 Token 自動掛載。不需要 Role/RoleBinding。
# 停用 Token 的 ServiceAccount — 最安全的預設值
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: my-namespace
automountServiceAccountToken: false # 不將 K8s API Token 注入 Pod
# 在 Deployment 中引用 — 無 Token,無 API 存取權
spec:
template:
spec:
serviceAccountName: my-app-sa
automountServiceAccountToken: false # 雙重保險:也在 Pod 層級設定
模式 B — 應用程式需要 Kubernetes API(Operator、控制器、配置監視器)
啟用 Token,並僅授予實際所需的權限。
# 1. ServiceAccount — 為此 SA 啟用 Token
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: my-namespace
automountServiceAccountToken: true # 需要 Token:應用程式呼叫 K8s API
# 2. Role — 僅授予應用程式所需的權限(命名空間範圍)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: my-app-role
namespace: my-namespace
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"] # 唯讀,特定資源
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["my-app-secrets"] # 依名稱限制為特定 Secret
verbs: ["get"]
# 3. 將 Role 綁定到 ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-app-rolebinding
namespace: my-namespace
subjects:
- kind: ServiceAccount
name: my-app-sa
namespace: my-namespace
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: my-app-role
# 4. 在 Deployment 中引用 SA
spec:
template:
spec:
serviceAccountName: my-app-sa
# automountServiceAccountToken 預設為 SA 的 true — Token 被注入
水平 Pod 自動擴展器 (HPA)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
namespace: my-namespace
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2 # 為高可用性至少 2 個副本
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # 平均 CPU > 70% 時擴展
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
HPA 需要在所有容器上設定
resources.requests— 它計算利用率的方式為current / request。
PodDisruptionBudget (PDB)
防止節點排空或滾動更新期間過多 Pod 下線:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
namespace: my-namespace
spec:
minAvailable: 2 # 或使用 maxUnavailable: 1
selector:
matchLabels:
app: my-app
命名空間與多租戶
# 建立附帶資源配額的命名空間
kubectl create namespace my-namespace
# 套用 ResourceQuota 以限制命名空間消耗
kubectl apply -f - <<EOF
apiVersion: v1
kind: ResourceQuota
metadata:
name: my-namespace-quota
namespace: my-namespace
spec:
hard:
requests.cpu: "4"
requests.memory: 4Gi
limits.cpu: "8"
limits.memory: 8Gi
pods: "20"
EOF
Job 與 CronJob
# 一次性 Job(資料庫遷移、資料處理)
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
namespace: my-namespace
spec:
backoffLimit: 3 # 失敗時最多重試 3 次
ttlSecondsAfterFinished: 3600 # 1 小時後自動刪除
template:
spec:
restartPolicy: OnFailure # Job 使用 Never(而非 Always)
containers:
- name: migrate
image: ghcr.io/org/my-app:1.0.0
command: ["python", "manage.py", "migrate"]
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
restartPolicy: Never
# 排程 Job(CronJob)
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-cleanup
namespace: my-namespace
spec:
schedule: "0 2 * * *" # 每天早上 2 點(Cron 格式)
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: Never
containers:
- name: cleanup
image: alpine:3.18
command: ["sh", "-c", "cleanup-script.sh"]
resources:
requests:
cpu: "10m"
memory: "32Mi"
limits:
cpu: "50m"
memory: "64Mi"
kubectl 除錯速查表
# 取得 Pod 狀態與節點資訊
kubectl get pods -n my-namespace -o wide
# 檢視 Pod 詳細資訊(包括事件)
kubectl describe pod <pod-name> -n my-namespace
# 即時串流日誌(類似 tail -f)
kubectl logs -f <pod-name> -n my-namespace
# 取得先前當機容器的日誌
kubectl logs <pod-name> --previous -n my-namespace
# 在 Pod 中執行互動式 Shell
kubectl exec -it <pod-name> -- /bin/sh
# 連接埠轉發至本機
kubectl port-forward pod/<pod-name> 8080:8080 -n my-namespace
# 乾執行並驗證 YAML(不套用)
kubectl apply -f manifest.yaml --dry-run=client
# 回滾 Deployment
kubectl rollout undo deployment/my-app -n my-namespace
# 檢查 Deployment 復原狀態
kubectl rollout status deployment/my-app -n my-namespace
# 檢視資源使用情況(需要 metrics-server)
kubectl top pods -n my-namespace
# 取得所有 API 資源
kubectl api-resources
常見錯誤診斷
| 錯誤 | 診斷 | 修復 |
|---|---|---|
CrashLoopBackOff | kubectl logs --previous 取得死前日誌 | 檢查應用程式錯誤、設定、資源限制 |
OOMKilled | kubectl describe pod → 退出代碼 137 | 增加 resources.limits.memory |
ImagePullBackOff | kubectl describe pod → 事件 | 檢查映像路徑、標籤、認證(imagePullSecrets) |
Pending | kubectl describe pod → 事件 | 檢查資源可用性、PVC、節點選擇器、污點 |
RunContainerError | kubectl logs | 檢查命令、工作目錄、volumeMounts、環境變數 |
反模式與檢查清單
不該做的事(反模式)
- 不要在環境中使用
:latest標籤 — 總是固定到不可變的版本哈希或語意化版本。 - 不要省略資源請求和限制 — 這會導致不可預測的行為和 OOM 驅逐。
- 不要將 ConfigMap 掛載為環境變數(對於大型配置)— 改為掛載為檔案。
- 不要為 ServiceAccount 啟用自動掛載(如果應用程式不需要 K8s API)— 這是一個安全漏洞。
- 不要使用
ClusterRole/ClusterRoleBinding(如果命名空間範圍的權限就足夠了)— 遵循最小權限原則。 - 不要將 Secret 提交到 Git — 使用 Sealed Secrets、External Secrets Operator 或 Vault。
- 不要將服務暴露在不安全的連接埠上(未加密的 HTTP)— 使用 Ingress 和 TLS。
- 不要忽視 probe 的
failureThreshold與periodSeconds數學計算 — 確定正確的逾時時間。 - 不要依賴
initialDelaySeconds來處理緩慢啟動 — 改用startupProbe。
部署檢查清單
- 使用固定映像標籤(非
:latest) - 設定資源請求與限制
- 配置至少一個存活探針(除非是批次 Job)
- 配置啟動探針(用於啟動緩慢的應用程式)
- 配置就緒探針(如果應用程式具有暫時性不可用狀態)
- 以非 root 使用者身份執行(
runAsNonRoot: true) - 停用權限提升(
allowPrivilegeEscalation: false) - 設定唯讀根檔案系統(
readOnlyRootFilesystem: true,含/tmp的emptyDir) - 刪除所有不必要的 Linux 功能(
capabilities.drop: ["ALL"]) - 設定優雅關閉時間(
terminationGracePeriodSeconds: 30) - 使用
rollingUpdate策略(maxSurge: 1、maxUnavailable: 0) - 將配置使用 ConfigMap(環境變數或檔案掛載)
- 將密碼使用 Secret(非 ConfigMap — 並使用 ESO/Sealed Secrets 進行加密)
- 應用最小權限 RBAC(如果應用程式需要 API 存取權)
- 設定 HPA 以進行自動擴展(對於無狀態工作負載)
- 為高可用性設定 PDB(對於有狀態工作負載或重要服務)
- 新增資源配額(對於多租戶命名空間)
- 設定
PodDisruptionBudget以維持服務穩定性 - 新增
livenessProbe和readinessProbe以確保應用程式健康 - 觀察日誌並設定監控告警(用於錯誤率和異常)
- 啟用可觀測性(指標、分散式追蹤、結構化日誌)
使用此技能來編寫可靠的、遵循最佳實務且萬無一失的 Kubernetes 工作負載。