前言

在上一篇文章中,我們認識了 Kubernetes 的核心架構與基本概念。本篇將深入探討 K8s 中最重要的三個資源類型:PodDeploymentService

這三個概念構成了 K8s 應用部署的基礎:

  • Pod:容器運行的最小單位
  • Deployment:管理 Pod 的部署與更新
  • Service:為 Pod 提供穩定的存取端點

掌握這三個概念,你就能開始在 K8s 上部署和管理應用程式了。


Pod:最小部署單位

什麼是 Pod?

Pod 是 K8s 中最小的可部署單位,也是最基本的調度單位。一個 Pod 代表叢集中運行的一個進程。

graph TB
    subgraph "Pod"
        C1[Container 1<br/>Web Server]
        C2[Container 2<br/>Sidecar]
        V[Shared Volume]
        N[Shared Network<br/>localhost]
    end

    C1 <--> N
    C2 <--> N
    C1 --> V
    C2 --> V

Pod 的核心特性

特性 說明
共享網路 同一 Pod 內的容器共享 IP 地址和端口空間
共享儲存 可定義共享的 Volume,容器之間共享資料
共同調度 同一 Pod 的容器永遠在同一節點上運行
生命週期 Pod 是臨時的(Ephemeral),不會被「修復」,只會被替換

為什麼不直接運行容器?

你可能會問:「為什麼不直接管理容器,而要多一層 Pod?」

原因在於 Pod 提供了容器之間協作的抽象

graph LR
    subgraph "傳統方式"
        A[Container A] -.->|需要配置網路| B[Container B]
    end

    subgraph "Pod 方式"
        C[Container A] <-->|localhost| D[Container B]
    end

常見的多容器 Pod 模式:

  1. Sidecar 模式

    • 日誌收集器(Fluentd、Filebeat)
    • 代理服務(Envoy、Istio Proxy)
  2. Ambassador 模式

    • 代理外部服務存取(資料庫 Proxy)
  3. Adapter 模式

    • 標準化輸出格式(Metrics Adapter)

Pod YAML 詳解

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
apiVersion: v1 # API 版本
kind: Pod # 資源類型
metadata:
name: my-pod # Pod 名稱(必填)
namespace: default # 命名空間
labels: # 標籤(用於選擇器)
app: my-app
env: production
annotations: # 註解(用於儲存元數據)
description: "這是一個範例 Pod"
spec:
containers: # 容器列表(必填)
- name: main-container # 容器名稱
image: nginx:1.21 # 映像檔
imagePullPolicy: IfNotPresent # 拉取策略
ports:
- containerPort: 80 # 容器端口
name: http # 端口名稱
env: # 環境變數
- name: NODE_ENV
value: "production"
resources: # 資源配置
requests: # 請求的最小資源
cpu: "100m" # 0.1 CPU
memory: "128Mi" # 128 MiB
limits: # 最大資源限制
cpu: "500m"
memory: "256Mi"
volumeMounts: # 掛載 Volume
- name: data-volume
mountPath: /data

volumes: # Volume 定義
- name: data-volume
emptyDir: {} # 空目錄 Volume

restartPolicy: Always # 重啟策略:Always/OnFailure/Never

Pod 生命週期

stateDiagram-v2
    [*] --> Pending: 創建 Pod
    Pending --> Running: 調度成功且容器啟動
    Pending --> Failed: 調度失敗
    Running --> Succeeded: 所有容器正常終止
    Running --> Failed: 容器異常終止
    Running --> Unknown: 節點失聯
    Succeeded --> [*]
    Failed --> [*]
狀態 說明
Pending Pod 已被接受,但容器尚未創建
Running Pod 已綁定到節點,所有容器已創建
Succeeded 所有容器成功終止,不會重啟
Failed 所有容器終止,至少一個失敗
Unknown 無法取得 Pod 狀態

常用 Pod 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 查看 Pod 列表
kubectl get pods
kubectl get pods -o wide # 顯示更多資訊
kubectl get pods -l app=my-app # 用標籤篩選
kubectl get pods --all-namespaces # 所有命名空間

# 查看 Pod 詳情
kubectl describe pod my-pod

# 查看 Pod 日誌
kubectl logs my-pod # 單容器
kubectl logs my-pod -c container-name # 多容器
kubectl logs -f my-pod # 即時追蹤

# 進入 Pod
kubectl exec -it my-pod -- /bin/sh
kubectl exec -it my-pod -c container-name -- /bin/sh # 多容器

# 刪除 Pod
kubectl delete pod my-pod
kubectl delete pod my-pod --grace-period=0 --force # 強制刪除

Deployment:聲明式部署

為什麼需要 Deployment?

直接創建 Pod 有幾個問題:

  1. Pod 掛掉不會自動重建
  2. 無法方便地水平擴展
  3. 更新時需要手動刪除舊 Pod

Deployment 解決了這些問題,它提供:

  • 自動維持期望的 Pod 副本數
  • 滾動更新與回滾
  • 擴縮容

Deployment、ReplicaSet 與 Pod 的關係

graph TD
    DEP[Deployment<br/>nginx-deployment] --> RS1[ReplicaSet<br/>nginx-rs-abc123<br/>v1.20]
    DEP --> RS2[ReplicaSet<br/>nginx-rs-def456<br/>v1.21]

    RS1 --> P1[Pod 1]
    RS1 --> P2[Pod 2]
    RS1 --> P3[Pod 3]

    RS2 --> P4[Pod 4<br/>新版本]
    RS2 --> P5[Pod 5<br/>新版本]

    style RS1 fill:#f0f0f0
    style RS2 fill:#90EE90
  • Deployment:管理 ReplicaSet,處理更新邏輯
  • ReplicaSet:確保指定數量的 Pod 副本運行
  • Pod:實際運行的容器實例

注意:通常不需要直接管理 ReplicaSet,讓 Deployment 來處理。

Deployment YAML 詳解

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
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3 # 副本數量

selector: # 選擇器(必須匹配 template.labels)
matchLabels:
app: nginx

strategy: # 更新策略
type: RollingUpdate # RollingUpdate 或 Recreate
rollingUpdate:
maxSurge: 1 # 更新時最多額外創建的 Pod 數
maxUnavailable: 0 # 更新時最多不可用的 Pod 數

template: # Pod 模板
metadata:
labels:
app: nginx # 必須與 selector 匹配
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "128Mi"

# 存活探針:容器是否健康
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 5 # 啟動後等待時間
periodSeconds: 10 # 檢查間隔
failureThreshold: 3 # 失敗閾值

# 就緒探針:容器是否準備好接收流量
readinessProbe:
httpGet:
path: /ready
port: 80
initialDelaySeconds: 5
periodSeconds: 5

更新策略詳解

RollingUpdate(滾動更新)

預設策略,逐步替換舊 Pod,確保服務不中斷。

graph LR
    subgraph "初始狀態"
        A1[v1 Pod] --> A2[v1 Pod] --> A3[v1 Pod]
    end

    subgraph "更新中"
        B1[v1 Pod] --> B2[v1 Pod] --> B3[v2 Pod]
    end

    subgraph "更新完成"
        C1[v2 Pod] --> C2[v2 Pod] --> C3[v2 Pod]
    end

參數說明:

參數 說明 範例
maxSurge 更新時可超出期望副本數的最大值 125%
maxUnavailable 更新時可不可用的最大 Pod 數 025%

常見設定:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 零停機更新(保守策略)
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0

# 快速更新(較激進)
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%

Recreate(重建)

先刪除所有舊 Pod,再創建新 Pod。會有短暫停機。

1
2
strategy:
type: Recreate

適用場景:

  • 不能同時運行兩個版本(如資料庫 Schema 不兼容)
  • 開發/測試環境快速更新

Deployment 常用操作

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
# 創建 Deployment
kubectl apply -f deployment.yaml

# 查看 Deployment
kubectl get deployments
kubectl describe deployment nginx-deployment

# 擴縮容
kubectl scale deployment nginx-deployment --replicas=5

# 更新映像
kubectl set image deployment/nginx-deployment nginx=nginx:1.22

# 查看更新狀態
kubectl rollout status deployment/nginx-deployment

# 查看更新歷史
kubectl rollout history deployment/nginx-deployment
kubectl rollout history deployment/nginx-deployment --revision=2

# 回滾到上一個版本
kubectl rollout undo deployment/nginx-deployment

# 回滾到指定版本
kubectl rollout undo deployment/nginx-deployment --to-revision=2

# 暫停/恢復更新
kubectl rollout pause deployment/nginx-deployment
kubectl rollout resume deployment/nginx-deployment

實用技巧:記錄版本變更

1
2
3
4
5
6
7
8
# 使用 --record 記錄變更命令
kubectl set image deployment/nginx-deployment nginx=nginx:1.22 --record

# 查看歷史會顯示變更原因
kubectl rollout history deployment/nginx-deployment
# REVISION CHANGE-CAUSE
# 1 kubectl apply --filename=deployment.yaml
# 2 kubectl set image deployment/nginx-deployment nginx=nginx:1.22 --record=true

Service:服務發現與負載均衡

為什麼需要 Service?

Pod 有以下特點:

  • IP 是動態的:Pod 重建後 IP 會改變
  • Pod 是臨時的:隨時可能被替換

這意味著你不能直接用 Pod IP 存取服務。Service 提供穩定的存取端點

graph LR
    CLIENT[Client] --> SVC[Service<br/>my-svc<br/>ClusterIP: 10.0.0.10]

    SVC --> P1[Pod 1<br/>10.244.1.5]
    SVC --> P2[Pod 2<br/>10.244.2.8]
    SVC --> P3[Pod 3<br/>10.244.3.12]

    style SVC fill:#90EE90

Label 與 Selector

Service 使用 Label Selector 來選擇後端 Pod。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Deployment 中的 Pod 標籤
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
metadata:
labels: # Pod 標籤
app: my-app
version: v1
# ...

---
# Service 選擇器
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector: # 選擇帶有這些標籤的 Pod
app: my-app

Service 四種類型

graph TD
    subgraph "Service 類型比較"
        CI[ClusterIP<br/>叢集內部存取]
        NP[NodePort<br/>節點端口暴露]
        LB[LoadBalancer<br/>外部負載均衡]
        EN[ExternalName<br/>外部服務別名]
    end

    CI --> |預設類型| A[內部服務]
    NP --> |開發測試| B[節點 IP:Port]
    LB --> |生產環境| C[雲端 LB]
    EN --> |外部整合| D[DNS 別名]

1. ClusterIP(預設)

僅叢集內部可存取。

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: my-clusterip-service
spec:
type: ClusterIP # 可省略,這是預設值
selector:
app: my-app
ports:
- port: 80 # Service 端口
targetPort: 8080 # Pod 端口
protocol: TCP
1
2
3
4
# 叢集內存取方式
curl http://my-clusterip-service # 同 Namespace
curl http://my-clusterip-service.default # 指定 Namespace
curl http://my-clusterip-service.default.svc.cluster.local # 完整 FQDN

2. NodePort

透過節點 IP 和指定端口存取,適合開發測試。

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # 可選,範圍 30000-32767
1
2
# 存取方式
curl http://<節點IP>:30080

3. LoadBalancer

在雲端環境自動創建外部負載均衡器。

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
1
2
3
4
# 查看外部 IP
kubectl get svc my-loadbalancer-service
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# my-loadbalancer-service LoadBalancer 10.0.0.5 203.0.113.10 80:31234/TCP

4. ExternalName

將 Service 映射到外部 DNS 名稱。

1
2
3
4
5
6
7
apiVersion: v1
kind: Service
metadata:
name: my-database
spec:
type: ExternalName
externalName: db.example.com
1
2
# 存取時會解析到 db.example.com
curl http://my-database

Service 類型選擇指南

類型 使用場景 優點 缺點
ClusterIP 內部服務通訊 簡單、安全 外部無法直接存取
NodePort 開發測試、臨時存取 不需額外設定 端口範圍限制
LoadBalancer 生產環境對外服務 自動配置 LB 需雲端支援、成本較高
ExternalName 存取外部服務 統一服務發現 僅支援 DNS

Headless Service

不分配 ClusterIP,直接返回 Pod IP,適用於 StatefulSet。

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
clusterIP: None # 關鍵設定
selector:
app: my-statefulset
ports:
- port: 80
1
2
3
4
5
6
# DNS 查詢會返回所有 Pod IP
nslookup my-headless-service
# my-headless-service.default.svc.cluster.local
# 10.244.1.5
# 10.244.2.8
# 10.244.3.12

實戰範例:部署完整應用

讓我們結合 Deployment 和 Service 部署一個完整的應用。

應用架構

graph LR
    USER[使用者] --> SVC[Service<br/>NodePort:30080]
    SVC --> P1[Pod 1<br/>Web App]
    SVC --> P2[Pod 2<br/>Web App]
    SVC --> P3[Pod 3<br/>Web App]

完整配置

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
# app-complete.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
labels:
app: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web
image: nginx:alpine
ports:
- containerPort: 80
resources:
requests:
cpu: "50m"
memory: "32Mi"
limits:
cpu: "100m"
memory: "64Mi"
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 2
periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
name: web-app-service
labels:
app: web-app
spec:
type: NodePort
selector:
app: web-app
ports:
- port: 80
targetPort: 80
nodePort: 30080

部署與測試

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 部署應用
kubectl apply -f app-complete.yaml

# 查看資源狀態
kubectl get all -l app=web-app

# 測試服務
curl http://localhost:30080 # Docker Desktop
minikube service web-app-service # Minikube

# 擴展到 5 個副本
kubectl scale deployment web-app --replicas=5

# 更新映像
kubectl set image deployment/web-app web=nginx:1.23

# 查看更新狀態
kubectl rollout status deployment/web-app

# 清理
kubectl delete -f app-complete.yaml

本章重點回顧

Pod 要點

  1. 最小部署單位:不是容器,是一組容器的抽象
  2. 共享資源:同一 Pod 內的容器共享網路和儲存
  3. 生命週期:臨時的(Ephemeral),不會被修復,只會被替換
  4. 不要直接管理:通常透過 Deployment 來管理

Deployment 要點

  1. 聲明式配置:描述期望狀態,K8s 自動達成
  2. 自動維護副本數:確保指定數量的 Pod 運行
  3. 滾動更新:無停機更新,支援回滾
  4. 與 ReplicaSet 關係:Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod

Service 要點

  1. 穩定端點:解決 Pod IP 動態變化問題
  2. 服務發現:透過 DNS 名稱存取服務
  3. 負載均衡:自動分配流量到多個 Pod
  4. 四種類型:ClusterIP、NodePort、LoadBalancer、ExternalName

下一篇預告

在下一篇文章中,我們將深入探討 K8s 網路

  • Kubernetes 網路模型
  • 服務發現機制詳解
  • Ingress Controller 與路由配置
  • 網路策略(Network Policy)

系列文章導覽

  • Part 1:入門篇 - 認識 K8s 與核心架構
  • Part 2:基礎篇 - Pod、Deployment 與 Service(本篇)
  • Part 3:網路篇 - 服務發現與流量管理
  • Part 4:配置與存儲篇 - ConfigMap、Secret 與 Volume
  • Part 5:進階篇 - 資源管理與自動擴展
  • Part 6:實戰篇 - 部署完整微服務應用

參考資源