ReplicaSet

ReplicaSet 的目的是维护一组在任何给定时间运行的稳定的副本 Pod。通常,您定义一个 Deployment 并让该 Deployment 自动管理 ReplicaSet。

ReplicaSet 的目的是维护一组在任何给定时间运行的稳定的副本 Pod。因此,它通常用于保证指定数量的相同 Pod 的可用性。

ReplicaSet 的工作原理

ReplicaSet 使用字段定义,包括一个选择器,指定如何标识它可以获取的 Pod,一个副本数量,指示它应该维护多少个 Pod,以及一个 Pod 模板,指定它应该创建的新 Pod 的数据以满足副本数量标准。然后,ReplicaSet 通过根据需要创建和删除 Pod 来实现其目的,以达到所需的数量。当 ReplicaSet 需要创建新的 Pod 时,它会使用其 Pod 模板。

ReplicaSet 通过 Pod 的 metadata.ownerReferences 字段链接到它的 Pod,该字段指定当前对象由哪个资源拥有。ReplicaSet 获取的所有 Pod 在其 ownerReferences 字段中都有其拥有的 ReplicaSet 的标识信息。正是通过此链接,ReplicaSet 才知道它正在维护的 Pod 的状态并相应地进行计划。

ReplicaSet 通过使用其选择器来识别要获取的新 Pod。如果存在没有 OwnerReference 或 OwnerReference 不是 控制器 并且匹配 ReplicaSet 的选择器的 Pod,它将立即被该 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.ac.cn/examples/controllers/frontend.yaml

然后,您可以获取当前部署的 ReplicaSet

kubectl get rs

并查看您创建的 frontend

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 的所有者引用是否设置为前端 ReplicaSet。为此,请获取正在运行的 Pod 之一的 yaml

kubectl get pods frontend-gbgfx -o yaml

输出将类似于此,其中前端 ReplicaSet 的信息设置在元数据的 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。

以先前的前端 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 没有控制器(或任何对象)作为它们的所有者引用,并且匹配前端 ReplicaSet 的选择器,它们将立即被其获取。

假设您在前端 ReplicaSet 部署并设置其初始 Pod 副本以满足其副本计数要求后创建 Pod

kubectl apply -f https://kubernetes.ac.cn/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.ac.cn/examples/pods/pod-rs.yaml

然后创建 ReplicaSet,但是

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

您将看到 ReplicaSet 已获取 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

你可以使用 kubectl delete 命令并加上 --cascade=orphan 选项来删除 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 被删除,你可以创建一个新的 ReplicaSet 来替换它。只要旧的和新的 .spec.selector 相同,新的 ReplicaSet 将会采用旧的 Pod。但是,它不会努力使现有的 Pod 匹配一个新的、不同的 Pod 模板。要以受控的方式将 Pod 更新到新的规范,请使用 Deployment,因为 ReplicaSet 不直接支持滚动更新。

将 Pod 从 ReplicaSet 中隔离

你可以通过更改 Pod 的标签将它们从 ReplicaSet 中移除。此技术可用于从服务中移除 Pod 以进行调试、数据恢复等。以这种方式移除的 Pod 将会被自动替换(假设副本数量也没有更改)。

扩缩 ReplicaSet

通过简单地更新 .spec.replicas 字段,可以轻松地扩容或缩减 ReplicaSet。ReplicaSet 控制器确保所需数量的具有匹配标签选择器的 Pod 可用且可操作。

在缩减时,ReplicaSet 控制器会通过对可用的 Pod 进行排序来选择要删除的 Pod,以优先基于以下通用算法缩减 Pod:

  1. 首先缩减处于 Pending(和不可调度)状态的 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 作为 Horizontal Pod Autoscaler 的目标

ReplicaSet 也可以作为 Horizontal Pod Autoscaler (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 可以独立使用,但如今它们主要被 Deployments 用作编排 Pod 创建、删除和更新的机制。当你使用 Deployments 时,你无需担心管理它们创建的 ReplicaSet。Deployments 拥有和管理它们的 ReplicaSet。因此,当你需要 ReplicaSet 时,建议使用 Deployments。

裸 Pod

与用户直接创建 Pod 的情况不同,ReplicaSet 会替换因任何原因(例如节点故障或破坏性节点维护(如内核升级))而被删除或终止的 Pod。因此,即使你的应用程序只需要一个 Pod,我们也建议你使用 ReplicaSet。将其视为类似于进程管理器,只是它管理跨多个节点的多个 Pod,而不是单个节点上的单个进程。ReplicaSet 将本地容器重启委托给节点上的某些代理,例如 Kubelet。

Job

对于预计会自行终止的 Pod(即,批处理作业),请使用 Job 而不是 ReplicaSet。

DaemonSet

对于提供机器级功能的 Pod(如机器监视或机器日志记录),请使用 DaemonSet 而不是 ReplicaSet。这些 Pod 的生命周期与机器的生命周期相关联:Pod 需要在其他 Pod 启动之前在机器上运行,并且当机器准备好重新启动/关闭时可以安全地终止。

ReplicationController

ReplicaSet 是 ReplicationController 的后继者。两者具有相同的用途,并且行为类似,只是 ReplicationController 不支持 标签用户指南 中描述的基于集合的选择器要求。因此,ReplicaSet 优先于 ReplicationController。

下一步

上次修改时间:2024 年 6 月 18 日下午 6:53 PST:为 LogarithmicScaleDown 特性门控添加文档 (76ec4489c1)