zoukankan      html  css  js  c++  java
  • k8s之Deployment 声明式地升级应用(五)

    Deployment 声明式地升级应用
    现在你已经知道如何将应用程序组件打包进容器,将他们分组到pod中,并为它们提供临时或者持久存储,将密钥或配置文件注入,并可以使pod之间互相通信。这就是微服务化:如何将一个大规模系统拆分成各个独立运行的组件。之后,你将会需要升级你的应用程序。如何升级再kubernetes集群中运行的程序,以及kubernetes如何帮助你实现真正的零停机升级过程。升级操作可以通过使用replicationController 或者 replicaSet实现,但Kubernetes提供了另一种基于ReplicaSet的资源Deployment,并支持声明式地更新应用程序。
     
    看一个简单的例子:
    现有一个应用 ,版本为V1 ,运行在Kubernetes的pod中,现在应用的镜像有更新,标记为v2,那么如何将新版本运行的pod替换掉V1版本的pod.
    有以下两种方法更新pod:
    1. 直接删除所有现有的pod,然后创建新pod
    2. 新创建一组pod,等待运行后删除旧pod. 可以一次性创建,一次性删除,也可以一部分一部分操作。
    这两种各有优缺点:第一种方法将会导致应用程序在一定的时间内不可用。第二种方法会导致新旧版本同时在线,如果对统一个数据的处理方式不一样,将会给系统带来数据损坏。
    暂且不管优缺点,先来看看如何实现。
     
    我们用replicationController来托管pod v1,可以直接通过将pod模版修改为v2,然后再删除旧pod,这时候rc就会按照新模版创建pod.
    如果你可以接受短暂的服务不可用,那这是最简单更新pod的方法。
     
    接下来我们用第二种方法。首先要保持两个版本同时在线,所以要双倍的硬件资源。
    前提我们是用service来暴露pod的。保持rc v1不变,然后创建 rc v2 ,rc v2的pod模版选择新镜像标签V2. 创建rc v2 ,rc v2将运行pod v2 。等待pod v2运行正常后,我们使用 kubectl set selector 命令来修改service的pod选择器。
     
    这种一次性从v1切到v2的方法即为蓝绿部署,主要问题就是硬件资源要两倍。如果我们不是一次切换,而是一点点切,资源将不需要两倍。比如你创建rc v2,成功后,缩容 rc v1 ,更改service的pod选择器。然后再扩容rc v2,在缩容 v1 这就是滚动更新。那么这个就要求你的应用允许存在两个不同版本,具体要根据实际需求来操作了。
     
    以上第二种方法的两种情况,我们大概知道是怎么回事了。但我们在实际操作中,要创建两个rc,并且要修改service的pod selector. 这要是用滚动更新,pod副本越多,我们手动操作的次数就越多,手动操作越多越容易出现问题。那么问题来了,难道kubernetes没有提供自动完成这些操作的方法吗?答案是提供了。k8s中使用kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2
     
    我们来通过一个例子了解下:
    用rc创建一个应用v1 ,并使用service的 loadbalancer将服务对外暴露。

    apiVersion: v1
    kind: ReplicationController
    metadata:
        name: kubia-v1
    spec:
        replicas: 3
        template:
            metadata: 
                name: kubia
                labels:
                   app: kubia
            spec:
                containers:
                -    image: luksa/kubia:v1
                    name: nodejs
    ---
    apiVersion: v1
    kind: Service
    metadata:
        name: kubia
    spec:
        type: LoadBalancer
        selector:
            app: kubia
        ports:
        -    port: 80
            targetPort: 8080                
    查看loaderbalancer的IP
    #kubectl get svc kubia
    访问测试
    # while true; do curl http://IP; done
    this is v1 running in pod 主机名
    接下来用v2版本升级,this is v2 running in pod 主机名
    kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2
    使用 kubia v2版本应用替换运行着kubia-v1 的replicationController,将新的复制控制器命名为kubia-v2,并使用luksa/kubia:v2作为容器镜像。
    kubectl 通过复制kubia-v1 的replicationController并在其pod模版中改变镜像版本。如果仔细观察控制器的标签选择器,会阿贤它也被做了修改。它不仅包含一个简单的app=kubia标签,而且还包含一个额外的deployment标签,为了由这个ReplicationController管理,pod必须具备这个标签。这也是为了避免使用新的和旧的RC来管理同一组Pod.但是即使新创建的pod添加了deployment标签,pod中还是有app=kubia标签,所以不仅新的RC要加deployment标签,旧的RC同样也要加上deployment标签,标签的值为 一个镜像hash(先这样理解)。要保证旧的RC添加deployment标签后依然可以管理之前创建的pod,因此也要修改旧pod,进行添加标签,而实际上正是在修改旧RC之前,kubectl修改了旧pod的标签:
    kubectl get po --show-labels 进行查看标签
    设置完成后,就开始执行更新操作了,过程就是我们上面描述的滚动更新过程。
     
    为什么 kubectl rolling-update已经过时
    我们可以在使用rolling-update命令的时候添加 --v 6 来提供日志级别,使得所有kubectl 发起的到API服务器的请求都会被输出。
    你会看到一个put请求:
    /api/v1/namespace/default/replicationcontrollers/kubia-v1
    它是表示kubia-v1 ReplicationController资源的RESTful URL.这些请求减少了RC的副本数,这表明伸缩的请求是由kubectl 客户端执行的,而不是kubernetes master执行的。那么当kubectl执行升级时失去了网络连接,升级过程就会中断。对于中断后的结果处理起来将很麻烦。所以我们想尽量把这个过程让master负责。这就引出了DeployMent资源。
     
    Deployment是一个高阶资源,replicationController和replicaSet都被认为是底层的概念。当创建一个Deployment时,ReplicaSet资源就会被创建,实际的pod是由Deployment的Replicaset创建和管理的,而不是由Deployment直接创建和管理的。
     
    接下来我们创建一个简单的deployment,将上面的replicationController稍作修改:

    apiVersion: apps/v1beta1 
    kind: Deployment
    metadata:
        name: kubia
    spec:
        replicas: 3
        template:
            metadata: 
                name: kubia
                labels:
                    app: kubia
            spec:
                containers:
                -    image: luksa/kubia:v1
                name: nodejs
    因为之前RC只维护和管理了一个特定的版本的pod,并需要命名为kubia-v1,而一个deployment资源高于版本本身。可以同时管理多个版本的pod,所以在命名时不需要指定应用的版本号。
     
    kubectl create -f kubia-deployment-v1.yaml --record
    使用 --record选项会记录历史版本号,在之后操作中非常有用。
    kubectl get deployment kubia
    kubectl describe deployment kubia
    还有一个命令,专门用于查看部署状态:
    kubectl rollout status deployment kubia
     
    查看pod,会发现使用deployment资源部署的pod名称有一定的规律,
    deployment的名字 + replicaset pod模版的hash + 随机数,名称我曾怀疑是template模版的名字 + 模版的hash+随机数。经过验证发现不是,甚至可以不给模版定义name.
    如:
    kubia-15098375-otnsd
    kubia-15098375-djc6s
    而rc创建出来的名称是 : rc名称 + 随机数
    kubia-v1-kgysg
    可以通过查看replicaSet来确认pod模版hash
    kubectl get replicasets
    kubia-15098375 ...
    deployment正是根据pod模版的hash值,对给定版本的pod模版创建相同的(或使用已有的)ReplicaSet.
     
    Deployment的高明之处
    有不同的更新策略
    Recreate 策略在删除旧的Pod之后才开始创建新的Pod。如果不允许多个版本共存,使用这个,但会有短暂的不可用。
    RollingUpdate 策略会渐进地删除旧的pod,于此同时创建新的pod.这是deployment默认使用的策略。如果支持多个版本共存,推荐使用这个。
     
    我们来演示下deployment滚动升级的过程。
    在演示之前我们先减慢滚动升级的速度,以方便我们观察
    kubectl path deployment kubia -p '{"spec": {"minReadySeconds": 10} }'
    使用path对于修改单个或少量资源属性非常有用,不需要在通过编辑器编辑,使用minReadySeconds 配置正常检查后多少时间才属于正常。
    注:使用patch命令更改Deployment的自有属性,并不会导致pod的任何更新,因为pod模版并没有被修改。更改其他Deployment的属性,比如所需要的副本数或部署策略,也不会触发滚动升级,现有运行中的pod也不会受影响。
     
    触发滚动升级
    kubectl set image deployment kubia nodejs=luksa/kubia:v2
    执行完成后pod模版的镜像会被更改为luksa/kubia:v2
     
    deployment的优点
    使用控制器接管整个升级过程,不用担心网络中断。
    仅仅更改pod模版即可。
    注:如果Deployment中的pod模版引用了一个ConfigMap(或secret),那么更改ConfigMap字眼本身将不会触发升级操作。如果真的需要修改应用程序的配置并想触发更新的话,可以通过创建一个新的ConfigMap并修改pod模版引用新的ConfigMap.
    Deployment背后完成的升级过程和kubectl rolling-update命令相似。
    一个新的ReplicaSet会被创建然后慢慢扩容,同时之前版本的Replicaset会慢慢缩容至0
     
    deployment的另外一个好处是可以回滚
    kubectl rollout undo deployment kubia
    deployment会被回滚到上一个版本
    undo命令也可以在滚动升级过程中运行,并直接停止滚动升级。在升级过程中一创建的pod会被删除并被老版本的pod替代。
    显示deployment的滚动升级历史
    kubectl rollout history deployment kubia
    reversion change-cause
    2 kubectl set image deployment kubia nodejs=luksa/kubia:v2
    3 kubectl set image deployment kubia nodejs=luksa/kubia:v3
    还记得创建Deployment的时候--record 参数吗?如果不给这个参数,版本历史中的Change-cause这一栏会空。这也会使用户很难辨别每次的版本做了哪些修改。
    回滚到一个特定的Deployment版本
    kubectl rollout undo deployment kubia --to-reversion=1
    这些历史版本的replicaset都用特定的版本号保存Deployment的完整的信息,所以不应该手动删除ReplicaSet。如果删除会丢失Deployment的历史版本记录而导致无法回滚。
    ReplicaSet默认保留最近的两个版本,可以通过制定Deployment的reversionHistoryLimit属性来限制历史版本数量。apps/v1beta2 版本的Deployment默认值为10.
     
    控制滚动更新的速率
    maxSurge 最多超过期望副本数多少个pod,默认为25%,可以设置为整数
    maxUanavailable 最多允许期望副本数内多少个pod为不可用状态。
    看图比较容易理解
     
    暂停滚动更新:
    kubectl rollout pause deployment kubia
     
    恢复滚动更新:
    kubectl rollout resume deployment kubia
     
    阻止出错版本的滚动升级
    使用minReadySeconds属性指定至少要成功运行多久之后,才能将其视为可用。在pod可用之前,滚动升级的过程不会继续。是由maxSurge属性和maxUanavailable属性决定。
    例子:

    apiVersion: apps/v1beta1
    kind: Deployment
    metadata:
      name: kubia
    spec:   replicas:3   minReadySeconds: 10   strategy:     rollingUpdate:       maxSurge: 1       maxUnavilable: 0     type: RollingUpdate   template:     metadata:       name: kubia       labels:         app: kubia     spec:       containers:       - image: luksa/kubia:v3          name: nodejs       readinessProbe:         periodSeconds: 1 1秒探测一次         httpGet:           path: /           port: 8080
    kubectl apply -f kubia-deployment-v3-with-readinesscheck.yaml
    kubectl rollout status deployment kubia

    就绪探针是如何阻止出错版本的滚动升级的
    首先luksa/kubia:v3镜像应用前5个应用是正常反馈,后面会500状态返回。因此pod会从service的endpoint中移除。当执行curl时,pod已经被标记为未就绪。这就解释了为什么请求不会到新的pod上了。
    使用kubectl rollout status deployment kubia查看状体,发现只显示一个新副本启动,之后滚动升级便没有进行下去,因为新的pod一直处于不可用状态。即使变为就绪状态后,也至少需要保持10秒,才是真正可用。在这之前滚动升级过程将不再创建任何新的pod,因为当前maxUnavailable属性设置为0,所以不会删除任何原始的pod。如果没有定义minReadySeconds,一旦有一次就绪探针调用成功,便会认为新的pod已经处于可用状态。因此最好适当的设置minReadySeconds.另外就绪探针默认间隔为1秒。
    deployment资源介绍完结。

     

  • 相关阅读:
    1. 二分查找
    Filezilla使用
    正则表达式regex
    TCP的三次握手和四次挥手
    Pycharm 更换源
    寒假学习进度(十四)
    寒假学习进度(十)
    寒假学习进度(九)
    寒假学习进度(八)
    寒假学习进度(七)
  • 原文地址:https://www.cnblogs.com/zhming26/p/11719702.html
Copyright © 2011-2022 走看看