副本集

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

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

ReplicaSet 的工作原理

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

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

ReplicaSet 使用其选择器来识别要获取的新 Pod。如果某个 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 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 的信息设置在元数据的 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 没有控制器(或任何对象)作为其所有者引用并且与 frontend ReplicaSet 的选择器匹配,因此它们将立即被该 ReplicaSet 获取。

假设您在部署 frontend 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 的数量与其所需的数量匹配。获取 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 后,您可以创建新的 ReplicaSet 来替换它。只要旧的和新的 .spec.selector 相同,新的 ReplicaSet 就会采用旧的 Pod。但是,它不会尝试使现有 Pod 与新的、不同的 Pod 模板匹配。要以受控方式将 Pod 更新到新的规范,请使用 Deployment,因为 ReplicaSet 不直接支持滚动更新。

从 ReplicaSet 中隔离 Pod

您可以通过更改 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(启用 LogarithmicScaleDown 功能门控 后,创建时间将以整数对数刻度进行分组)。

如果以上所有条件都匹配,则随机选择。

Pod 删除成本

功能状态: Kubernetes v1.22 [测试版]

用户可以使用 controller.kubernetes.io/pod-deletion-cost 注解来设置在缩减 ReplicaSet 时优先删除哪些 Pod。

该注解应设置在 Pod 上,范围为 [-2147483648, 2147483647]。它表示删除 Pod 与属于同一 ReplicaSet 的其他 Pod 相比的成本。与删除成本较高的 Pod 相比,优先删除删除成本较低的 Pod。

未设置此注解的 Pod 的隐式值为 0;允许使用负值。API 服务器将拒绝无效值。

此功能处于测试阶段,默认情况下启用。您可以使用 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 可以独立使用,但如今它们主要由 Deployment 用作协调 Pod 创建、删除和更新的机制。当您使用 Deployment 时,您不必担心管理它们创建的 ReplicaSet。Deployment 拥有并管理其 ReplicaSet。因此,建议您在需要 ReplicaSet 时使用 Deployment。

裸 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 年 3 月 14 日下午 2:28 PST:添加元数据以使用 API 参考链接机制 (c889d9b251)