副本集
副本集的目的是在任何给定时间维护一组稳定的副本 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 对象一样,副本集需要 apiVersion
、kind
和 metadata
字段。对于副本集,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.selector
但不同的 .spec.template.metadata.labels
和 .spec.template.spec
字段的副本集,每个副本集都忽略另一个副本集创建的 Pod。副本
您可以通过设置 .spec.replicas
来指定应该同时运行多少个 Pod。副本集将创建/删除其 Pod 以匹配此数量。
如果您没有指定 .spec.replicas
,那么它默认为 1。
使用副本集
删除副本集及其 Pod
要删除副本集及其所有 Pod,请使用 kubectl delete
。默认情况下,垃圾回收器 会自动删除所有依赖 Pod。
使用 REST API 或 client-go
库时,您必须在 -d
选项中将 propagationPolicy
设置为 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"
仅删除副本集
您可以使用 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:
- 首先缩减 Pending(不可调度)的 Pod。
- 如果设置了 `controller.kubernetes.io/pod-deletion-cost` 注释,则具有较低值的 Pod 将优先显示。
- 具有更多副本的节点上的 Pod 在具有更少副本的节点上的 Pod 之前。
- 如果 Pod 的创建时间不同,则最近创建的 Pod 在较旧的 Pod 之前(创建时间以整数对数刻度分组)。
如果以上所有条件都匹配,则选择随机。
Pod 删除成本
使用 controller.kubernetes.io/pod-deletion-cost
注释,用户可以设置首选项,以确定在缩减副本集时要先删除哪些 Pod。
该注释应设置在 Pod 上,范围为 [-2147483648, 2147483647]。它表示删除 Pod 相对于同一副本集中其他 Pod 的成本。删除成本较低的 Pod 比删除成本较高的 Pod 更优先删除。
未设置此注释的 Pod 的隐式值为 0;允许使用负值。API 服务器将拒绝无效值。
此功能处于测试阶段,默认情况下已启用。您可以使用 功能门控 `PodDeletionCost` 在 kube-apiserver 和 kube-controller-manager 中禁用它。
注意
- 这是在尽力而为的基础上完成的,因此它不提供任何关于 Pod 删除顺序的保证。
- 用户应避免频繁更新注释,例如根据指标值更新注释,因为这样做会对 apiserver 生成大量 Pod 更新。
用例示例
应用程序的不同 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 启动之前在机器上运行,并且在机器准备重新启动/关闭时安全终止。
复制控制器
副本集是 复制控制器 的继任者。两者服务于相同的目的,行为也类似,只是复制控制器不支持在 标签用户指南 中描述的基于集合的选择器要求。因此,副本集比复制控制器更受欢迎。
下一步
- 了解 Pod。
- 了解 部署。
- 使用部署运行无状态应用程序,它依赖于副本集来工作。
ReplicaSet
是 Kubernetes REST API 中的顶级资源。阅读 副本集 对象定义,了解副本集的 API。- 阅读有关 PodDisruptionBudget 的内容,以及如何在中断期间使用它来管理应用程序可用性。