K8S初级入门系列之六-控制器(RC/RS/Deployment)

一、前言

     在第一章我们了解到,如果master节点是一个大脑,那么控制器组件就是大脑的总管,用来控制Pod的状态和行为。今天我们就来认识弹性扩缩容相关的控制器ReplicationController,ReplicaSet,Deployment。

二、ReplicationController(RC)

      前面我们创建的Pod都是单个的,无法满足高可用的设计要求。对于无状态的应用,一般采用多副本机制,并将多副本实例打散部署到不同的节点上,确保任何一个副本出现问题,整体服务还是可用的。

       ReplicationController是多副本管理的控制器,确保在任何时候都有特定数量的 Pod 副本处于运行状态。如下图示例所示:

K8S初级入门系列之六-控制器(RC/RS/Deployment)_第1张图片

    Pod副本数为3,一开始就启动3个Pod实例,运行过程中,其中一个Pod由于节点宕机处于不可用,RC就会新启一个Pod,确保副本数为3。同样,如果Pod副本数超过3,就是删除掉多余Pod

 1、RC创建

编辑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

   该文件结构看上去比较复杂,我们来分析,如下图所示:

K8S初级入门系列之六-控制器(RC/RS/Deployment)_第2张图片

 可以看到,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

2、扩缩容

    通过调整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

大家可以自行尝试下缩容。 

3、RC删除

     RC的删除有两种方式,一种采用kubectl delete rc 指令是将rc以及pod都删除。如下所示:

[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  --cascade=orphan指令,仅删除rc,而保留pod。

[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

    ReplicaSet是ReplicationController的升级版,实现的功能是一致的,K8S推荐使用ReplicaSet。ReplicaSet对于选择器selector支持更多的表达模式,包括matchLabels和matchExpressions两种。

  • matchLabels

matchLabels支持多个标签同时匹配,如下:

 selector:
    matchLabels:
      app: busybox-prod
      app: nginx-rc
  • matchExpressions

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。

四、Deployment

     前面讲的RC,RS虽然能解决弹性扩缩容,确保既定副本数的Pod,但是在实际工程中,扩缩容仅是其中一个场景,比如在版本发布过程中如何进行灰度,版本升级,回滚等,那这些问题需要使用Deployment控制器才能解决。K8S也推荐使用Deployment,不推荐直接使用RC或者RS。

1、创建Deployment

编辑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基本类似。其模型为:

K8S初级入门系列之六-控制器(RC/RS/Deployment)_第3张图片

 执行该文件创建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的,他们之间的关系:

K8S初级入门系列之六-控制器(RC/RS/Deployment)_第4张图片

 2、升级

     接下来,我们需要将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,保留的目的是为了版本回滚,后面详细介绍。

 其过程示意图如下:

K8S初级入门系列之六-控制器(RC/RS/Deployment)_第5张图片

    需要注意的是,新的Pod创建成功后,才会删除老的的Pod,所以在整个过程中,保证了新老Pod的可用副本数为3,确保不会影响业务的容量,而Pod副本数最多为4个。

     Deployment是通过一定的算法确保整个升级过程,其中有两个参数可以调整设置。

  • spec.strategy.rollingUpdate.maxUnavailable,最大不可用,用于指定在Deployment在更新的过程中不可用状态的Pod数据上限,可以是绝对值,也可以是百分比(基准为副本期望值),不过这个值要反过来算,如1.6版本后,该值默认为25%,那么可用Pod就为75%(向上取整)。比如副本数为3,3*75%=2.25,取整后为3,也就是升级期间,新老Pod的可用实例数至少有3个。
  • spec.strategy.rollingUpdate.maxSurge,最大峰值,用于指定在Deployment更新Pod的过程中
    Pod总数超过Pod期望副本数部分的最大值。可以是绝对值,也可以是百分比(基准为期望值,向上取整),1.6版本后,该值默认为25%,比如副本数为3,3*25%=0.75,取整后为1,也就是升级期间,新老Pod的总数不能超4(3+1)。

我们图示下整个过程(副本为3,maxUnavailable和maxSurge为默认值25%,即maxUnavailable=0,maxSurge=1)。 

K8S初级入门系列之六-控制器(RC/RS/Deployment)_第6张图片

3、灰度

    实际工程中,经常需要进行版本灰度,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分别控制不同的版本升级

4、回滚

     当升级失败后,需要回滚到某个正常版本。下面我们演示回滚的过程,首先升级到一个不存在的版本(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/ --revision=

[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已经删除了,回滚成功。

5、扩缩容

        Deployment是通过RS对象控制Pod,所以天然继承了RS的扩缩容的能力。这里我们使用更简便的指令方式实现扩缩容,指令如下:kubectl scale deploy --replicas=

[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初级入门系列之十二-计算资源管理

你可能感兴趣的:(K8S,kubernetes,RC,ReplicaSet,Deployment)