前言 當應用程式部署到 K8s 後,如何確保它穩定運行且高效利用資源?本篇將探討:
資源管理 :設定 CPU 和記憶體的請求與限制
自動擴展 :根據負載自動調整 Pod 數量
調度策略 :控制 Pod 在哪些節點上運行
健康檢查 :確保只有健康的 Pod 接收流量
這些是生產環境部署的必備知識。
資源管理:Requests 與 Limits 為什麼需要資源管理? 沒有資源限制的情況下:
某個 Pod 可能消耗所有 CPU/記憶體
導致其他 Pod 無法正常運行
節點可能因 OOM(Out of Memory)崩潰
graph TD
subgraph "沒有限制"
A[Pod A] -->|佔用 80% CPU| N1[Node]
B[Pod B] -->|飢餓| N1
C[Pod C] -->|飢餓| N1
end
subgraph "有限制"
D[Pod A: 限制 30%] --> N2[Node]
E[Pod B: 限制 30%] --> N2
F[Pod C: 限制 30%] --> N2
end
Requests vs Limits
概念
說明
用途
Requests
最小需求
調度決策、資源預留
Limits
最大限制
防止資源濫用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 apiVersion: v1 kind: Pod metadata: name: resource-demo spec: containers: - name: app image: my-app:latest resources: requests: cpu: "250m" memory: "128Mi" limits: cpu: "500m" memory: "256Mi"
CPU 資源單位
表示法
說明
1
1 個 CPU 核心
500m
0.5 CPU(500 毫核心)
100m
0.1 CPU
CPU 超出 Limits :會被限流(throttle),不會被殺掉
記憶體資源單位
表示法
說明
128Mi
128 MiB(2^20 bytes)
1Gi
1 GiB
128M
128 MB(10^6 bytes)
記憶體超出 Limits :Pod 會被 OOMKilled(重啟)
QoS 類別 K8s 根據 requests/limits 設定將 Pod 分為三個服務品質類別:
graph TD
G[Guaranteed<br/>最高優先級] --> B[Burstable<br/>中等優先級]
B --> BE[BestEffort<br/>最低優先級]
G -->|"requests = limits"| G
B -->|"設定了部分資源"| B
BE -->|"沒有設定資源"| BE
QoS 類別
條件
OOM 優先被殺順序
Guaranteed
所有容器都設定相同的 requests 和 limits
最後
Burstable
至少一個容器設定了 requests
中間
BestEffort
沒有設定任何資源請求或限制
最先
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 resources: requests: cpu: "500m" memory: "256Mi" limits: cpu: "500m" memory: "256Mi" resources: requests: cpu: "250m" memory: "128Mi" limits: cpu: "500m" memory: "256Mi"
LimitRange:命名空間預設值 為避免忘記設定資源,可使用 LimitRange 設定預設值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 apiVersion: v1 kind: LimitRange metadata: name: default-limits namespace: production spec: limits: - default: cpu: "500m" memory: "256Mi" defaultRequest: cpu: "100m" memory: "64Mi" max: cpu: "2" memory: "1Gi" min: cpu: "50m" memory: "32Mi" type: Container
ResourceQuota:命名空間配額 限制整個命名空間的資源使用量:
1 2 3 4 5 6 7 8 9 10 11 12 13 apiVersion: v1 kind: ResourceQuota metadata: name: resource-quota namespace: production spec: hard: requests.cpu: "10" requests.memory: "20Gi" limits.cpu: "20" limits.memory: "40Gi" pods: "50" persistentvolumeclaims: "10"
自動擴展 擴展類型 graph LR
A[自動擴展] --> HPA[水平擴展 HPA<br/>增減 Pod 數量]
A --> VPA[垂直擴展 VPA<br/>調整資源配置]
A --> CA[叢集擴展 CA<br/>增減節點數量]
Horizontal Pod Autoscaler(HPA) 根據 CPU、記憶體或自定義指標自動調整 Pod 副本數。
graph LR
M[Metrics Server] -->|收集| HPA[HPA Controller]
HPA -->|調整副本數| DEP[Deployment]
DEP --> P1[Pod 1]
DEP --> P2[Pod 2]
DEP --> P3[Pod 3]
安裝 Metrics Server 1 2 3 4 5 6 7 8 9 minikube addons enable metrics-server kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml kubectl top nodes kubectl top pods
創建 HPA 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: web-app-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: web-app minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 50 periodSeconds: 60 scaleUp: stabilizationWindowSeconds: 0 policies: - type: Pods value: 4 periodSeconds: 60
快速創建 HPA 1 2 3 4 5 6 7 8 kubectl autoscale deployment web-app \ --min=2 --max=10 \ --cpu-percent=70 kubectl get hpa kubectl describe hpa web-app-hpa
HPA 運作原理 1 期望副本數 = 當前副本數 × (當前指標值 / 目標指標值)
範例 :
當前副本:3
當前 CPU 使用率:90%
目標 CPU 使用率:70%
計算:3 × (90 / 70) = 3.86 → 4 個副本
Vertical Pod Autoscaler(VPA) 自動調整 Pod 的 CPU 和記憶體請求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: web-app-vpa spec: targetRef: apiVersion: apps/v1 kind: Deployment name: web-app updatePolicy: updateMode: "Auto" resourcePolicy: containerPolicies: - containerName: "*" minAllowed: cpu: "100m" memory: "50Mi" maxAllowed: cpu: "2" memory: "2Gi" controlledResources: ["cpu" , "memory" ]
注意 :VPA 需要額外安裝,且與 HPA 一起使用時需謹慎配置。
Pod 調度策略 Node Selector 最簡單的節點選擇方式:
1 2 3 4 5 6 7 8 9 10 11 apiVersion: v1 kind: Pod metadata: name: gpu-pod spec: nodeSelector: gpu: "true" disk: "ssd" containers: - name: gpu-app image: gpu-app:latest
1 2 3 kubectl label nodes node-1 gpu=true kubectl label nodes node-1 disk=ssd
Node Affinity 更強大的節點選擇規則:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 apiVersion: v1 kind: Pod metadata: name: affinity-pod spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: zone operator: In values: - asia-east1-a - asia-east1-b preferredDuringSchedulingIgnoredDuringExecution: - weight: 80 preference: matchExpressions: - key: disktype operator: In values: - ssd containers: - name: app image: my-app:latest
Pod Affinity / Anti-Affinity 控制 Pod 之間的關係:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 apiVersion: apps/v1 kind: Deployment metadata: name: web-app spec: replicas: 3 template: spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: web-app topologyKey: kubernetes.io/hostname podAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: cache topologyKey: kubernetes.io/hostname containers: - name: web image: web-app:latest
Taints 與 Tolerations 「污點」讓節點拒絕 特定 Pod,「容忍」讓 Pod 克服 污點。
1 2 3 4 5 kubectl taint nodes node-1 dedicated=gpu:NoSchedule kubectl taint nodes node-1 dedicated=gpu:NoSchedule-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 apiVersion: v1 kind: Pod metadata: name: gpu-pod spec: tolerations: - key: "dedicated" operator: "Equal" value: "gpu" effect: "NoSchedule" containers: - name: gpu-app image: gpu-app:latest
Effect
說明
NoSchedule
新 Pod 不會調度到此節點
PreferNoSchedule
盡量避免調度(軟性)
NoExecute
不調度且驅逐現有 Pod
健康檢查:Probes 三種探針 graph LR
SP[Startup Probe<br/>啟動檢查] --> LP[Liveness Probe<br/>存活檢查]
LP --> RP[Readiness Probe<br/>就緒檢查]
SP -->|失敗| KILL1[重啟容器]
LP -->|失敗| KILL2[重啟容器]
RP -->|失敗| REMOVE[從 Service 移除]
探針
用途
失敗行為
Startup
檢查應用是否已啟動
重啟容器
Liveness
檢查應用是否正常運行
重啟容器
Readiness
檢查應用是否準備好接收流量
從 Service 移除
探針類型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 livenessProbe: httpGet: path: /healthz port: 8080 httpHeaders: - name: Custom-Header value: Awesome livenessProbe: tcpSocket: port: 8080 livenessProbe: exec: command: - cat - /tmp/healthy livenessProbe: grpc: port: 8080
完整探針配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 apiVersion: v1 kind: Pod metadata: name: probe-demo spec: containers: - name: app image: my-app:latest ports: - containerPort: 8080 startupProbe: httpGet: path: /healthz port: 8080 failureThreshold: 30 periodSeconds: 10 livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 0 periodSeconds: 10 timeoutSeconds: 1 successThreshold: 1 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3
探針最佳實踐
總是設定 readinessProbe :避免流量發送到未就緒的 Pod
合理設定 liveness 閾值 :避免因短暫問題頻繁重啟
慢啟動應用使用 startupProbe :替代增加 initialDelaySeconds
分離健康端點 :healthz(健康)和 ready(就緒)應該是不同的邏輯
實戰範例:生產就緒配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 apiVersion: apps/v1 kind: Deployment metadata: name: production-app spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 selector: matchLabels: app: production-app template: metadata: labels: app: production-app spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: production-app topologyKey: kubernetes.io/hostname containers: - name: app image: my-app:v1.0.0 ports: - containerPort: 8080 resources: requests: cpu: "250m" memory: "256Mi" limits: cpu: "500m" memory: "512Mi" startupProbe: httpGet: path: /healthz port: 8080 failureThreshold: 30 periodSeconds: 10 livenessProbe: httpGet: path: /healthz port: 8080 periodSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: 8080 periodSeconds: 5 failureThreshold: 3 env: - name: NODE_ENV value: "production" securityContext: readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1000 --- apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: production-app-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: production-app minReplicas: 3 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 25 periodSeconds: 60 --- apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: production-app-pdb spec: minAvailable: 2 selector: matchLabels: app: production-app
本章重點回顧 資源管理
Requests :調度決策、資源保證
Limits :資源上限、防止濫用
QoS :Guaranteed > Burstable > BestEffort
LimitRange/ResourceQuota :命名空間級別控制
自動擴展
HPA :基於 CPU/記憶體自動調整副本數
VPA :自動調整資源請求
設定合理的 min/max :防止過度擴展
調度策略
Node Selector :簡單標籤選擇
Affinity :複雜的親和性規則
Taints/Tolerations :節點排斥與容忍
健康檢查
Startup :慢啟動應用
Liveness :應用是否存活
Readiness :是否可接收流量
下一篇預告 在最後一篇文章中,我們將進行 實戰部署 :
完整微服務應用架構
Helm 簡介與應用
多環境配置管理
故障排除技巧
系列文章導覽
Part 1:入門篇 - 認識 K8s 與核心架構
Part 2:基礎篇 - Pod、Deployment 與 Service
Part 3:網路篇 - 服務發現與流量管理
Part 4:配置與存儲篇 - ConfigMap、Secret 與 Volume
Part 5:進階篇 - 資源管理與自動擴展 (本篇)
Part 6:實戰篇 - 部署完整微服務應用
參考資源