副本集

ReplicaSet 的目的是在任何給定時間維持一組穩定的副本 Pod 執行。通常,你定義一個 Deployment,並讓該 Deployment 自動管理 ReplicaSet。

ReplicaSet 的目的是在任何給定時間維持一組穩定的副本 Pod 執行。因此,它常用於保證指定數量的相同 Pod 的可用性。

ReplicaSet 如何工作

ReplicaSet 透過以下欄位定義:一個選擇器(selector),用於指定如何識別它可以獲取的 Pod;一個副本數(replicas),指示它應該維護多少個 Pod;以及一個 Pod 模板(pod template),用於指定它應該建立的新 Pod 的資料,以滿足副本數標準。ReplicaSet 然後透過按需建立和刪除 Pod 來達到所需的數量。當 ReplicaSet 需要建立新 Pod 時,它會使用其 Pod 模板。

ReplicaSet 透過 Pod 的 metadata.ownerReferences 欄位與其 Pod 關聯,該欄位指定當前物件所屬的資源。所有被 ReplicaSet 獲取的 Pod 在其 ownerReferences 欄位中都包含其所屬 ReplicaSet 的識別資訊。正是透過此連結,ReplicaSet 瞭解其維護的 Pod 的狀態並相應地進行規劃。

ReplicaSet 透過使用其選擇器來識別要獲取的新 Pod。如果有一個 Pod 沒有 OwnerReference,或者 OwnerReference 不是一個 Controller,並且它匹配 ReplicaSet 的選擇器,它將立即被該 ReplicaSet 獲取。

何時使用 ReplicaSet

ReplicaSet 確保在任何給定時間都有指定數量的 Pod 副本在執行。然而,Deployment 是一個更高級別的概念,它管理 ReplicaSet 並提供對 Pod 的宣告式更新以及許多其他有用的功能。因此,我們建議使用 Deployment 而不是直接使用 ReplicaSet,除非你需要自定義更新編排或者根本不需要更新。

這實際上意味著你可能永遠不需要操作 ReplicaSet 物件:而是使用 Deployment,並在其 spec 部分定義你的應用程式。

示例

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # modify replicas according to your case
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: us-docker.pkg.dev/google-samples/containers/gke/gb-frontend:v5

將此清單儲存到 frontend.yaml 並提交到 Kubernetes 叢集將建立所定義的 ReplicaSet 及其管理的 Pod。

kubectl apply -f https://kubernetes.club.tw/examples/controllers/frontend.yaml

然後你可以獲取當前已部署的 ReplicaSet

kubectl get rs

並檢視你建立的 frontend ReplicaSet。

NAME       DESIRED   CURRENT   READY   AGE
frontend   3         3         3       6s

你還可以檢查 ReplicaSet 的狀態

kubectl describe rs/frontend

你將看到類似以下的輸出

Name:         frontend
Namespace:    default
Selector:     tier=frontend
Labels:       app=guestbook
              tier=frontend
Annotations:  <none>
Replicas:     3 current / 3 desired
Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  tier=frontend
  Containers:
   php-redis:
    Image:        us-docker.pkg.dev/google-samples/containers/gke/gb-frontend:v5
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  13s   replicaset-controller  Created pod: frontend-gbgfx
  Normal  SuccessfulCreate  13s   replicaset-controller  Created pod: frontend-rwz57
  Normal  SuccessfulCreate  13s   replicaset-controller  Created pod: frontend-wkl7w

最後,你可以檢查已啟動的 Pod

kubectl get pods

你將看到類似以下的 Pod 資訊

NAME             READY   STATUS    RESTARTS   AGE
frontend-gbgfx   1/1     Running   0          10m
frontend-rwz57   1/1     Running   0          10m
frontend-wkl7w   1/1     Running   0          10m

你還可以驗證這些 Pod 的所有者引用是否已設定為 frontend ReplicaSet。為此,獲取其中一個正在執行的 Pod 的 yaml

kubectl get pods frontend-gbgfx -o yaml

輸出將類似於此,其中 frontend ReplicaSet 的資訊設定在 metadata 的 ownerReferences 欄位中

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2024-02-28T22:30:44Z"
  generateName: frontend-
  labels:
    tier: frontend
  name: frontend-gbgfx
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: frontend
    uid: e129deca-f864-481b-bb16-b27abfd92292
...

非模板 Pod 的獲取

雖然你可以毫無問題地建立裸 Pod,但強烈建議確保裸 Pod 不具有與你的某個 ReplicaSet 的選擇器匹配的標籤。原因是 ReplicaSet 不僅限於擁有其模板指定的 Pod——它可以透過前幾節中指定的方式獲取其他 Pod。

以前面的 frontend ReplicaSet 示例和以下清單中指定的 Pod 為例

apiVersion: v1
kind: Pod
metadata:
  name: pod1
  labels:
    tier: frontend
spec:
  containers:
  - name: hello1
    image: gcr.io/google-samples/hello-app:2.0

---

apiVersion: v1
kind: Pod
metadata:
  name: pod2
  labels:
    tier: frontend
spec:
  containers:
  - name: hello2
    image: gcr.io/google-samples/hello-app:1.0

由於這些 Pod 沒有 Controller(或任何物件)作為其所有者引用,並且匹配 frontend ReplicaSet 的選擇器,它們將立即被該 ReplicaSet 獲取。

假設你在 frontend ReplicaSet 部署並設定其初始 Pod 副本以滿足其副本數要求之後建立 Pod

kubectl apply -f https://kubernetes.club.tw/examples/pods/pod-rs.yaml

新的 Pod 將被 ReplicaSet 獲取,然後立即終止,因為 ReplicaSet 將超出其所需計數。

獲取 Pod

kubectl get pods

輸出顯示新的 Pod 要麼已經終止,要麼正在終止過程中。

NAME             READY   STATUS        RESTARTS   AGE
frontend-b2zdv   1/1     Running       0          10m
frontend-vcmts   1/1     Running       0          10m
frontend-wtsmm   1/1     Running       0          10m
pod1             0/1     Terminating   0          1s
pod2             0/1     Terminating   0          1s

如果你先建立 Pod

kubectl apply -f https://kubernetes.club.tw/examples/pods/pod-rs.yaml

然後建立 ReplicaSet

kubectl apply -f https://kubernetes.club.tw/examples/controllers/frontend.yaml

你將看到 ReplicaSet 已獲取這些 Pod,並且僅根據其規範建立了新的 Pod,直到新 Pod 和原始 Pod 的數量與其所需計數匹配。例如,獲取 Pod

kubectl get pods

將在其輸出中顯示

NAME             READY   STATUS    RESTARTS   AGE
frontend-hmmj2   1/1     Running   0          9s
pod1             1/1     Running   0          36s
pod2             1/1     Running   0          36s

以這種方式,ReplicaSet 可以擁有一組非同構的 Pod。

編寫 ReplicaSet 清單

與其他所有 Kubernetes API 物件一樣,ReplicaSet 需要 apiVersionkindmetadata 欄位。對於 ReplicaSet,kind 始終是 ReplicaSet。

當控制面為 ReplicaSet 建立新的 Pod 時,ReplicaSet 的 .metadata.name 是這些 Pod 命名依據的一部分。ReplicaSet 的名稱必須是有效的 DNS 子域名 值,但這可能會導致 Pod 主機名出現意外結果。為了獲得最佳相容性,名稱應遵循更嚴格的 DNS 標籤 規則。

ReplicaSet 還需要 .spec 部分

Pod 模板

.spec.template 是一個 Pod 模板,它也需要設定標籤。在我們的 frontend.yaml 示例中,我們有一個標籤:tier: frontend。請注意不要與其他控制器的選擇器重疊,以免它們嘗試接管此 Pod。

對於模板的 重啟策略 欄位 .spec.template.spec.restartPolicy,唯一允許的值是 Always,這也是預設值。

Pod 選擇器

.spec.selector 欄位是一個 標籤選擇器。如 前文 所述,這些標籤用於識別要獲取的潛在 Pod。在我們的 frontend.yaml 示例中,選擇器是

matchLabels:
  tier: frontend

在 ReplicaSet 中,.spec.template.metadata.labels 必須與 spec.selector 匹配,否則將被 API 拒絕。

副本

你可以透過設定 .spec.replicas 來指定應同時執行的 Pod 數量。ReplicaSet 將建立/刪除其 Pod 以匹配此數量。

如果你未指定 .spec.replicas,則預設值為 1。

使用 ReplicaSet

刪除 ReplicaSet 及其 Pod

要刪除 ReplicaSet 及其所有 Pod,請使用 kubectl delete。預設情況下,垃圾收集器 會自動刪除所有依賴的 Pod。

當使用 REST API 或 client-go 庫時,你必須在 -d 選項中將 propagationPolicy 設定為 BackgroundForeground。例如

kubectl proxy --port=8080
curl -X DELETE  'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
  -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
  -H "Content-Type: application/json"

只刪除 ReplicaSet

你可以使用帶有 --cascade=orphan 選項的 kubectl delete 命令刪除 ReplicaSet,而不影響其任何 Pod。當使用 REST API 或 client-go 庫時,你必須將 propagationPolicy 設定為 Orphan。例如

kubectl proxy --port=8080
curl -X DELETE  'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
  -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
  -H "Content-Type: application/json"

一旦原始的被刪除,你可以建立一個新的 ReplicaSet 來替換它。只要新舊 .spec.selector 相同,新的 ReplicaSet 就會接管舊的 Pod。然而,它不會努力使現有 Pod 匹配新的、不同的 Pod 模板。要以受控方式將 Pod 更新為新的規範,請使用 Deployment,因為 ReplicaSet 不直接支援滾動更新。

終止 Pod

特性狀態: `Kubernetes v1.33 [alpha]`(預設啟用:false)

你可以透過在 API 伺服器kube-controller-manager 上設定 DeploymentReplicaSetTerminatingReplicas 功能門 來啟用此功能。

由於刪除或縮容而進入終止狀態的 Pod 可能需要很長時間才能終止,並且在此期間可能會消耗額外的資源。因此,所有 Pod 的總數可能會暫時超過 .spec.replicas。可以使用 ReplicaSet 的 .status.terminatingReplicas 欄位來跟蹤終止中的 Pod。

將 Pod 從 ReplicaSet 中隔離

你可以透過更改 Pod 的標籤將其從 ReplicaSet 中移除。此技術可用於將 Pod 從服務中移除,以便進行除錯、資料恢復等。以這種方式移除的 Pod 將自動被替換(假設副本數沒有改變)。

擴縮 ReplicaSet

透過簡單地更新 .spec.replicas 欄位,可以輕鬆地對 ReplicaSet 進行擴容或縮容。ReplicaSet 控制器確保所需數量的具有匹配標籤選擇器的 Pod 可用並正常執行。

在縮容時,ReplicaSet 控制器透過對可用 Pod 進行排序來選擇要刪除的 Pod,並根據以下通用演算法優先縮容 Pod:

  1. 首先縮容待定(且不可排程)的 Pod。
  2. 如果設定了 controller.kubernetes.io/pod-deletion-cost 註解,則值較低的 Pod 將優先被刪除。
  3. 具有更多副本的節點上的 Pod 優先於具有較少副本的節點上的 Pod。
  4. 如果 Pod 的建立時間不同,則建立時間較新的 Pod 優先於較舊的 Pod(建立時間按整數對數刻度分桶)。

如果上述所有條件都匹配,則隨機選擇。

Pod 刪除成本

功能狀態: Kubernetes v1.22 [beta]

使用 controller.kubernetes.io/pod-deletion-cost 註解,使用者可以設定在縮減 ReplicaSet 時優先刪除哪些 Pod。

該註解應設定在 Pod 上,範圍為 [-2147483648, 2147483647]。它表示刪除 Pod 相對於屬於同一 ReplicaSet 的其他 Pod 的成本。刪除成本較低的 Pod 優先於刪除成本較高的 Pod 被刪除。

未設定此註解的 Pod 的隱式值為 0;允許負值。API 伺服器將拒絕無效值。

此功能處於 Beta 階段,預設啟用。你可以在 kube-apiserver 和 kube-controller-manager 中使用 功能門 PodDeletionCost 停用它。

示例用例

應用程式的不同 Pod 可能具有不同的利用率水平。在縮容時,應用程式可能更傾向於移除利用率較低的 Pod。為了避免頻繁更新 Pod,應用程式應在發出縮容指令前更新一次 controller.kubernetes.io/pod-deletion-cost(將註解設定為與 Pod 利用率水平成比例的值)。如果應用程式本身控制縮容,例如 Spark 部署的驅動 Pod,這種方式就適用。

ReplicaSet 作為水平 Pod 自動擴縮器目標

ReplicaSet 也可以是 Horizontal Pod Autoscalers (HPA) 的目標。也就是說,ReplicaSet 可以由 HPA 自動擴縮。以下是一個針對我們在前面示例中建立的 ReplicaSet 的 HPA 示例。

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: frontend-scaler
spec:
  scaleTargetRef:
    kind: ReplicaSet
    name: frontend
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

將此清單儲存到 hpa-rs.yaml 並提交到 Kubernetes 叢集,應該會建立定義的 HPA,該 HPA 會根據副本 Pod 的 CPU 使用情況自動擴縮目標 ReplicaSet。

kubectl apply -f https://k8s.io/examples/controllers/hpa-rs.yaml

或者,你可以使用 kubectl autoscale 命令來完成相同的功能(而且更簡單!)

kubectl autoscale rs frontend --max=10 --min=3 --cpu-percent=50

ReplicaSet 的替代方案

Deployment 是一個可以擁有 ReplicaSet 並透過宣告式、伺服器端滾動更新來更新它們及其 Pod 的物件。雖然 ReplicaSet 可以獨立使用,但現在它們主要被 Deployment 用作編排 Pod 建立、刪除和更新的機制。當使用 Deployment 時,你無需擔心管理它們建立的 ReplicaSet。Deployment 擁有並管理它們的 ReplicaSet。因此,當你想要 ReplicaSet 時,建議使用 Deployment。

裸 Pod

與使用者直接建立 Pod 的情況不同,ReplicaSet 會替換因任何原因(例如節點故障或破壞性節點維護,如核心升級)而被刪除或終止的 Pod。因此,我們建議即使你的應用程式只需要一個 Pod,也應使用 ReplicaSet。可以將其視為一個程序主管,只是它監督的是跨多個節點的多個 Pod,而不是單個節點上的單個程序。ReplicaSet 將本地容器重啟委託給節點上的某個代理,例如 Kubelet。

作業

對於預期自行終止的 Pod(即批處理作業),請使用 Job 而不是 ReplicaSet。

DaemonSet

對於提供機器級功能的 Pod(例如機器監控或機器日誌記錄),請使用 DaemonSet 而不是 ReplicaSet。這些 Pod 的生命週期與機器的生命週期繫結:在其他 Pod 啟動之前,Pod 需要在機器上執行;當機器準備好重新啟動/關閉時,這些 Pod 可以安全地終止。

複製控制器

ReplicaSet 是 ReplicationController 的繼承者。兩者服務於相同的目的,行為相似,只是 ReplicationController 不支援 標籤使用者指南 中描述的基於集合的選擇器要求。因此,ReplicaSet 優於 ReplicationController。

下一步

上次修改時間:2025 年 6 月 11 日下午 3:37 PST: 更新 replicaset.md (eeb36c7a10)