当前位置 博文首页 > RtxTitanV的博客:Kubernetes控制器之ReplicaSet
ReplicaSet用来维护一组在任何时候都处于运行状态的Pod保持稳定的副本数。因此,它通常用来保证给定数量的完全相同的Pod的可用性。现在ReplicaSet基本取代了ReplicationController,ReplicaSet支持集合式的selector,而ReplicationController仅支持等式。不过现在建议使用Deployment来自动管理ReplicaSet,这样无需担心跟其他机制的不兼容问题,并且还支持版本记录、回滚、暂停升级等高级特性。
本文主要对Kubernetes的ReplicaSet控制器进行简单总结。
本文所使用的环境如下:
ReplicaSet的目的是维护一组在任何时候都处于运行状态的Pod保持稳定的副本数。因此,它通常用来保证给定数量的完全相同的Pod的可用性。
至于RepicaSet的工作原理,首先RepicaSet是通过一组字段定义的,包括一个用来识别可获得的Pod的selector,一个用来表明应该维护的Pod副本数(replica),一个用来指定应该创建新Pod以满足副本数条件时要使用的Pod模板(template)等。每个 ReplicaSet都将根据需要创建和删除Pod以达到期望的Pod副本数。当ReplicaSet需要创建新的Pod时,会使用所提供的Pod模板。ReplicaSet通过Pod上的metadata.ownerReferences
字段连接到所属Pod,该字段指定当前对象属于哪个资源。ReplicaSet所获得的Pod都在其ownerReferences
字段中包含了属主ReplicaSet的标识信息。正是通过这一连接,ReplicaSet获知了它所维护的Pod集合的状态并据此计划其操作行为。ReplicaSet使用其selector来识别要获取的Pod。如果某个Pod没有OwnerReference或者其OwnerReference不是一个控制器,并且其匹配到某ReplicaSet的selector,则该Pod立即被此ReplicaSet获得。
虽然ReplicaSet可以确保任何时间都有指定数量的Pod副本运行并可以独立使用,但现在它主要是用作协调Deploymen创建、删除和更新Pod的一种机制。使用Deployment时,我们不必担心它们创建的ReplicaSet如何管理,Deployment会拥有并管理它们,并向Pod提供声明式的更新以及许多其他有用的功能,例如滚动更新,Deployment支持而ReplicaSet不支持。所以推荐使用Deployment而不是直接使用ReplicaSet,除非需要自定义更新业务流程或根本不需要更新。
可以通过以下命令查看ReplicaSet资源清单规则:
kubectl explain rs
kubectl explain rs.spec
kubectl explain rs.spec.template
也可以通过官方Kubernetes API手册进行查找。这里主要总结一下资源清单中重要的字段:
字段 | 类型 | 说明 |
---|---|---|
apiVersion | string | api版本定义 |
kind | string | 资源类型定义 |
metadata | object | 元数据定义 |
spec | object | ReplicaSet的Spec定义 |
spec.replicas | integer | Pod副本的期望数目,默认值为1 |
spec.selector | object | 标签选择器,定义匹配Pod的标签。ReplicaSet管理所有标签与标签选择器匹配的Pod |
spec.selector.matchLabels | object | {key,value}对的映射 |
spec.template | object | Pod模板定义,除了它是嵌套的并且没有apiVersion 和kind 之外,与Pod资源清单的语法几乎完全一致。它是spec 唯一需要的字段 |
spec.template.metadata | object | Pod的元数据定义 |
spec.template.metadata.name | string | 定义Pod的名称 |
spec.template.metadata.labels | object | 定义Pod的标签,必须与spec.selector 匹配(可以多出其他标签),否则它将被API拒绝 |
spec.template.spec | object | Pod的Spec定义 |
注意:
- 对于2个指定相同
spec.selector
但不同spec.template.metadata.labels
和spec.template.spec
字段的ReplicaSet,每个ReplicaSet将忽略另一个ReplicaSet创建的Pod。- 通常情况下不应该创建标签与此ReplicaSet标签选择器匹配的任何Pod,或者直接与另一个ReplicaSet或另一个控制器(如 Deployment)匹配的任何Pod,否则ReplicaSet会认为是它创建了这些Pod。
ReplicaSet通过其selector来识别要获取的Pod,管理所有标签与标签选择器匹配的Pod,一个简单的示意图如下:
首先准备一个镜像,创建一个名为mynginx1
的Dockerfile
文件:
FROM nginx:alpine
RUN echo 'mynginx:v1' > /usr/share/nginx/html/index.html
构建mynginx:v1
镜像:
docker build -t mynginx:v1 -f mynginx1 .
创建ReplicaSet
资源清单文件mynginx-rs.yaml
:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: mynginx-rs
labels:
app: mynginx
env: test
spec:
replicas: 3
selector:
matchLabels:
app: mynginx
env: test
template:
metadata:
labels:
app: mynginx
env: test
creator: rtxtitanv
spec:
containers:
- name: nginx
image: mynginx:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
执行以下命令创建ReplicaSet
:
kubectl apply -f mynginx-rs.yaml
查看ReplicaSet
和Pod
信息:
kubectl get rs -o wide
kubectl describe rs mynginx-rs
kubectl get pod -o wide --show-labels
验证这些Pod
的所有者引用是否为mynginx-rs
,查看其中一个Pod
的Yaml:
kubectl get pod mynginx-rs-jrv2t -o yaml
查看结果如下,这里只列出了其中一部分:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2020-08-24T02:06:33Z"
generateName: mynginx-rs-
labels:
app: mynginx
creator: rtxtitanv
env: test
...
name: mynginx-rs-jrv2t
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: mynginx-rs
uid: 066e381d-e74e-48ab-bfaa-732e308f2d0a
resourceVersion: "543754"
selfLink: /api/v1/namespaces/default/pods/mynginx-rs-jrv2t
uid: aaf22e96-38f5-4604-a4cb-119f26e67117
...
可见在元数据的ownerReferences
字段中设置了mynginx-rs
的信息。下面删除其中一个Pod
再次查看Pod
,发现又创建了一个新的Pod
以维持Pod
副本数:
在创建没有控制器管理的Pod时最好确保Pod的标签不要与ReplicaSet的标签选择器匹配,因为ReplicaSet不限于拥有其Pod模板所指定的Pod,它可以获取其他的Pod。获取非模板创建的Pod有两种情况,如果先创建了ReplicaSet再创建标签与ReplicaSet的标签选择器匹配的Pod,ReplicaSet也会获取这些标签与ReplicaSet的标签选择器匹配的Pod,但是由于ReplicaSet会维护Pod副本数稳定处于期望副本数,获取了这些非模板创建的Pod后ReplicaSet所管理的Pod超出了期望副本数,这时这些非模板创建的Pod会被ReplicaSet终止。如下图所示,灰色的两个Pod表示在ReplicaSet之后创建的标签与ReplicaSet标签选择器匹配的Pod,被ReplicaSet获取之后由于超出了期望副本数3而被终止:
下面就创建两个与ReplicaSet
的标签选择器匹配的Pod
,先创建资源清单文件mynginx-pods.yaml
:
apiVersion: v1
kind: Pod
metadata:
name: mynginx-pod1
labels:
app: mynginx
env: test
creator: rtxtitanv
spec:
containers:
- name: nginx
image: mynginx:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: mynginx-pod2
labels:
app: mynginx
env: test
creator: rtxtitanv
spec:
containers:
- name: mginx
image: mynginx:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
创建与ReplicaSet
的标签选择器匹配的Pod
并查看,发现新创建的Pod
已终止或正在终止:
如果先创建标签与ReplicaSet的标签选择器匹配的Pod再创建ReplicaSet,ReplicaSet会先获取这些Pod,如果没有达到期望的副本数,ReplicaSet会根据其Spec创建了新的Pod直到Pod副本数达到期望数目为止,不管怎样,ReplicaSet会维护Pod副本数保持在期望副本数。如下图所示,先创建了图中左边的两个Pod,然后创建ReplicaSet,先创建的两个Pod标签匹配ReplicaSet标签选择器,被ReplicaSet获取,此时Pod副本数小于期望副本数3(如果等于期望副本数,则不会再创建Pod,如果多于期望副本数,会删除多余的Pod以维持期望副本数),ReplicaSet会根据其Pod模板再创建一个Pod:
删除ReplicaSet
,然后先创建上面这两个Pod
,再创建ReplicaSet
,查看所有Pod
发现ReplicaSet
已经获取到了这两个自主创建的Pod
,并根据其Spec创建了新的Pod
直到Pod
副本数达到期望数目为止,这样ReplicaSet
就拥有了一组非同构的Pod
:
可以通过修改ReplicaSet的Pod模板来更新ReplicaSet,如下图所示,将Pod使用的镜像由mynginx:v1
改为mynginx:v2
,重载ReplicaSet可以进行更新,但由于新旧ReplicaSet的spec.selector
是相同的,新的ReplicaSet将接管老ReplicaSet的Pod,所以需要删除老ReplicaSet所创建的Pod,新的ReplicaSet就会根据其Pod模板创建新的Pod,达到期望副本数为止:
先准备一个mynginx:v2
镜像,创建一个名为mynginx2
的Dockerfile
文件:
FROM nginx:alpine
RUN echo 'mynginx:v2' > /usr/share/nginx/html/index.html
构建mynginx:v2
镜像:
docker build -t mynginx:v2 -f mynginx2 .
先删除之前创建的ReplicaSet
再重新创建,然后修改Pod模板以进行更新。如下所示,修改ReplicaSet
资源清单文件,将镜像mynginx:v1
改为mynginx:v2
:
template:
metadata:
labels:
app: mynginx
env: test
creator: rtxtitanv
spec:
containers:
- name: nginx
image: mynginx:v2
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
执行apply
命令重载ReplicaSet
,然后执行下面的命令查看Pod
所用镜像,发现没有更新,如下图所示:
kubectl get pod -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
这是因为这些Pod
是在ReplicaSet
重载之前创建的,需要手动删除ReplicaSet
管理的Pod
。下面手动删除ReplicaSet
管理的Pod
后再次查看Pod
所用镜像,发现已经更新:
通过更新spec.replicas
字段,ReplicaSet可以被轻松的进行缩放。可以通过编辑资源清单文件修改spec.replicas
字段,也可以通过kubect edit
命令修改,kubectl
还提供了一个子命令scale
用于实现应用规模的伸缩,支持从资源清单文件中获取新的目标副本数量,也可以直接在命令行通过--replicas
选项进行读取。下面先查看ReplicaSet
和Pod
:
然后执行以下命令将Pod
副本扩容为5个:
kubectl scale rs mynginx-rs --replicas=5
查看ReplicaSet
和Pod
:
执行以下命令将Pod
副本收缩至3个:
kubectl scale rs mynginx-rs --replicas=3
查看ReplicaSet
和Pod
:
ReplicaSet也可以被HPA(Horizontal Pod Autoscaler)自动缩放。例如以下HPA
资源清单:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: mynginx-scaler
spec:
scaleTargetRef:
kind: ReplicaSet
name: mynginx-rs
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 50
创建该HPA
资源对象就能根据Pod
的CPU利用率对目标ReplicaSet
自动缩放,当CPU利用率超过50%
,Pod
最多不能扩容至超过10
个,当CPU利用率低于50%
,Pod
最少不能缩减至少于3
个。也可以使用kubectl autoscale
命令完成相同的操作,更加简单:
kubectl autoscale rs mynginx-rs --max=10 --min=3 --cpu-percent=50
由于HPA需要获取CPU内存等指标,需要部署监控系统来收集这些指标,这里就不进行演示了。
可以通过改变Pod标签来从ReplicaSet中移除Pod。这种技术可以用来从服务中去除Pod,以便进行排错、数据恢复等。如果期望的副本数没有改变的话,以这种方式移除的Pod将被自动替换。如下图所示:
先查看Pod
及其标签:
执行下面的命令修改其中一个pod
的标签,然后查看Pod
及标签,发现ReplicaSet
又新建了一个标签为env=test
的pod
,如下图所示:
kubectl label pod mynginx-rs-g75mm --overwrite env=dev
因为ReplicaSet管理所有标签与标签选择器匹配的Pod,当有一个pod的标签改变,不再与标签选择器匹配,这个Pod将从ReplicaSet中移除,而ReplicaSet发现管理的pod少了一个,就会新建一个以维持期望副本数。而app=mynginx1
的pod就变为了自主pod,删除了也不会创建出一个新的Pod。下面删除修改了标签的Pod
,发现并没有再创建Pod
:
要删除ReplicaSet和它的所有Pod,使用kubectl delete
命令。默认情况下,垃圾收集器会自动删除所有依赖的Pod。执行以下命令删除ReplicaSet
和它的所有Pod
,然后查看ReplicaSet
和Pod
,发现均已经被删除,如下图所示:
kubectl delete rs mynginx-rs
如果只想删除ReplicaSet而不删除它的Pod,可以使用kubectl delete
命令并设置--cascade=false
选项。先创建ReplicaSet
,查看ReplicaSet
和Pod
:
执行以下命令只删除ReplicaSet
,然后查看ReplicaSet
和Pod
,发现ReplicaSet
被删了,Pod
没有被删除,如下图所示:
kubectl delete rs mynginx-rs --cascade=false
cs