学习笔记-剖析k8s之Deployment实现-3月day17

前言

今天,主要了解一下Kubernetes里有代表性的控制器模式的完整实现:Deployment。
Deployment看似简单,实际上,它实现了Kubernetes项目中一个非常重要的功能:Pod的“水平扩缩”(horizontal scaling out/in)。这个功能,是从PaaS时代开始,一个平台级项目就必须具备的编排能力。

Deployment

如果你更新了Deployment的Pod模板(比如,修改了容器的镜像),那么Deployment就需要遵循一种叫作“滚动更新”(rolling update)的方式,来升级现有的容器。
这个能力的实现,依赖的是Kubernetes项目中的一个非常重要的概念(API对象):ReplicaSet。

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx-set
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9

从这个YAML文件中,我们可以看到,一个ReplicaSet对象,其实就是由副本数目的定义和一个Pod模板组成的。不难发现,它的定义其实是Deployment的一个子集

更重要的是,Deployment控制器实际操纵的,正是这样的ReplicaSet对象,而不是Pod对象。 同时,对于一个Deployment所管理的Pod,它的ownerReference并不是Deployment,而是ReplicaSet

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

以上正是常用的nginx-deployment。

在具体的实现上,对比Deployment,与ReplicaSet,以及Pod的关系:
在这里插入图片描述
从上图可以看到,一个定义了replicas=3的Deployment,与它的ReplicaSet,以及Pod的关系,实际上是一种“层层控制”的关系。其中,ReplicaSet负责通过“控制器模式”,保证系统中Pod的个数永远等于指定的个数(比如,3个)。这也正是Deployment只允许容器的restartPolicy=Always的主要原因:只有在容器能保证自己始终是Running状态的前提下,ReplicaSet调整Pod的个数才有意义。

在此基础上,Deployment同样通过“控制器模式”,操作ReplicaSet的个数和属性,进而实现“水平扩缩”和“滚动更新”这两个编排动作。其中,“水平扩缩”容易实现,Deployment Controller只需要修改它所控制的ReplicaSet的Pod副本个数就可以了。比如,把这个值从3改成4,那么Deployment所对应的ReplicaSet,就会根据修改后的值自动创建一个新的Pod。这就是“水平扩展”了;“水平收缩”则反之。操作的指令也非常简单:

$ kubectl scale deployment nginx-deployment --replicas=4
deployment.apps/nginx-deployment scaled

滚动更新

下面介绍Deployment如何实现滚动更新
先创建这个nginx-deployment:

$ kubectl create -f nginx-deployment.yaml --record

这里的record参数,用于记录下每次操作所执行的命令,方便后面查看。
立即检查一下nginx-deployment创建后的状态信息:

$ kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         0         0            0           1s

打印中有四个状态字段,它们的含义如下:

  1. DESIRED:用户期望的Pod副本个数(spec.replicas的值);
  2. CURRENT:当前处于Running状态的Pod的个数;
  3. UP-TO-DATE:当前处于最新版本的Pod的个数,所谓最新版本指的是Pod的Spec部分与Deployment里Pod模板里定义的完全一致
  4. AVAILABLE:当前已经可用的Pod的个数,即:既是Running状态,又是最新版本,并且已经处于Ready(健康检查正确)状态的Pod的个数。
    只有这个AVAILABLE字段,描述的才是用户所期望的最终状态

我们可以通过命令kubectl rollout status实时查看Deployment对象的状态变化:

$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.apps/nginx-deployment successfully rolled out

再等待一会儿,可以看到这个Deployment的3个Pod,就进入到了AVAILABLE状态:

NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           20s

查看一下这个Deployment所控制的ReplicaSet:

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-3167673210   3         3         3       20s

以上过程总结如下:

  • 在用户提交了一个Deployment对象后,Deployment Controller就会立即创建一个Pod副本个数为3的ReplicaSet。这个ReplicaSet的名字,是由Deployment的名字和一个随机字符串(pod-template-hash)共同组成。在以上这个例子里就是:3167673210。
  • ReplicaSet会把这个随机字符串加在它所控制的所有Pod的标签里,从而保证这些Pod不会与集群里的其他Pod混淆。
  • ReplicaSet的DESIRED、CURRENT和READY字段的含义,和Deployment中是一致的。所以,相比之下,Deployment只是在ReplicaSet的基础上,添加了UP-TO-DATE这个跟版本有关的状态字段。

这个时候,如果我们修改了Deployment的Pod模板,“滚动更新”就会被自动触发。修改Deployment有很多方法。比如,我可以直接使用kubectl edit指令编辑Etcd里的API对象:

$ kubectl edit deployment/nginx-deployment
... 
    spec:
      containers:
      - name: nginx
        image: nginx:1.9.1 # 1.7.9 -> 1.9.1
        ports:
        - containerPort: 80
...
deployment.extensions/nginx-deployment edited

kubectl edit指令,会帮你直接打开nginx-deployment的API对象。然后,你就可以修改这里的Pod模板部分了。比如,在这里,我将nginx镜像的版本升级到了1.9.1。
kubectl edit指令编辑完成后,保存退出,Kubernetes就会立刻触发“滚动更新”的过程。你还可以通过kubectl rollout status指令查看nginx-deployment的状态变化:

$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.extensions/nginx-deployment successfully rolled out

通过查看Deployment的Events,看到这个“滚动更新”的流程:

$ kubectl describe deployment nginx-deployment
...
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
...
  Normal  ScalingReplicaSet  24s   deployment-controller  Scaled up replica set nginx-deployment-1764197365 to 1
  Normal  ScalingReplicaSet  22s   deployment-controller  Scaled down replica set nginx-deployment-3167673210 to 2
  Normal  ScalingReplicaSet  22s   deployment-controller  Scaled up replica set nginx-deployment-1764197365 to 2
  Normal  ScalingReplicaSet  19s   deployment-controller  Scaled down replica set nginx-deployment-3167673210 to 1
  Normal  ScalingReplicaSet  19s   deployment-controller  Scaled up replica set nginx-deployment-1764197365 to 3
  Normal  ScalingReplicaSet  14s   deployment-controller  Scaled down replica set nginx-deployment-3167673210 to 0

可以看到:

  • 当你修改了Deployment里的Pod定义之后,Deployment Controller会使用这个修改后的Pod模板,创建一个新的ReplicaSet(hash=1764197365),这个新的ReplicaSet的初始Pod副本数是:0。
  • 在Age=24 s的位置,Deployment Controller开始将这个新的ReplicaSet所控制的Pod副本数从0个变成1个,即:“水平扩展”出一个副本。
  • 在Age=22 s的位置,Deployment Controller又将旧的ReplicaSet(hash=3167673210)所控制的旧Pod副本数减少一个,即:“水平收缩”成两个副本。
  • 如此交替进行,新ReplicaSet管理的Pod副本数,从0个变成1个,再变成2个,最后变成3个。而旧的ReplicaSet管理的Pod副本数则从3个变成2个,再变成1个,最后变成0个。这样,就完成了这一组Pod的版本升级过程。

像这样,将一个集群中正在运行的多个Pod版本,交替地逐一升级的过程,就是“滚动更新”。

在“滚动更新”过程完成之后,查看一下新、旧两个ReplicaSet的最终状态:

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1764197365   3         3         3       6s
nginx-deployment-3167673210   0         0         0       30s

“滚动更新”机制带来的好处:

  • 在升级刚开始的时候,集群里只有1个新版本的Pod。如果这时,新版本Pod有问题启动不起来,那么“滚动更新”就会停止,从而允许开发和运维人员介入。而在这个过程中,由于应用本身还有两个旧版本的Pod在线,所以服务并不会受到太大的影响。
  • 这也就要求你一定要使用Pod的Health Check机制检查应用的运行状态,而不是简单地依赖于容器的Running状态。要不然的话,虽然容器已经变成Running了,但服务很有可能尚未启动,“滚动更新”的效果也就达不到了。
  • 而为了进一步保证服务的连续性,Deployment Controller还会确保,在任何时间窗口内,只有指定比例的Pod处于离线状态。同时,它也会确保,在任何时间窗口内,只有指定比例的新Pod被创建出来。这两个比例的值都是可以配置的,默认都是DESIRED值的25%。

控制器在“滚动更新”的过程中永远都会确保至少有xxx个Pod处于可用状态,至多只有xxx个Pod同时存在于集群中。这个策略,是Deployment对象的一个字段,名叫RollingUpdateStrategy,如下所示:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
...
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1

maxSurge指定的是除了DESIRED数量之外,在一次“滚动”中,Deployment控制器还可以创建多少个新Pod;而maxUnavailable指的是,在一次“滚动”中,Deployment控制器可以删除多少个旧Pod。

再来看Deployment、ReplicaSet和Pod的关系图:
在这里插入图片描述

如上所示,Deployment的控制器,实际上控制的是ReplicaSet的数目,以及每个ReplicaSet的属性。而一个应用的版本,对应的正是一个ReplicaSet;这个版本应用的Pod数量,则由ReplicaSet通过它自己的控制器(ReplicaSet Controller)来保证。通过这样的多个ReplicaSet对象,Kubernetes项目就实现了对多个“应用版本”的描述。

基于“应用版本和ReplicaSet一一对应”的设计思想,进一步就能理解Deployment对应用进行版本控制的具体原理了。
(待补充)

此文章为3月Day17学习笔记,内容来源于极客时间《深入剖析Kubernetes》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值