Kubernetes 系列文章:

  1. 从 RunC 聊起
  2. Kubernetes 介绍与安装
  3. Pod
  4. Service
  5. Replicas
  6. Deployment

在介绍这一篇的内容之前,我觉得有必要复习一下前面一篇文章:Kubernetes 入门五:Replicas,然后联系第五篇中的内容,试想一下该如何实现后面介绍的几种部署升级方式。

本文要介绍的是 Deployment,故名思议,就是介绍如何将 Pod 运行在 Kubernetes 的一种技术,因为是部署,所以我想先介绍一下一些在日常使用中常见的部署(升级)方式:

常见的部署方式

蓝绿部署

蓝绿部署是一种比较常见的部署方式,经常会用于新旧版本不兼容的情况,具体的操作过程为:

  1. 先将新版本(绿版)的服务运行起来
  2. 将流量都切换到新版本的服务上
  3. 删除旧版本(蓝版)的服务

这种部署方式可能以我们现在的了解量还是比较容易实现的:

  1. 创建新版本的 ReplicaSet,部署它
  2. 将 Service 指向新的 Pods
  3. 删除旧版本的 ReplicaSet

滚动升级

蓝绿部署可以有效的保证业务的正确性,但是也带来了一些风险,例如稳定性,假设新部署的应用是有问题的,一切换之后就会导致业务崩溃了,造成一些损失,于是就有了一种稍微友好一些的升级方式,滚动升级:

  1. 先关闭旧版本的一个实例
  2. 开启新版本的一个实例用于替换旧版本
  3. 替换成功之后循环 1 和 2,直到所有的实例升级完

在这个过程中,如果中途发现异常可以即时停手,即时止损,而且 Kubernetes 也在客户端 kubectl 中支持了这个特性:kubectl rolling-update,但是有两个弊端:

金丝雀发布

金丝雀发布是滚动发布的一种特例,在滚动发布中,滚动的车轮是匍匐前进的,不会等待,如果中间出错了。但是,有的时候,我们并不想要全都升级,可能出于一些 POC 的原因,我们就希望只有部分实例是新的,大部分都是旧的,而这种情形,我们就称之为金丝雀发布:

  1. 升级少部分实例
  2. 查看效果(业务/技术效果),如果好,全部升级
  3. 如果不好,则不升级

声明式升级

前面介绍的这些方式在非 Kubernetes 上或者在 Kubernetes 上很多时候都是半手工方式执行的,而 Kubernets 作为一款 DevOPS 友好的系统,已经内置了对于部署方式的一种资源抽象,这个资源就是:Deployment,它的工作原理为:

Deployment 存在的意义为:在升级应用程序时,需要引入一个额外的 ReplicaSet,并协调新旧两个 RS,使他们再根据彼此不断修改,而不会造成干扰,而 Deployment 将这些运维过程都代码化,内置为自己的业务逻辑,从而让升级部署过程变得简单。

我这里就演示一下如何通过 Deployment 进行升级过程的操作,这里就先通过 Deployment 创建几个运行的实例:

[[email protected]]# cat 00-deployment-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rest-deployment
  labels:
    app: chap06-demo-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: chap06-demo-app
  template:
    metadata:
      labels:
        app: chap06-demo-app
    spec:
      containers:
        - name: chap06-demo-app
          image: lukelau/rest-docker:0.0.1
          ports:
            - containerPort: 8080
          args:
            - -server.addr=0.0.0.0:8080
[[email protected]]# kubectl apply -f 00-deployment-v1.yaml
[[email protected]]# kubectl get pods
NAME                               READY   STATUS    RESTARTS   AGE
rest-deployment-576676486f-jf49v   1/1     Running   0          10s
rest-deployment-576676486f-l5k95   1/1     Running   0          10s
rest-deployment-576676486f-p9vm6   1/1     Running   0          10s

然后创建第一个 v2 的版本的:

[[email protected]]# cat 01-deployment-v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rest-deployment
  labels:
    app: chap06-demo-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: chap06-demo-app
  template:
    metadata:
      labels:
        app: chap06-demo-app
    spec:
      containers:
        - name: chap06-demo-app
          image: lukelau/rest-docker:0.0.2
          ports:
            - containerPort: 8080
          args:
            - -server.addr=0.0.0.0:8080
[[email protected]]# kubectl apply -f 01-deployment-v2.yaml --record
[[email protected]]# kubectl get pods
NAME                               READY   STATUS        RESTARTS   AGE
rest-deployment-56bc74bf46-4vjz2   1/1     Running       0          10s
rest-deployment-56bc74bf46-kt8kr   1/1     Running       0          13s
rest-deployment-56bc74bf46-xk5jt   1/1     Running       0          12s
rest-deployment-576676486f-nlng8   0/1     Terminating   0          32s

可以发现旧版本的 Pods 都被关掉了,然后创建出新版本的 Pods。通过 describe deployment 我们可以看到它的升级过程:

[[email protected]]# kubectl describe deployment
... ...
  Type    Reason             Age                    From                   Message
  ----    ------             ----                   ----                   -------
  Normal  ScalingReplicaSet  5m18s                  deployment-controller  Scaled up replica set rest-deployment-576676486f to 3
  Normal  ScalingReplicaSet  4m7s                   deployment-controller  Scaled up replica set rest-deployment-56bc74bf46 to 1
  Normal  ScalingReplicaSet  4m                     deployment-controller  Scaled down replica set rest-deployment-576676486f to 2
  Normal  ScalingReplicaSet  3m58s                  deployment-controller  Scaled down replica set rest-deployment-576676486f to 1
  Normal  ScalingReplicaSet  2m44s                  deployment-controller  Scaled up replica set rest-deployment-576676486f to 1
  Normal  ScalingReplicaSet  2m43s                  deployment-controller  Scaled down replica set rest-deployment-56bc74bf46 to 2
  Normal  ScalingReplicaSet  2m23s (x2 over 4m)     deployment-controller  Scaled up replica set rest-deployment-56bc74bf46 to 2
  Normal  ScalingReplicaSet  2m21s (x2 over 3m58s)  deployment-controller  Scaled up replica set rest-deployment-56bc74bf46 to 3
  Normal  ScalingReplicaSet  2m21s (x7 over 2m43s)  deployment-controller  (combined from similar events): Scaled down replica set rest-deployment-576676486f to 1
  Normal  ScalingReplicaSet  2m18s (x2 over 3m56s)  deployment-controller  Scaled down replica set rest-deployment-576676486f to 0

同样得,还可以查看 Deployment 的执行历史,这些都是通过 --record 参数记录的:

[[email protected]]# kubectl rollout history deployment rest-deployment
deployment.extensions/rest-deployment
REVISION  CHANGE-CAUSE
3         kubectl apply --filename=00-deployment-v1.yaml --record=true
4         kubectl apply --filename=01-deployment-v2.yaml --record=true

但是,因为刚才的升级过程太快了,我们没有看清楚,所以这里我们可以尝试一下 Deployment 的控制升级速率:

[[email protected]]# cat 02-deployment-speed-limit.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rest-deployment
  labels:
    app: chap06-demo-app
spec:
  replicas: 3
  minReadySeconds: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: chap06-demo-app
  template:
    metadata:
      labels:
        app: chap06-demo-app
    spec:
      containers:
        - name: chap06-demo-app
          image: lukelau/rest-docker:0.0.3
          ports:
            - containerPort: 8080
          args:
            - -server.addr=0.0.0.0:8080
[[email protected]]# kubectl get pods
NAME                               READY   STATUS              RESTARTS   AGE
rest-deployment-546f7b5dcf-l2c97   1/1     Running             0          19s
rest-deployment-546f7b5dcf-lqklg   0/1     ContainerCreating   0          2s
rest-deployment-56bc74bf46-kt8kr   1/1     Running             0          11m
rest-deployment-56bc74bf46-xk5jt   1/1     Running             0          11m

这下情况就变得不一样的,我们可以发现升级过程慢下来了,然后可以观察到整个升级过程,甚至于我们都可以停止升级过程:

[[email protected]]# kubectl rollout pause deployment rest-deployment
deployment.extensions/rest-deployment paused

然后可以恢复升级,或者取消升级:

[[email protected]]# kubectl rollout resume deployment rest-deployment
[[email protected]]# kubectl rollout undo deployment rest-deployment

Reference