副本集
ReplicaSet 的目的是在任何给定时间维持一组稳定的副本 Pod 运行。因此,它常用于保证指定数量的相同 Pod 的可用性。
ReplicaSet 如何工作
ReplicaSet 通过以下字段定义:一个选择器(selector),用于指定如何识别它可以获取的 Pod;一个副本数(replicas),指示它应该维护多少个 Pod;以及一个 Pod 模板(pod template),用于指定它应该创建的新 Pod 的数据,以满足副本数标准。ReplicaSet 然后通过按需创建和删除 Pod 来达到所需的数量。当 ReplicaSet 需要创建新 Pod 时,它会使用其 Pod 模板。
ReplicaSet 通过 Pod 的 metadata.ownerReferences 字段与其 Pod 关联,该字段指定当前对象所属的资源。所有被 ReplicaSet 获取的 Pod 在其 ownerReferences 字段中都包含其所属 ReplicaSet 的识别信息。正是通过此链接,ReplicaSet 了解其维护的 Pod 的状态并相应地进行规划。
ReplicaSet 通过使用其选择器来识别要获取的新 Pod。如果有一个 Pod 没有 OwnerReference,或者 OwnerReference 不是一个 Controller,并且它匹配 ReplicaSet 的选择器,它将立即被该 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 的信息设置在 metadata 的 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 没有 Controller(或任何对象)作为其所有者引用,并且匹配 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 需要 apiVersion
、kind
和 metadata
字段。对于 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.selector
但不同 .spec.template.metadata.labels
和 .spec.template.spec
字段的 ReplicaSet,每个 ReplicaSet 都会忽略由另一个 ReplicaSet 创建的 Pod。副本
你可以通过设置 .spec.replicas
来指定应同时运行的 Pod 数量。ReplicaSet 将创建/删除其 Pod 以匹配此数量。
如果你未指定 .spec.replicas
,则默认值为 1。
使用 ReplicaSet
删除 ReplicaSet 及其 Pod
要删除 ReplicaSet 及其所有 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"
只删除 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 来替换它。只要新旧 .spec.selector
相同,新的 ReplicaSet 就会接管旧的 Pod。然而,它不会努力使现有 Pod 匹配新的、不同的 Pod 模板。要以受控方式将 Pod 更新为新的规范,请使用 Deployment,因为 ReplicaSet 不直接支持滚动更新。
终止 Pod
你可以通过在 API 服务器 和 kube-controller-manager 上设置 DeploymentReplicaSetTerminatingReplicas
功能门 来启用此功能。
由于删除或缩容而进入终止状态的 Pod 可能需要很长时间才能终止,并且在此期间可能会消耗额外的资源。因此,所有 Pod 的总数可能会暂时超过 .spec.replicas
。可以使用 ReplicaSet 的 .status.terminatingReplicas
字段来跟踪终止中的 Pod。
将 Pod 从 ReplicaSet 中隔离
你可以通过更改 Pod 的标签将其从 ReplicaSet 中移除。此技术可用于将 Pod 从服务中移除,以便进行调试、数据恢复等。以这种方式移除的 Pod 将自动被替换(假设副本数没有改变)。
扩缩 ReplicaSet
通过简单地更新 .spec.replicas
字段,可以轻松地对 ReplicaSet 进行扩容或缩容。ReplicaSet 控制器确保所需数量的具有匹配标签选择器的 Pod 可用并正常运行。
在缩容时,ReplicaSet 控制器通过对可用 Pod 进行排序来选择要删除的 Pod,并根据以下通用算法优先缩容 Pod:
- 首先缩容待定(且不可调度)的 Pod。
- 如果设置了
controller.kubernetes.io/pod-deletion-cost
注解,则值较低的 Pod 将优先被删除。 - 具有更多副本的节点上的 Pod 优先于具有较少副本的节点上的 Pod。
- 如果 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 删除顺序。
- 用户应避免频繁更新此注解,例如根据度量值进行更新,因为这样做会在 apiserver 上生成大量 Pod 更新。
示例用例
应用程序的不同 Pod 可能具有不同的利用率水平。在缩容时,应用程序可能更倾向于移除利用率较低的 Pod。为了避免频繁更新 Pod,应用程序应在发出缩容指令前更新一次 controller.kubernetes.io/pod-deletion-cost
(将注解设置为与 Pod 利用率水平成比例的值)。如果应用程序本身控制缩容,例如 Spark 部署的驱动 Pod,这种方式就适用。
ReplicaSet 作为水平 Pod 自动扩缩器目标
ReplicaSet 也可以是 Horizontal Pod Autoscalers (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(推荐)
Deployment
是一个可以拥有 ReplicaSet 并通过声明式、服务器端滚动更新来更新它们及其 Pod 的对象。虽然 ReplicaSet 可以独立使用,但现在它们主要被 Deployment 用作编排 Pod 创建、删除和更新的机制。当使用 Deployment 时,你无需担心管理它们创建的 ReplicaSet。Deployment 拥有并管理它们的 ReplicaSet。因此,当你想要 ReplicaSet 时,建议使用 Deployment。
裸 Pod
与用户直接创建 Pod 的情况不同,ReplicaSet 会替换因任何原因(例如节点故障或破坏性节点维护,如内核升级)而被删除或终止的 Pod。因此,我们建议即使你的应用程序只需要一个 Pod,也应使用 ReplicaSet。可以将其视为一个进程主管,只是它监督的是跨多个节点的多个 Pod,而不是单个节点上的单个进程。ReplicaSet 将本地容器重启委托给节点上的某个代理,例如 Kubelet。
作业
对于预期自行终止的 Pod(即批处理作业),请使用 Job
而不是 ReplicaSet。
DaemonSet
对于提供机器级功能的 Pod(例如机器监控或机器日志记录),请使用 DaemonSet
而不是 ReplicaSet。这些 Pod 的生命周期与机器的生命周期绑定:在其他 Pod 启动之前,Pod 需要在机器上运行;当机器准备好重新启动/关闭时,这些 Pod 可以安全地终止。
复制控制器
ReplicaSet 是 ReplicationController 的继承者。两者服务于相同的目的,行为相似,只是 ReplicationController 不支持 标签用户指南 中描述的基于集合的选择器要求。因此,ReplicaSet 优于 ReplicationController。
下一步
- 了解Pod。
- 了解 Deployment。
- 使用 Deployment 运行无状态应用程序,它依赖 ReplicaSet 来工作。
ReplicaSet
是 Kubernetes REST API 中的顶级资源。阅读 ReplicaSet 对象定义以了解 ReplicaSet 的 API。- 了解 PodDisruptionBudget 以及如何使用它来管理中断期间的应用程序可用性。