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 不是 控制器 并且匹配 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
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 的所有者引用是否设置为前端 ReplicaSet。为此,请获取正在运行的 Pod 之一的 yaml
kubectl get pods frontend-gbgfx -o yaml
输出将类似于此,其中前端 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。
以先前的前端 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 没有控制器(或任何对象)作为它们的所有者引用,并且匹配前端 ReplicaSet 的选择器,它们将立即被其获取。
假设您在前端 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
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
字段的 2 个 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
你可以使用 kubectl delete
命令并加上 --cascade=orphan
选项来删除 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 不直接支持滚动更新。
将 Pod 从 ReplicaSet 中隔离
你可以通过更改 Pod 的标签将它们从 ReplicaSet 中移除。此技术可用于从服务中移除 Pod 以进行调试、数据恢复等。以这种方式移除的 Pod 将会被自动替换(假设副本数量也没有更改)。
扩缩 ReplicaSet
通过简单地更新 .spec.replicas
字段,可以轻松地扩容或缩减 ReplicaSet。ReplicaSet 控制器确保所需数量的具有匹配标签选择器的 Pod 可用且可操作。
在缩减时,ReplicaSet 控制器会通过对可用的 Pod 进行排序来选择要删除的 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]。它表示删除一个 Pod 相对于属于同一 ReplicaSet 的其他 Pod 的成本。具有较低删除成本的 Pod 优先于具有较高删除成本的 Pod 被删除。
对于未设置此注解的 Pod,此注解的隐式值为 0;允许负值。API 服务器将拒绝无效值。
此功能为 Beta 版,默认启用。你可以使用 kube-apiserver 和 kube-controller-manager 中的 特性门控 PodDeletionCost
禁用它。
注意
- 这是尽力而为的执行,因此它不提供关于 Pod 删除顺序的任何保证。
- 用户应避免频繁更新注解,例如基于指标值更新注解,因为这样做会在 API 服务器上生成大量 Pod 更新。
用例示例
应用程序的不同 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 (推荐)
Deployment
是一个可以拥有 ReplicaSet 并通过声明式的服务器端滚动更新来更新它们及其 Pod 的对象。虽然 ReplicaSet 可以独立使用,但如今它们主要被 Deployments 用作编排 Pod 创建、删除和更新的机制。当你使用 Deployments 时,你无需担心管理它们创建的 ReplicaSet。Deployments 拥有和管理它们的 ReplicaSet。因此,当你需要 ReplicaSet 时,建议使用 Deployments。
裸 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 对象定义,以了解 ReplicaSet 的 API。- 阅读关于 PodDisruptionBudget 以及如何使用它来管理中断期间的应用程序可用性。