副本集

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

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

副本集的工作原理

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

副本集通过 Pod 的 metadata.ownerReferences 字段与其 Pod 关联,该字段指定当前对象由哪个资源拥有。副本集获取的所有 Pod 的 ownerReferences 字段中都包含其拥有副本集的识别信息。正是通过这种关联,副本集才能了解其维护的 Pod 的状态并相应地进行规划。

副本集使用其选择器识别要获取的新 Pod。如果某个 Pod 没有 OwnerReference 或 OwnerReference 不是 控制器,并且它与副本集的选择器匹配,它将立即被该副本集获取。

何时使用副本集

副本集确保在任何给定时间运行指定数量的 Pod 副本。但是,Deployment 是一个更高级别的概念,它管理副本集并提供对 Pod 的声明式更新,以及许多其他有用的功能。因此,我们建议使用 Deployment 而不是直接使用副本集,除非您需要自定义更新编排或根本不需要更新。

这实际上意味着您可能永远不需要操作副本集对象:使用 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 集群将创建定义的副本集以及它管理的 Pod。

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

然后,您可以获取已部署的当前副本集

kubectl get rs

并查看您创建的 frontend 副本集

NAME       DESIRED   CURRENT   READY   AGE
frontend   3         3         3       6s

您还可以检查副本集的状态

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 副本集。为此,获取正在运行的某个 Pod 的 yaml

kubectl get pods frontend-gbgfx -o yaml

输出将类似于此,并在元数据的 ownerReferences 字段中设置 frontend 副本集的信息

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 不具有与您的某个副本集的选择器匹配的标签。原因是副本集不仅限于拥有其模板指定的 Pod - 它可以通过前面部分中指定的方式获取其他 Pod。

以之前的 frontend 副本集示例和以下清单中指定的 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 副本集的选择器匹配,因此它们将立即被它获取。

假设您在 frontend 副本集部署并设置其初始 Pod 副本以满足其副本数量要求后创建了这些 Pod

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

新 Pod 将被副本集获取,然后立即终止,因为副本集将超过其所需数量。

获取 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

然后创建副本集

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

您将看到副本集已获取 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

通过这种方式,副本集可以拥有不均匀的 Pod 集

编写副本集清单

与所有其他 Kubernetes API 对象一样,副本集需要 apiVersionkindmetadata 字段。对于副本集,kind 始终为 ReplicaSet。

当控制平面为副本集创建新的 Pod 时,副本集的 .metadata.name 是为这些 Pod 命名的一部分基础。副本集的名称必须是有效的 DNS 子域名 值,但这会对 Pod 主机名产生意外结果。为了确保最佳兼容性,名称应遵循更严格的 DNS 标签 规则。

副本集还需要一个 .spec 部分

Pod 模板

.spec.template 是一个 Pod 模板,它也需要设置标签。在我们的 frontend.yaml 示例中,我们有一个标签:tier: frontend。请小心不要与其他控制器的选择器重叠,以免它们试图采用此 Pod。

对于模板的 重启策略 字段 .spec.template.spec.restartPolicy,唯一允许的值是 Always,这是默认值。

Pod 选择器

.spec.selector 字段是一个 标签选择器。如 前面 所述,这些是用于识别要获取的潜在 Pod 的标签。在我们的 frontend.yaml 示例中,选择器为

matchLabels:
  tier: frontend

在副本集中,.spec.template.metadata.labels 必须与 spec.selector 匹配,否则 API 将拒绝它。

副本

您可以通过设置 .spec.replicas 来指定应该同时运行多少个 Pod。副本集将创建/删除其 Pod 以匹配此数量。

如果您没有指定 .spec.replicas,那么它默认为 1。

使用副本集

删除副本集及其 Pod

要删除副本集及其所有 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"

仅删除副本集

您可以使用 kubectl delete 以及 --cascade=orphan 选项删除副本集而不影响其任何 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"

删除原始副本集后,您可以创建一个新的副本集来替换它。只要旧的和新的 .spec.selector 相同,那么新的副本集将采用旧的 Pod。但是,它不会努力使现有 Pod 匹配新的、不同的 Pod 模板。要以受控方式将 Pod 更新到新规范,请使用 Deployment,因为副本集不支持直接滚动更新。

将 Pod 与副本集隔离

您可以通过更改 Pod 的标签来将 Pod 从副本集中删除。此技术可用于将 Pod 从服务中移除以进行调试、数据恢复等。以这种方式移除的 Pod 将自动替换(假设副本数量也未更改)。

扩展副本集

只需更新 ` .spec.replicas` 字段,即可轻松扩展或缩减副本集。副本集控制器确保有足够数量的匹配标签选择器的 Pod 可用并处于运行状态。

缩减时,副本集控制器会根据以下通用算法对可用 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 注释,用户可以设置首选项,以确定在缩减副本集时要先删除哪些 Pod。

该注释应设置在 Pod 上,范围为 [-2147483648, 2147483647]。它表示删除 Pod 相对于同一副本集中其他 Pod 的成本。删除成本较低的 Pod 比删除成本较高的 Pod 更优先删除。

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

此功能处于测试阶段,默认情况下已启用。您可以使用 功能门控 `PodDeletionCost` 在 kube-apiserver 和 kube-controller-manager 中禁用它。

用例示例

应用程序的不同 Pod 可能具有不同的利用率。缩减时,应用程序可能更倾向于删除利用率较低的 Pod。为了避免频繁更新 Pod,应用程序应在发出缩减操作之前更新一次 `controller.kubernetes.io/pod-deletion-cost`(将注释设置为与 Pod 利用率水平成比例的值)。如果应用程序本身控制着缩减操作,则此方法有效;例如,Spark 部署的驱动程序 Pod。

副本集作为水平 Pod 自动伸缩目标

副本集也可以作为 水平 Pod 自动伸缩器 (HPA) 的目标。也就是说,副本集可以由 HPA 自动伸缩。以下是一个针对我们在前面示例中创建的副本集的 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 使用率自动伸缩目标副本集。

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

或者,您可以使用 `kubectl autoscale` 命令来完成相同的操作(而且更容易!)。

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

副本集的替代方法

Deployment 是一个对象,它可以拥有副本集并通过声明式的服务器端滚动更新来更新它们及其 Pod。虽然副本集可以独立使用,但现在它们主要由部署作为一种机制来编排 Pod 的创建、删除和更新。当您使用部署时,您不必担心管理它们创建的副本集。部署拥有并管理它们的副本集。因此,当您想要副本集时,建议使用部署。

裸 Pod

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

Job

对于预期自行终止的 Pod(即批处理作业),请使用 Job 而不是副本集。

守护进程集

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

复制控制器

副本集是 复制控制器 的继任者。两者服务于相同的目的,行为也类似,只是复制控制器不支持在 标签用户指南 中描述的基于集合的选择器要求。因此,副本集比复制控制器更受欢迎。

下一步

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