在第一章我们了解到,如果master节点是一个大脑,那么控制器组件就是大脑的总管,用来控制Pod的状态和行为。今天我们就来认识弹性扩缩容相关的控制器ReplicationController,ReplicaSet,Deployment。
前面我们创建的Pod都是单个的,无法满足高可用的设计要求。对于无状态的应用,一般采用多副本机制,并将多副本实例打散部署到不同的节点上,确保任何一个副本出现问题,整体服务还是可用的。
ReplicationController是多副本管理的控制器,确保在任何时候都有特定数量的 Pod 副本处于运行状态。如下图示例所示:
Pod副本数为3,一开始就启动3个Pod实例,运行过程中,其中一个Pod由于节点宕机处于不可用,RC就会新启一个Pod,确保副本数为3。同样,如果Pod副本数超过3,就是删除掉多余Pod
编辑nginx-rc.yaml文件,内容如下:
[root@k8s-master yaml]# cat nginx-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx
spec:
replicas: 3
selector:
app: nginx-rc
template:
metadata:
name: nginx
labels:
app: nginx-rc
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
该文件结构看上去比较复杂,我们来分析,如下图所示:
可以看到,ReplicationController是个"套娃"结构,共分三层。
第一层为RC自身的属性定义,主要关注replicas,selector,template三个属性。replicas设置副本数;selector选择待控制的pod标签,需要与下面Pod的标签一致;template表示Pod的模板,其内容是Pod的定义。
第二层和第三层,就是Pod和Container的定义,这个我们非常熟悉,就不一一介绍。
执行该文件,创建RC对象,可以查看下rc列表和Pod副本
[root@k8s-master yaml]# kubectl get rc
NAME DESIRED CURRENT READY AGE
nginx 3 3 3 26m
[root@k8s-master yaml]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-2l2vp 1/1 Running 0 26m
nginx-ln4hd 1/1 Running 0 26m
nginx-n5cxx 1/1 Running 0 26m
在rc列表中,期望副本数为3个,当前为3个。再看pod列表,确实启动了三个nginx的Pod,其名称都是定义的Pod的Name(name:nginx)后增加了5个随机字符,以区分不同的Pod副本。
RC控制器能将副本数控制在目标值,接下来,我们删除其中一个Pod,模拟异常测试下。
[root@k8s-master yaml]# kubectl delete pod nginx-2l2vp
pod "nginx-2l2vp" deleted
[root@k8s-master yaml]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-c4rr6 0/1 ContainerCreating 0 4s
nginx-ln4hd 1/1 Running 0 33m
nginx-n5cxx 1/1 Running 0 33m
可以看到,RC又帮我们重新启动了一个Pod,从而确保副本数为3,从rc的详情中也可以看到该过程。
[root@k8s-master yaml]# kubectl describe rc nginx
....
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 35m replication-controller Created pod: nginx-ln4hd
Normal SuccessfulCreate 35m replication-controller Created pod: nginx-n5cxx
Normal SuccessfulCreate 35m replication-controller Created pod: nginx-2l2vp
Normal SuccessfulCreate 2m8s replication-controller Created pod: nginx-c4rr6
通过调整replicas的个数,进行扩缩容,上面的例子,我们将副本数增加到4个,修改nginx-rc.yaml 文件。
[root@k8s-master yaml]# cat nginx-rc.yaml
....
spec:
replicas: 4
....
仅修改replicas值为4,其他保持一致,重新执行该文件。
[root@k8s-master yaml]# kubectl apply -f nginx-rc.yaml
replicationcontroller/nginx configured
可以看到期望副本数变成了4个。
[root@k8s-master yaml]# kubectl get rc
NAME DESIRED CURRENT READY AGE
nginx 4 4 4 6h16m
再看下pod的实际副本数,已经变成了4个。
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-c4rr6 1/1 Running 0 7h22m
nginx-ln4hd 1/1 Running 0 7h55m
nginx-n5cxx 1/1 Running 0 7h55m
nginx-q847k 1/1 Running 0 103m
大家可以自行尝试下缩容。
RC的删除有两种方式,一种采用kubectl delete rc
[root@k8s-master ~]# kubectl delete rc nginx
replicationcontroller "nginx" deleted
[root@k8s-master ~]# kubectl get rc
No resources found in default namespace.
[root@k8s-master ~]# kubectl get pod -l app=nginx-rc
No resources found in default namespace.
另一种是将为kubectl delete rc
[root@k8s-master yaml]# kubectl delete rc nginx --cascade=orphan
replicationcontroller "nginx" deleted
[root@k8s-master yaml]# kubectl get rc
No resources found in default namespace.
[root@k8s-master yaml]# kubectl get pod -l app=nginx-rc
NAME READY STATUS RESTARTS AGE
nginx-k4d2g 1/1 Running 0 3m16s
nginx-rjz9n 1/1 Running 0 3m16s
nginx-xqm66 1/1 Running 0 3m16s
nginx-znjdv 1/1 Running 0 3m16s
ReplicaSet是ReplicationController的升级版,实现的功能是一致的,K8S推荐使用ReplicaSet。ReplicaSet对于选择器selector支持更多的表达模式,包括matchLabels和matchExpressions两种。
matchLabels支持多个标签同时匹配,如下:
selector:
matchLabels:
app: busybox-prod
app: nginx-rc
matchExpressions支持更复杂的表达式,如下:
selector:
matchExpressions:
- key: app
operator: In
values:
- busybox-prod
- busybox-test
- busybox-dev
其中运算符operator支持:
In:Label的值必须与其中一个指定的values匹配。
NotIn:Label的值与任何指定的values不匹配。
Exists:pod必须包含一个指定名称的标签(值不重要)。使用此运算符时,不应指定
values字段。
DoesNotExist:pod不得包含有指定名称的标签。values属性不得指定。
下面我们用ReplicaSet来改写上面的例子的yaml文件为 nginx-rs.yaml,内容如下:
[root@k8s-master yaml]# cat nginx-rs.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx
spec:
replicas: 4
selector:
matchLabels:
app: nginx-rs
template:
metadata:
name: nginx
labels:
app: nginx-rs
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
接下来,执行指令创建rs,并查看结果
[root@k8s-master yaml]# kubectl apply -f nginx-rs.yaml
replicaset.apps/nginx created
[root@k8s-master yaml]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx 4 4 4 45s
[root@k8s-master yaml]# kubectl get pod -l app=nginx-rs
NAME READY STATUS RESTARTS AGE
nginx-ht9cg 1/1 Running 0 114s
nginx-kbc9d 1/1 Running 0 114s
nginx-lzf59 1/1 Running 0 114s
nginx-qkxv9 1/1 Running 0 114s
可以看到,创建了一个RS对象,并正确的生成了4个标签为nginx-rs的Pod副本。
其他的功能可以参考rc。
前面讲的RC,RS虽然能解决弹性扩缩容,确保既定副本数的Pod,但是在实际工程中,扩缩容仅是其中一个场景,比如在版本发布过程中如何进行灰度,版本升级,回滚等,那这些问题需要使用Deployment控制器才能解决。K8S也推荐使用Deployment,不推荐直接使用RC或者RS。
编辑ngnix-deploy.yaml文件,内容如下:
[root@k8s-master yaml]# cat nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deploy
template:
metadata:
labels:
app: nginx-deploy
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
yaml文件的结构和RC,RS基本类似。其模型为:
执行该文件创建deployment对象,并查看pod对象
[root@k8s-master yaml]# kubectl apply -f nginx-deploy.yaml
deployment.apps/nginx-deployment created
[root@k8s-master yaml]# kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 9m32s
[root@k8s-master yaml]# kubectl get pod -l app=nginx-deploy
NAME READY STATUS RESTARTS AGE
nginx-deployment-7bfc746ff9-8mxzj 1/1 Running 0 2m55s
nginx-deployment-7bfc746ff9-gpgm6 1/1 Running 0 2m55s
nginx-deployment-7bfc746ff9-w5hhw 1/1 Running 0 2m55s
可以看到,创建一个deployments对象,并按照设定的replicas,启动了3个副本pod。我们再来看下rs列表。
[root@k8s-master yaml]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-7bfc746ff9 3 3 3 9m6s
我们并没有在yaml文件中定义rs,这里为何自动生成了RS对象呢(以deployment名字为前缀)?实际上,Deployment是通过ReplicaSet操控Pod的,他们之间的关系:
接下来,我们需要将nginx的版本从1.14.2更新到1.16.1。可以使用如下指令更新
kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
或者
kubectl edit deployment/nginx-deployment
更新完成后,我们看下pod列表,已经完成了Pod实例的替换和升级。
[root@k8s-master ~]# kubectl get pod -l app=nginx-deploy
NAME READY STATUS RESTARTS AGE
nginx-deployment-588b64cc48-7tmp6 1/1 Running 0 4m31s
nginx-deployment-588b64cc48-b9s4n 1/1 Running 0 5m23s
nginx-deployment-588b64cc48-bg9m2 1/1 Running 0 4m30s
查看其中一个pod的ngnix版本号,已经升级到1.16.1版本。
[root@k8s-master ~]# kubectl describe pod nginx-deployment-588b64cc48-7tmp6
....
Containers:
nginx:
Container ID: docker://9ce7482f41624bf48cfabf1fbb866f35371a23e62818d23f21dad474bcf8c48c
Image: nginx:1.16.1
....
那么deployment是如何做到的呢?在deployment的详情事件信息记录着这一过程。
[root@k8s-master ~]# kubectl describe deployments nginx-deployment
....
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 28m deployment-controller Scaled up replica set nginx-deployment-588b64cc48 to 1
Normal ScalingReplicaSet 27m deployment-controller Scaled down replica set nginx-deployment-7bfc746ff9 to 2
Normal ScalingReplicaSet 27m deployment-controller Scaled up replica set nginx-deployment-588b64cc48 to 2
Normal ScalingReplicaSet 27m deployment-controller Scaled down replica set nginx-deployment-7bfc746ff9 to 1
Normal ScalingReplicaSet 27m deployment-controller Scaled up replica set nginx-deployment-588b64cc48 to 3
Normal ScalingReplicaSet 27m deployment-controller Scaled down replica set nginx-deployment-7bfc746ff9 to 0
一开始 产生了一个新的ReplicaSet控制器对象(nginx-deployment-588b64cc48),并将replica设置为1,即新增一个新Pod;再通过老的ReplicaSet控制器对象(nginx-deployment-7bfc746ff9),设置replica为2,即将老的Pod缩减到2。如此循环,其过程就是新Pod递增,而老Pod递减的过程。需要注意的是,老的Pod删除了,但是老的RS对象依然保留,只是控制的pod为0,保留的目的是为了版本回滚,后面详细介绍。
其过程示意图如下:
需要注意的是,新的Pod创建成功后,才会删除老的的Pod,所以在整个过程中,保证了新老Pod的可用副本数为3,确保不会影响业务的容量,而Pod副本数最多为4个。
Deployment是通过一定的算法确保整个升级过程,其中有两个参数可以调整设置。
我们图示下整个过程(副本为3,maxUnavailable和maxSurge为默认值25%,即maxUnavailable=0,maxSurge=1)。
实际工程中,经常需要进行版本灰度,Deployment目前仅支持手动灰度。下面我们将部分版本升级到1.19.3。
kubectl set image deployment/nginx-deployment nginx=nginx:1.19.3
采用如下指令kubectl get rs -w 进行监控升级pod数
[root@k8s-master yaml]# kubectl get rs -w
NAME DESIRED CURRENT READY AGE
nginx-deployment-588b64cc48 3 3 3 24m
nginx-deployment-86644697c5 1 1 1 12s
nginx-deployment-588b64cc48 2 3 3 24m
nginx-deployment-86644697c5 为新RS,nginx-deployment-588b64cc48 为老RS,当前已经升级了一个新版本pod。此时,我们手动停止升级,保持1个新版本pod,2个老版本pod进行灰度。指令如下:kubectl rollout pause deployment/
[root@k8s-master yaml]# kubectl rollout pause deployment/nginx-deployment
deployment.apps/nginx-deployment paused
当灰度完成后,手动再恢复升级,将余下的Pod也升级到新版本。指令:kubectl rollout resume deployment/
[root@k8s-master yaml]# kubectl rollout resume deployment/nginx-deployment
deployment.apps/nginx-deployment resumed
监控升级过程,完成剩余2个pod的升级。
[root@k8s-master yaml]# kubectl get rs -w
NAME DESIRED CURRENT READY AGE
nginx-deployment-86644697c5 1 1 1 32s
nginx-deployment-588b64cc48 2 3 3 24m
nginx-deployment-86644697c5 2 1 1 32s
nginx-deployment-588b64cc48 2 3 3 24m
nginx-deployment-588b64cc48 2 2 2 24m
nginx-deployment-86644697c5 2 1 1 33s
nginx-deployment-86644697c5 2 2 1 33s
nginx-deployment-86644697c5 2 2 2 34s
nginx-deployment-588b64cc48 1 2 2 24m
nginx-deployment-86644697c5 3 2 2 34s
nginx-deployment-588b64cc48 1 2 2 24m
nginx-deployment-588b64cc48 1 1 1 24m
nginx-deployment-86644697c5 3 2 2 34s
nginx-deployment-86644697c5 3 3 2 34s
nginx-deployment-86644697c5 3 3 3 35s
nginx-deployment-588b64cc48 0 1 1 24m
nginx-deployment-588b64cc48 0 1 1 24m
nginx-deployment-588b64cc48 0 0 0 24m
升级过程前面已经介绍,可以对照实例列表自行分析下。这种手动灰度升级,缺点很明显,没办法精确控制灰度Pod的个数。实际生产中,一般采用多个deployment分别控制不同的版本升级。
当升级失败后,需要回滚到某个正常版本。下面我们演示回滚的过程,首先升级到一个不存在的版本(1.91),模拟升级失败,然后再回滚到正常版本。
升级一个失败版本。
kubectl set image deployment/nginx-deployment nginx=nginx:1.91
查看下rs列表
[root@k8s-master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-588b64cc48 0 0 0 21h
nginx-deployment-76c486cd68 1 1 0 3m18s
nginx-deployment-86644697c5 3 3 3 21h
创建了新的RS(nginx-deployment-76c486cd68)进行升级,但是一直停留在1个副本。
继续看下pod状态
[root@k8s-master ~]# kubectl get pod -l app=nginx-deploy
NAME READY STATUS RESTARTS AGE
nginx-deployment-76c486cd68-p2rxc 0/1 ImagePullBackOff 0 6m14s
nginx-deployment-86644697c5-g6fzd 1/1 Running 0 21h
nginx-deployment-86644697c5-g8m5c 1/1 Running 0 21h
nginx-deployment-86644697c5-v9vcs 1/1 Running 0 21h
新创建的pod(nginx-deployment-76c486cd68-p2rxc),拉取镜像失败,导致升级卡住了。
接下来,需要进行版本回滚,首先查看下有哪些历史版本可以回滚
[root@k8s-master ~]# kubectl rollout history deployment/nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1
2
3
4
包括当前版本,一共有4个版本,我们继续查看下版本的详细信息,指令如下:kubectl rollout history deployment/
[root@k8s-master ~]# kubectl rollout history deployment/nginx-deployment --revision=3
deployment.apps/nginx-deployment with revision #3
Pod Template:
Labels: app=nginx-deploy
pod-template-hash=86644697c5
Containers:
nginx:
Image: nginx:1.19.3
Port: 80/TCP
Host Port: 0/TCP
Environment:
Mounts:
Volumes:
可以看到,3版本对应的就是1.19.1。 我们需要回滚到这个版本,可以使用
kubectl rollout undo deployment/nginx-deployment默认回滚到上一个版本,
也可以使用kubectl rollout undo deployment/nginx-deployment --to-revision=3回滚到指定版本。
[root@k8s-master ~]# kubectl rollout undo deployment/nginx-deployment --to-revision=3
deployment.apps/nginx-deployment rolled back
再看下rs列表
[root@k8s-master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-588b64cc48 0 0 0 21h
nginx-deployment-76c486cd68 0 0 0 9m45s
nginx-deployment-86644697c5 3 3 3 21h
恢复到1.19.3版本的RS上(升级后,还需要保留老RS对象,目的就在于此)。继续看下pod列表
[root@k8s-master ~]# kubectl get pod -l app=nginx-deploy
NAME READY STATUS RESTARTS AGE
nginx-deployment-86644697c5-g6fzd 1/1 Running 0 21h
nginx-deployment-86644697c5-g8m5c 1/1 Running 0 21h
nginx-deployment-86644697c5-v9vcs 1/1 Running 0 21h
刚才新创建的错误的pod已经删除了,回滚成功。
Deployment是通过RS对象控制Pod,所以天然继承了RS的扩缩容的能力。这里我们使用更简便的指令方式实现扩缩容,指令如下:kubectl scale deploy
[root@k8s-master ~]# kubectl scale deploy nginx-deployment --replicas=4
deployment.apps/nginx-deployment scaled
再查看下Pod的列表
[root@k8s-master ~]# kubectl get pod -l app=nginx-deploy
NAME READY STATUS RESTARTS AGE
nginx-deployment-86644697c5-96q8l 1/1 Running 0 7s
nginx-deployment-86644697c5-g6fzd 1/1 Running 0 37h
nginx-deployment-86644697c5-g8m5c 1/1 Running 0 37h
nginx-deployment-86644697c5-v9vcs 1/1 Running 0 37h
正确地扩容到4个Pod副本。
本章节介绍了弹性扩缩容相关的控制器RC,RS以及Deployment。
RS与RC的功能一致,推荐使用RS,通过设置副本数,控制目标Pod实例的个数,在异常情况,自动增删Pod实例,确保副本数与目标值一致。
Deployment是更高阶的控制器,通过RS来控制Pod,除了基本的扩缩容功能,还实现了版本升级,灰度,回滚等功能。
K8S初级入门系列之一-概述
K8S初级入门系列之二-集群搭建
K8S初级入门系列之三-Pod的基本概念和操作
K8S初级入门系列之四-Namespace/ConfigMap/Secret
K8S初级入门系列之五-Pod的高级特性
K8S初级入门系列之六-控制器(RC/RS/Deployment)
K8S初级入门系列之七-控制器(Job/CronJob/Daemonset)
K8S初级入门系列之八-网络
K8S初级入门系列之九-共享存储
K8S初级入门系列之十-控制器(StatefulSet)
K8S初级入门系列之十一-安全
K8S初级入门系列之十二-计算资源管理