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 不是 控制器(Controller) 且与 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 的信息设置在 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),但强烈建议确保这些裸 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 数量将超过其期望的数量。
获取 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,并且只按照其 spec 创建了新的 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 拒绝。
注意
对于 2 个 ReplicaSet,如果它们指定了相同的.spec.selector
但 .spec.template.metadata.labels
和 .spec.template.spec
字段不同,那么每个 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 被删除后,你可以创建一个新的 ReplicaSet 来替换它。只要旧的 ReplicaSet 和新的 ReplicaSet 的 .spec.selector
相同,新的 ReplicaSet 就会收养旧的 Pod。但是,它不会尝试使现有 Pod 与新的、不同的 Pod 模板匹配。要以受控的方式将 Pod 更新到新的 spec,请使用 Deployment,因为 ReplicaSet 不直接支持滚动更新。
终止 Pod
Kubernetes v1.33 [alpha]
(默认禁用: false)你可以通过在 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:
- Pending(待定)状态(且不可调度)的 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]。它表示与其他属于同一 ReplicaSet 的 Pod 相比,删除该 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 部署的 driver 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,它会根据复制的 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 并通过声明式、服务器端滚动更新来更新 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。
下一步
- 了解 Pod。
- 了解 Deployment。
- 使用 Deployment 运行无状态应用,它依赖于 ReplicaSet 来工作。
ReplicaSet
是 Kubernetes REST API 中的顶级资源。阅读 ReplicaSet 对象定义,了解副本集的 API。- 阅读关于 PodDisruptionBudget 的内容,以及如何使用它来管理中断期间的应用可用性。