Kubernetes ReplicaSet

2022-05-05 13:52 更新

ReplicaSet

ReplicaSet 的目的是維護一組在任何時候都處于運行狀態(tài)的 Pod 副本的穩(wěn)定集合。 因此,它通常用來保證給定數(shù)量的、完全相同的 Pod 的可用性。

ReplicaSet 的工作原理

ReplicaSet 是通過一組字段來定義的,包括一個用來識別可獲得的 Pod 的集合的選擇算符、一個用來標(biāo)明應(yīng)該維護的副本個數(shù)的數(shù)值、一個用來指定應(yīng)該創(chuàng)建新 Pod 以滿足副本個數(shù)條件時要使用的 Pod 模板等等。 每個 ReplicaSet 都通過根據(jù)需要創(chuàng)建和 刪除 Pod 以使得副本個數(shù)達到期望值, 進而實現(xiàn)其存在價值。當(dāng) ReplicaSet 需要創(chuàng)建新的 Pod 時,會使用所提供的 Pod 模板。

ReplicaSet 通過 Pod 上的 metadata.ownerReferences 字段連接到附屬 Pod,該字段給出當(dāng)前對象的屬主資源。 ReplicaSet 所獲得的 Pod 都在其 ownerReferences 字段中包含了屬主 ReplicaSet 的標(biāo)識信息。正是通過這一連接,ReplicaSet 知道它所維護的 Pod 集合的狀態(tài), 并據(jù)此計劃其操作行為。

ReplicaSet 使用其選擇算符來辨識要獲得的 Pod 集合。如果某個 Pod 沒有 OwnerReference 或者其 OwnerReference 不是一個 控制器,且其匹配到 某 ReplicaSet 的選擇算符,則該 Pod 立即被此 ReplicaSet 獲得。

何時使用 ReplicaSet

ReplicaSet 確保任何時間都有指定數(shù)量的 Pod 副本在運行。 然而,Deployment 是一個更高級的概念,它管理 ReplicaSet,并向 Pod 提供聲明式的更新以及許多其他有用的功能。 因此,我們建議使用 Deployment 而不是直接使用 ReplicaSet,除非 你需要自定義更新業(yè)務(wù)流程或根本不需要更新。

這實際上意味著,你可能永遠(yuǎn)不需要操作 ReplicaSet 對象:而是使用 Deployment,并在 spec 部分定義你的應(yīng)用。

示例

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: gcr.io/google_samples/gb-frontend:v3

將此清單保存到 ?frontend.yaml? 中,并將其提交到 Kubernetes 集群, 應(yīng)該就能創(chuàng)建 yaml 文件所定義的 ReplicaSet 及其管理的 Pod。

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

你可以看到當(dāng)前被部署的 ReplicaSet:

kubectl get rs

并看到你所創(chuàng)建的前端:

NAME       DESIRED   CURRENT   READY   AGE
frontend   3         3         3       6s

你也可以查看 ReplicaSet 的狀態(tài):

kubectl describe rs/frontend

你會看到類似如下的輸出:

Name:		frontend
Namespace:	default
Selector:	tier=frontend
Labels:		app=guestbook
		tier=frontend
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"apps/v1","kind":"ReplicaSet","metadata":{"annotations":{},"labels":{"app":"guestbook","tier":"frontend"},"name":"frontend",...
Replicas:	3 current / 3 desired
Pods Status:	3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:       tier=frontend
  Containers:
   php-redis:
    Image:      gcr.io/google_samples/gb-frontend:v3
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:             <none>
  Volumes:              <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  117s  replicaset-controller  Created pod: frontend-wtsmm
  Normal  SuccessfulCreate  116s  replicaset-controller  Created pod: frontend-b2zdv
  Normal  SuccessfulCreate  116s  replicaset-controller  Created pod: frontend-vcmts

最后可以查看啟動了的 Pods:

kubectl get pods

你會看到類似如下的 Pod 信息:

NAME             READY   STATUS    RESTARTS   AGE
frontend-b2zdv   1/1     Running   0          6m36s
frontend-vcmts   1/1     Running   0          6m36s
frontend-wtsmm   1/1     Running   0          6m36s

你也可以查看 Pods 的屬主引用被設(shè)置為前端的 ReplicaSet。 要實現(xiàn)這點,可取回運行中的 Pods 之一的 YAML:

kubectl get pods frontend-b2zdv -o yaml

輸出將類似這樣,frontend ReplicaSet 的信息被設(shè)置在 metadata 的 ?ownerReferences ?字段中:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2020-02-12T07:06:16Z"
  generateName: frontend-
  labels:
    tier: frontend
  name: frontend-b2zdv
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: frontend
    uid: f391f6db-bb9b-4c09-ae74-6a1f77f3d5cf
...

非模板 Pod 的獲得 

盡管你完全可以直接創(chuàng)建裸的 Pods,強烈建議你確保這些裸的 Pods 并不包含可能與你 的某個 ReplicaSet 的選擇算符相匹配的標(biāo)簽。原因在于 ReplicaSet 并不僅限于擁有 在其模板中設(shè)置的 Pods,它還可以像前面小節(jié)中所描述的那樣獲得其他 Pods。

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,或其他對象)作為其屬主引用,并且 其標(biāo)簽與 frontend ReplicaSet 的選擇算符匹配,它們會立即被該 ReplicaSet 獲取。

假定你在 frontend ReplicaSet 已經(jīng)被部署之后創(chuàng)建 Pods,并且你已經(jīng)在 ReplicaSet 中設(shè)置了其初始的 Pod 副本數(shù)以滿足其副本計數(shù)需要:

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

新的 Pods 會被該 ReplicaSet 獲取,并立即被 ReplicaSet 終止,因為 它們的存在會使得 ReplicaSet 中 Pod 個數(shù)超出其期望值。

取回 Pods:

kubectl get pods

輸出顯示新的 Pods 或者已經(jīng)被終止,或者處于終止過程中:

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

如果你先行創(chuàng)建 Pods:

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

之后再創(chuàng)建 ReplicaSet:

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

你會看到 ReplicaSet 已經(jīng)獲得了該 Pods,并僅根據(jù)其規(guī)約創(chuàng)建新的 Pods,直到 新的 Pods 和原來的 Pods 的總數(shù)達到其預(yù)期個數(shù)。 這時取回 Pods:

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 中可以包含異質(zhì)的 Pods 集合。

編寫 ReplicaSet 的 spec

與所有其他 Kubernetes API 對象一樣,ReplicaSet 也需要 ?apiVersion?、?kind?、和 ?metadata ?字段。 對于 ReplicaSets 而言,其 ?kind ?始終是 ReplicaSet。

ReplicaSet 對象的名稱必須是合法的 DNS 子域名。

ReplicaSet 也需要 ?.spec? 部分。

Pod 模版

?.spec.template? 是一個Pod 模版, 要求設(shè)置標(biāo)簽。在 ?frontend.yaml? 示例中,我們指定了標(biāo)簽 ?tier: frontend?。 注意不要將標(biāo)簽與其他控制器的選擇算符重疊,否則那些控制器會嘗試收養(yǎng)此 Pod。

對于模板的重啟策略 字段,?.spec.template.spec.restartPolicy?,唯一允許的取值是 ?Always?,這也是默認(rèn)值.

Pod 選擇算符 

?.spec.selector? 字段是一個標(biāo)簽選擇算符。 如前文中所討論的,這些是用來標(biāo)識要被獲取的 Pods 的標(biāo)簽。在簽名的 ?frontend.yaml? 示例中,選擇算符為:

matchLabels:
  tier: frontend

在 ReplicaSet 中,?.spec.template.metadata.labels? 的值必須與 ?spec.selector? 值 相匹配,否則該配置會被 API 拒絕。

對于設(shè)置了相同的 ?.spec.selector?,但 ?.spec.template.metadata.labels? 和 ?.spec.template.spec? 字段不同的 兩個 ReplicaSet 而言,每個 ReplicaSet 都會忽略被另一個 ReplicaSet 所 創(chuàng)建的 Pods。

Replicas

你可以通過設(shè)置 ?.spec.replicas? 來指定要同時運行的 Pod 個數(shù)。 ReplicaSet 創(chuàng)建、刪除 Pods 以與此值匹配。

如果你沒有指定 ?.spec.replicas?, 那么默認(rèn)值為 1。

使用 ReplicaSets

刪除 ReplicaSet 和它的 Pod

要刪除 ReplicaSet 和它的所有 Pod,使用 kubectl delete 命令。 默認(rèn)情況下,垃圾收集器 自動刪除所有依賴的 Pod。

當(dāng)使用 REST API 或 ?client-go? 庫時,你必須在刪除選項中將 ?propagationPolicy ?設(shè)置為 ?Background ?或 ?Foreground?。例如:

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

你可以只刪除 ReplicaSet 而不影響它的 Pods,方法是使用 kubectl delete 命令并設(shè)置 ?--cascade=orphan? 選項。

當(dāng)使用 REST API 或 ?client-go? 庫時,你必須將 ?propagationPolicy ?設(shè)置為 ?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,就可以創(chuàng)建一個新的來替換它。 由于新舊 ReplicaSet 的 ?.spec.selector? 是相同的,新的 ReplicaSet 將接管老的 Pod。 但是,它不會努力使現(xiàn)有的 Pod 與新的、不同的 Pod 模板匹配。 若想要以可控的方式更新 Pod 的規(guī)約,可以使用 Deployment 資源,因為 ReplicaSet 并不直接支持滾動更新。

將 Pod 從 ReplicaSet 中隔離

可以通過改變標(biāo)簽來從 ReplicaSet 的目標(biāo)集中移除 Pod。 這種技術(shù)可以用來從服務(wù)中去除 Pod,以便進行排錯、數(shù)據(jù)恢復(fù)等。 以這種方式移除的 Pod 將被自動替換(假設(shè)副本的數(shù)量沒有改變)。

縮放 RepliaSet 

通過更新 ?.spec.replicas? 字段,ReplicaSet 可以被輕松的進行縮放。ReplicaSet 控制器能確保匹配標(biāo)簽選擇器的數(shù)量的 Pod 是可用的和可操作的。

在降低集合規(guī)模時,ReplicaSet 控制器通過對可用的 Pods 進行排序來優(yōu)先選擇 要被刪除的 Pods。其一般性算法如下:

  1. 首先選擇剔除懸決(Pending,且不可調(diào)度)的 Pods
  2. 如果設(shè)置了 ?controller.kubernetes.io/pod-deletion-cost? 注解,則注解值 較小的優(yōu)先被裁減掉
  3. 所處節(jié)點上副本個數(shù)較多的 Pod 優(yōu)先于所處節(jié)點上副本較少者
  4. 如果 Pod 的創(chuàng)建時間不同,最近創(chuàng)建的 Pod 優(yōu)先于早前創(chuàng)建的 Pod 被裁減。 (當(dāng) ?LogarithmicScaleDown ?這一 特性門控 被啟用時,創(chuàng)建時間是按整數(shù)冪級來分組的)。

如果以上比較結(jié)果都相同,則隨機選擇。

Pod 刪除開銷 

FEATURE STATE: Kubernetes v1.22 [beta]

通過使用 ?controller.kubernetes.io/pod-deletion-cost? 注解,用戶可以對 ReplicaSet 縮容時要先刪除哪些 Pods 設(shè)置偏好。

此注解要設(shè)置到 Pod 上,取值范圍為 [-2147483647, 2147483647]。 所代表的的是刪除同一 ReplicaSet 中其他 Pod 相比較而言的開銷。 刪除開銷較小的 Pods 比刪除開銷較高的 Pods 更容易被刪除。

Pods 如果未設(shè)置此注解,則隱含的設(shè)置值為 0。負(fù)值也是可接受的。 如果注解值非法,API 服務(wù)器會拒絕對應(yīng)的 Pod。

此功能特性處于 Beta 階段,默認(rèn)被禁用。你可以通過為 kube-apiserver 和 kube-controller-manager 設(shè)置 特性門控 ?PodDeletionCost ?來啟用此功能。

此機制實施時僅是盡力而為,并不能對 Pod 的刪除順序作出任何保證;
用戶應(yīng)避免頻繁更新注解值,例如根據(jù)某觀測度量值來更新此注解值是應(yīng)該避免的。 這樣做會在 API 服務(wù)器上產(chǎn)生大量的 Pod 更新操作。

使用場景示例

同一應(yīng)用的不同 Pods 可能其利用率是不同的。在對應(yīng)用執(zhí)行縮容操作時,可能 希望移除利用率較低的 Pods。為了避免頻繁更新 Pods,應(yīng)用應(yīng)該在執(zhí)行縮容 操作之前更新一次 ?controller.kubernetes.io/pod-deletion-cost? 注解值 (將注解值設(shè)置為一個與其 Pod 利用率對應(yīng)的值)。 如果應(yīng)用自身控制器縮容操作時(例如 Spark 部署的驅(qū)動 Pod),這種機制 是可以起作用的。

ReplicaSet 作為水平的 Pod 自動縮放器目標(biāo)

ReplicaSet 也可以作為 水平的 Pod 縮放器 (HPA) 的目標(biāo)。也就是說,ReplicaSet 可以被 HPA 自動縮放。 以下是 HPA 以我們在前一個示例中創(chuàng)建的副本集為目標(biāo)的示例。

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 集群,就能創(chuàng)建它所定義的 HPA,進而就能根據(jù)復(fù)制的 Pod 的 CPU 利用率對目標(biāo) 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 (推薦)

Deployment 是一個 可以擁有 ReplicaSet 并使用聲明式方式在服務(wù)器端完成對 Pods 滾動更新的對象。 盡管 ReplicaSet 可以獨立使用,目前它們的主要用途是提供給 Deployment 作為 編排 Pod 創(chuàng)建、刪除和更新的一種機制。當(dāng)使用 Deployment 時,你不必關(guān)心 如何管理它所創(chuàng)建的 ReplicaSet,Deployment 擁有并管理其 ReplicaSet。 因此,建議你在需要 ReplicaSet 時使用 Deployment。

裸 Pod

與用戶直接創(chuàng)建 Pod 的情況不同,ReplicaSet 會替換那些由于某些原因被刪除或被終止的 Pod,例如在節(jié)點故障或破壞性的節(jié)點維護(如內(nèi)核升級)的情況下。 因為這個原因,我們建議你使用 ReplicaSet,即使應(yīng)用程序只需要一個 Pod。 想像一下,ReplicaSet 類似于進程監(jiān)視器,只不過它在多個節(jié)點上監(jiān)視多個 Pod, 而不是在單個節(jié)點上監(jiān)視單個進程。 ReplicaSet 將本地容器重啟的任務(wù)委托給了節(jié)點上的某個代理(例如,Kubelet 或 Docker)去完成。

Job

使用Job 代替ReplicaSet, 可以用于那些期望自行終止的 Pod。

DaemonSet

對于管理那些提供主機級別功能(如主機監(jiān)控和主機日志)的容器, 就要用 ?DaemonSet ?而不用 ReplicaSet。 這些 Pod 的壽命與主機壽命有關(guān):這些 Pod 需要先于主機上的其他 Pod 運行, 并且在機器準(zhǔn)備重新啟動/關(guān)閉時安全地終止。

ReplicationController

ReplicaSet 是 ReplicationController 的后繼者。二者目的相同且行為類似,只是 ReplicationController 不支持 標(biāo)簽用戶指南 中討論的基于集合的選擇算符需求。 因此,相比于 ReplicationController,應(yīng)優(yōu)先考慮 ReplicaSet。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號