介绍
ReplicaSet的目的是维护一组在任何时候都处于运行状态的Pod副本的稳定集合。因此它通常用来保证给定数量的、完全相同的Pod的可用性。
工作原理
ReplicaSet 是通过一组字段来定义的,包括一个用来识别可获得的 Pod 的集合的选择算符(label标签)、一个用来标明应该维护的副本个数的数值、一个用来指定应该创建新 Pod 以满足副本个数条件时要使用的 Pod 模板等等。 每个 ReplicaSet 都通过根据需要创建和 删除 Pod 以使得副本个数达到期望值, 进而实现其存在价值。当 ReplicaSet 需要创建新的 Pod 时,会使用所提供的 Pod 模板。
ReplicaSet 通过 Pod 上的 metadata.ownerReferences字段连接到附属 Pod,该字段给出当前对象的属主资源。 ReplicaSet 所获得的 Pod 都在其 ownerReferences 字段中包含了属主 ReplicaSet 的标识信息。正是通过这一连接,ReplicaSet 知道它所维护的 Pod 集合的状态, 并据此计划其操作行为。
ReplicaSet 使用其选择算符来辨识要获得的 Pod 集合。如果某个 Pod 没有 OwnerReference 或者其 OwnerReference 不是一个 控制器,且其匹配到 某 ReplicaSet 的选择算符,则该 Pod 立即被此 ReplicaSet 获得。
何时使用ReplicaSet
ReplicaSet确保任何时间都有指定数量的Pod副本在运行。然后,Deployment是一个更高级的概念,它管理ReplicaSet,并向Pod提供声明式的更新以及许多其他有用的功能。
这实际上意味着,可能永远不需要操作ReplicaSet对象,而是使用Deployment,并在spec部分定义你的应用。
示例
# frontend.yaml
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: nginx
image: nginx:1.17.1
将上面的yaml提交到Kubernetes集群,就能创建yaml文件中所定义的ReplicaSet及其管理的Pod。
kubectl apply -f frontend.yaml
然后可以使用kubectl get命令来查看当前被部署的ReplicaSet:
kubectl get rs
可以看到如下信息:
NAME DESIRED CURRENT READY AGE
frontend 3 3 1 10s
当然也可以使用kubectl describe命令来查看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: 2 Running / 1 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: tier=frontend
Containers:
nginx:
Image: nginx:1.17.1
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 3m59s replicaset-controller Created pod: frontend-k4j9z
Normal SuccessfulCreate 3m58s replicaset-controller Created pod: frontend-czcs8
Normal SuccessfulCreate 3m58s replicaset-controller Created pod: frontend-wstq8
我们也可以查看Pods的属主应用被设置为前端的ReplicaSet。我们查看运行中Pods之一的YAML:
kubectl get pod frontend-wstq8 -o yaml
输出类似于,frontend ReplicaSet的信息被设置在matedata的ownerReferences字段中:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2022-06-20T05:22:12Z"
generateName: frontend-
labels:
tier: frontend
name: frontend-wstq8
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: frontend
uid: dcfeb006-8839-467b-8c3c-a989063b4b6f
resourceVersion: "72950983"
uid: 0e75acf2-a319-4c7c-8de4-66ca9e8955f0
......
非模版Pod的获得
尽管完全可以直接创建裸Pod,但是强烈建议确保这些裸的Pod并不包含可能与某个ReolicaSet的选择算符相匹配的标签。
原因在于ReplicaSet并不仅限于拥有在其模版中设置的Pod,它还可以通过标签关联其他的Pod。
# pod-rs.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod1
labels:
tier: frontend
spec:
containers:
- name: hello1
image: nginx:1.17.1
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
labels:
tier: frontend
spec:
containers:
- name: hello2
image: nginx:1.17.1
由于这些Pod没有控制器作为其属主引用,并且其标签于frontend ReplicaSet的选择算符匹配,他们会立即被该ReplicaSet获取。
假定在 frontend ReplicaSet 已经被部署之后创建 Pod,并且已经在 ReplicaSet 中设置了其初始的 Pod 副本数以满足其副本计数需要,新的Pod会被该ReplicaSet获取,并立即被ReplicaSet终止,因为他们的存在会使得ReplicaSet中Pod个数超出其期望值。
当我们获取Pods后,会发现输出显示新的Pod已经被终止,或者处于终止过程中:
NAME READY STATUS RESTARTS AGE
frontend-4ngcq 1/1 Running 0 93s
frontend-clwh2 1/1 Running 0 93s
frontend-grvjm 1/1 Running 0 93s
pod1 0/1 Terminating 0 4s
pod2 0/1 Terminating 0 4s
如果先行创建Pods:
kubectl apply -f rs-pod.yaml
之后再创建ReplicaSet:
kubectl apply -f frontend.yaml
这时候会看到ReplicaSet已经获得了该Pod,并仅根据其规约创建新的Pod,直到新的Pod和原来的Pod的总数达到其预期的个数,这时候取回Pod列表:
kubectl get pods
将会看到如下输出:
NAME READY STATUS RESTARTS AGE
frontend-5578w 1/1 Running 0 13s
pod1 1/1 Running 0 6m6s
pod2 1/1 Running 0 6m6s
采用这种方式,一个ReplicaSet中可以包含异质的Pod集合。
编写ReplicaSet清单
与所有其他Kubernetes API对象一样,ReplicaSet也需要apiVersion、kind、metadata字段。对于ReplicaSets而言,其kind始终都是ReplicaSet。
ReplicaSet对象的名称必须是合法的DNS子域名(1、最多63个字符;2、只能包含小写字母、数字、以及“-”;3、必须以字母数字开头;4、必须以字母数字结尾)
ReplicaSet也需要.spec部分。
Pod模版
.spec.template是一个Pod模板,要求设置标签。在frontend.yaml示例中,我们指定了标签tier:frontend和app: guestbook。注意不要将标签与其他控制器的选择算符重叠,否则那些控制器也会尝试收养此Pod。
对于模版的重启策略字段,.spec.template.spce.restartPolicy,唯一允许的取值是Always,这也是默认值。
Pod选择算符
.spec.selector字段是一个标签选择算符,是用来标识要被获取的Pods的标签,在frontend.yaml示例中,选择算符为:
matchLabels:
tier: frontend
在ReplicaSet中,.spec.template.metadata.labels的值必须与spec.selector值相匹配,否则该配置会被API拒绝。
说明
对于设置了相同的
.spec.selector,但.spec.template.metadata.labels和.spec.template.spce字段不同的两个ReplicasSet而言,每个ReplicaSet都会忽略被另一个ReplicaSet所创建的Pods。
Replicas
可以通过设置.spce.replicas来指定要同时运行的Pod个数。ReplicaSet创建、删除以此值匹配。
如果没有指定.spce.replicas,那么默认值为1。
使用ReplicaSets
删除ReplicaSet和它的Pods
要删除ReplicaSet和它所管理的所有Pod,直接使用kubectl delete命令。
默认情况下,垃圾收集器会自动删除所有依赖的Pod。
当使用REST API 或者client-go库时,必须在-d选项中将propagationPolicy设置为Backgroup或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
可以只删除ReplicaSet而不影响它的Pods,方法是使用kubectl delete命令并设置--cascade=orphan选项。
当使用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的.spce.selector是相同的,新的ReplicaSet将接管老的Pod。但是它不会努力使现有的Pod与新的、不同的Pod模版匹配。若想以可控的方式更新Pod的规约,可以使用Deployment资源,因为ReplicaSet并不知节支持滚动更新。
将Pod从ReplicaSet中隔离
可以通过改变标签来从ReplicaSet中移除Pod。这种技术可以用来从服务中去除Pod,以便进行排错,数据恢复等。以这种方式移除的Pod将被自动替换(假设副本数量没有改变)。
缩放RepliaSet
通过更新.spec.replicas字段,ReplicaSet可以轻松的进行缩放。ReplicaSet控制器能确保匹配标签选择器的数量的Pod时可用的和可操作的。
在降低集合规模时,ReplicaSet控制器通过对可用的Pods进行排序来优化选择要被删除的Pods。其一般性算法如下:
- 首先选择剔除Pending,且不可调度的Pods
- 如果设置了
controller.kubernetes.io/pod-deletion-cost注解,则注解值较小的优先被裁减掉 - 所处节点上副本个数较多的Pod优先于所处节点上副本较少者
- 如果Pod的创建时间不同,最近创建的Pod优先于早前创建的Pod被裁减
如果以上比较结果相同,则随机选择。
Pod删除开销
特性状态 :Kubernetes v1.22 [beta]
通过使用controller.kubernetes.io/pod-deletion-cost注解,用户可以对ReplicaSet缩容时要先删除那些Pods设置偏好。
此注解要设置到 Pod 上,取值范围为 [-2147483647, 2147483647]。 所代表的的是删除同一 ReplicaSet 中其他 Pod 相比较而言的开销。 删除开销较小的 Pods 比删除开销较高的 Pods 更容易被删除。
Pods 如果未设置此注解,则隐含的设置值为 0。负值也是可接受的。 如果注解值非法,API 服务器会拒绝对应的 Pod。
此功能特性处于 Beta 阶段,默认被禁用。可以通过为 kube-apiserver 和 kube-controller-manager 设置特性门控 PodDeletionCost 来启用此功能。
说明:
1、此机制实施时仅是尽力而为,并不能对Pod的删除顺序作出任何保证;
2、用户应避免频繁更新注解值,例如根据某观测度量值来更新此注解值是应该避免的。这样做会在API服务器上产生大量的Pod更新操作。
使用场景
同一应用的不同 Pods 可能其利用率是不同的。在对应用执行缩容操作时,可能 希望移除利用率较低的 Pods。为了避免频繁更新 Pods,应用应该在执行缩容 操作之前更新一次 controller.kubernetes.io/pod-deletion-cost 注解值 (将注解值设置为一个与其 Pod 利用率对应的值)。 如果应用自身控制器缩容操作时(例如 Spark 部署的驱动 Pod),这种机制 是可以起作用的。
ReplicaSet作为水平的Pod自动缩放器目标
ReplicaSet 也可以作为水平的 Pod 缩放器 (HPA)的目标。也就是说,ReplicaSet 可以被 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,进而就能根据复制的 Pod 的 CPU 利用率对目标 ReplicaSet进行自动缩放。
kubectl apply -f hpa-rs.yaml
或者可以使用kubectl autoscale命令完成相同的操作:
kubectl autoscale rs frontend --max=10 --min=3 --cpu-percent=50