1 滚动更新的定义和目标

滚动更新的含义

一次只更新一小部分副本,成功后,再更新更多的副本,最终完成所有副本的更新。

滚动更新的好处

最大好处是零停机,整个更新过程始终有副本在运行,从而保证了业务的连续性。

根据 yaml 创建资源, apply 可以重复执行,create 不行

kubectl create -f deploy.yml
kubectl apply -f deploy.yml --record

注意 当deploy.yml 是上次发布的内容没有更改时,使用kubectl apply无法进行更新发布。比如镜像使用的是latest标签时。

我们在发布deployment时,如果使用了如下的发布方式 或者 参数配置不正确时,会发现 服务会挂掉一会儿,用户会访问不了。

kubectl  delete  --ignore-not-found=true -f deploy.yml
kubectl  create -f deploy.yml

原因是
pod被删除或者 容器启动后,到服务真正工作起来,中间会有一段时间无法正常访问,但 k8s 却认为服务是正常就绪状态。

本篇文章主要解决这个问题,实现 平滑的发布,发布更新服务过程中保证服务一直可用,用户零感知。

2 设置滚动更新

2.1 服务准备

我们有一个api服务deploy.yaml如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: familytree-api
  namespace: default
spec:
  selector:
    matchLabels:
      app: familytree-api
  replicas: 3
  template:
    metadata:
      labels:
        app: familytree-api
    spec:
      containers:
      - name: familytree-api
        image: 123.dkr.ecr.cn-sourthwest-1.amazonaws.com.cn/module/familytree-api-module:1.0.0
        imagePullPolicy: Always
        resources:
          requests:
            cpu: 1
            memory: 1Gi
          limits:
            cpu: 4
            memory: 4Gi
        env:
          - name: VERSION
            value: 1.0.0
        ports:
          - containerPort: 8080


---

apiVersion: v1
kind: Service
metadata:
  name: familytree-api-service
  namespace: default
spec:
  selector:
    app: familytree-api
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
---

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: familytree-api-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.class: traefik
    traefik.frontend.rule.type: PathPrefix
spec:
  rules:
  -
    host: api.test.com
    http:
      paths:
      - path: /api/nowater
        backend:
          serviceName: familytree-api-service
          servicePort: 8080
---

服务中包含url路由接口可返回版本号和当前hostname

访问路由

app.router.add_get('/api/nowater/version', service.version_print)

实现代码如下:

async def version_print(request):
    version = config["version"]
    hostname = socket.gethostname()
    try:
        print("打印版本 "+version+" hostname " + hostname)
        return web.json_response({'version': version, "hostname": hostname})
    except Exception as e:
        return web.json_response({'msg': e.value}, status=500)

使用命令发布服务

kubectl create -f deploy.yml

查看部署列表

kubectl get deployments | grep familytree-api

查看正在运行的pod

kubectl get pods | grep familytree-api

查看正在运行的pod使用的镜像

kubectl get deployment -o wide | grep familytree-api

输出如下:

[zzq@host3 ~]$ kubectl get deployment -o wide | grep familytree-api
familytree-api 3 3 3 0 4m familytree-api 123.dkr.ecr.cn-sourthwest-1.amazonaws.com.cn/module/familytree-api-module:1.0.0 app=familytree-api

通过pod描述,查看服务的当前映像版本

kubectl describe pods familytree-api-7c6fd4bb75-5qtr5

访问服务url

[zzq@host3 ~]$ curl http://api.test.com/api/nowater/version
{"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}

2.2 进行滚动升级

方式一 将yaml中的镜像版本修改为升级版本–推荐

image: 123.dkr.ecr.cn-sourthwest-1.amazonaws.com.cn/module/familytree-api-module:2.0.0

环境变量也修改为2.0.0

env:
          - name: VERSION
            value: 2.0.0

然后使用apply命令重新发布服务

kubectl apply -f deploy.yml --record

记得一定需要带有 --record,否则没有版本的信息记录,不利于回滚定位。

方式二 使用命令

kubectl set image deployments/familytree-api familytree-api=123.dkr.ecr.cn-sourthwest-1.amazonaws.com.cn/module/familytree-api-module:2.0.0

2.3 查看是否升级成功

查看状态:

kubectl rollout status deployment/familytree-api
Waiting for rollout to finish: 1 out of 3 new replicas have been updated..
deployment "familytree-api" successfully rolled out

暂停升级

kubectl rollout pause deployment <deployment>

继续升级

kubectl rollout resume deployment <deployment>

升级结束后,继续查看rs的状态:

kubectl get rs

根据AGE我们可以看到离我们最近的当前状态是:3,和我们的yaml文件是一致的,证明升级成功了。用describe命令可以查看升级的全部信息:

kubectl describe deployment familytree-api

输出为

[zzq@host3 ~]$ kubectl describe deployment familytree-api
Name:                   familytree-api
Namespace:              default
CreationTimestamp:      Mon, 23 Sep 2019 18:12:43 +0800
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision=2
                        kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"kubernetes.io/change-cause":"kubectl apply --filename=./deploy/deploy-prod.yml ...
                        kubernetes.io/change-cause=kubectl apply --filename=./deploy/deploy-prod.yml --record=true
Selector:               app=familytree-api
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=familytree-api
  Containers:
   familytree-api:
    Image:     123.dkr.ecr.cn-sourthwest-1.amazonaws.com.cn/module/familytree-api-module:2.0.0
    Port:       8080/TCP
    Host Port:  0/TCP
    Limits:
      cpu:     4
      memory:  4Gi
    Requests:
      cpu:     1
      memory:  1Gi
    Environment:
      VERSION:  2.0.0
    Mounts:     <none>
  Volumes:      <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   familytree-api-c6fbb4499 (3/3 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  20m   deployment-controller  Scaled up replica set familytree-api-66f79b747c to 3
  Normal  ScalingReplicaSet  3m    deployment-controller  Scaled up replica set familytree-api-c6fbb4499 to 1
  Normal  ScalingReplicaSet  3m    deployment-controller  Scaled down replica set familytree-api-66f79b747c to 2
  Normal  ScalingReplicaSet  3m    deployment-controller  Scaled up replica set familytree-api-c6fbb4499 to 2
  Normal  ScalingReplicaSet  3m    deployment-controller  Scaled down replica set familytree-api-66f79b747c to 1
  Normal  ScalingReplicaSet  3m    deployment-controller  Scaled up replica set familytree-api-c6fbb4499 to 3
  Normal  ScalingReplicaSet  3m    deployment-controller  Scaled down replica set familytree-api-66f79b747c to 0

1 滚动更新的定义和目标-LMLPHP

访问服务url

[zzq@host3 ~]$ curl http://api.test.com/api/nowater/version
{"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}

3 设置服务存活探针和服务健康检查探针以及相应参数

探针官网介绍

使用上面的发布步骤并不能保证 服务完全平滑。

因为k8s不能判断 一个的服务是否已经可用,只要pod正常启动就会判断成 可用。

但是pod启动后并不代表服务可用,比如java的程序 可能还需要启动spring boot框架,去连接数据库等待。

这样就会导致 短暂的 服务不可用。

3.1 服务不可用的示例


比如我们一直访问准备好的version版本查询接口如下:

while :; do curl http://api.test.com/api/nowater/version; sleep 1; done

然后进行版本更新发布

kubectl apply -f deploy.yml --record

输出如下:

[zzq@host3 ~]$ while :; do curl http://api.test.com/api/nowater/version; sleep 1; done 
----------
{"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
----------
{"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
----------
{"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
----------
{"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
----------
curl: (7) Failed to connect to http://api.test.com/api/nowater/version: Connection refused
curl: (7) Failed to connect to http://api.test.com/api/nowater/version: Connection refused
curl: (7) Failed to connect to http://api.test.com/api/nowater/version: Connection refused
curl: (7) Failed to connect to http://api.test.com/api/nowater/version: Connection refused
curl: (7) Failed to connect to http://api.test.com/api/nowater/version: Connection refused
----------
{"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
----------
{"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
----------
{"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
----------
{"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
----------
{"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
----------

我们可以发现镜像版本已经更新到2.0.0
我们可以在2.0.0镜像中加入延迟启动服务,也就是说,会先sleep 20s,然后才去启动app服务。
这就模拟了在服务启动过程中,虽然pod已经是存在的状态,但是并没有真正提供服务。
可以看到,由于延迟启动,api并没有真正做好准备提供服务,此时流量已经发到后端,导致服务不可用的状态

3.2 解决方案–探针检测

  在实际生产环境中会遇到这样那样的问题,比如:容器里面应用挂了或者说新启动的容器里面应用还没有就绪等等,所以说就需要进行探测来检验容器是否满足需求。

  那么一般的检测分为几种,比如:进程检测、业务检测。

  进程检测呢很好理解,也就是说通过检测容器进程来验证容器内应用是否存活。
  Kubelet会定期通过Docker Daemon获取所有Docker进程的运行情况,如果发现某个Docker容器未正常运行,则重新启动该容器进程。目前,进程级的健康检查都是默认启用的。

  业务检测呢也好理解,有些人会问,有了进程检测不就挺好的么,为什么要进行业务检测? 
  因为在很多实际场景下,仅仅使用进程级健康检查还远远不够。有时,从Docker的角度来看,容器进程依旧在运行;但是如果从应用程序的角度来看,假设应用代码处于死锁状态的话,那每次调度到这个容器的时候永远都无法正常响应用户的业务。比如对于使用java web服务的应用来说,并不是简单地说tomcat启动成功就可以对外提供服务的,还需要等待spring容器初始化,数据库连接连接上等等。

  为了解决以上问题,Kubernetes引人了一个在容器内执行的活性探针(liveness probe)的概念,以支持用户自己实现应用业务级的健康检查。这些检查项由Kubelet代为执行,以确保用户的应用程序正确运转,至于什么样的状态才算“正确”,则由用户自己定义。

  Kubernetes支持3种类型的应用健康检查动作,分别为HTTP Get、Container Exec和TCP Socket。exec的方式比较通用的,因为不是每个服务都有http服务,但每个服务都可以在自己内部定义健康检查的job,定期执行,然后将检查结果保存到一个特定的文件中,外部探针就不断的查看这个健康文件就OK了。

  介绍完存活性探针(liveness probe)之后我们来看看就绪探针(readiness probe),就绪探针是来确定容器是否已经就绪可以接受访问,只有当Pod中的容器都处于就绪状态时kubelet才会认定该Pod处于就绪状态,至于什么样的状态才算 ”就绪”,还是由用户自己定义。该状态的作用就是控制哪些Pod可以作为service的后端,如果Pod处于非就绪状态,那么它们将会被从service的load balancer中移除,防止 流量分发到 异常的pod中。

4 存活性探针(liveness probe)和就绪探针(readiness probe)的区别

  readinessProbe检查成功与否,决定这个pod是否会被加入到Service的load balancer列表中,即是否给它分配访问的流量,并不影响Pod本身的生命周期

  如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活探针; kubelet 将根据Pod的restartPolicy自动执行正确的操作。

  如果您希望容器在探测失败时被杀死并重新启动,那么请指定一个存活探针,并指定restartPolicy为Always或OnFailure。

  如果要仅在探测成功时才开始向 Pod 发送流量,请指定就绪探针。在这种情况下,就绪探针可能与存活探针相同,但是spec中的就绪探针的存在意味着Pod将在没有接收到任何流量的情况下启动,并且只有在探针探测成功后才开始接收流量。

  两种探测的配置方法完全一样,支持的配置参数也一样,既可单独探测又可结合者一起执行。

  LivenessProbe:用于判断容器是否存活(running状态),如果LivenessProbe探针探测到容器不健康,则kubelet杀掉该容器,并根据容器的重启策略做相应的处理。如果一个容器不包含LivenessProbe探针,则kubelet认为该容器的LivenessProbe探针返回的值永远是“Success”。

  ReadinessProbe:用于判断容器是否启动完成(ready状态),可以接收请求。如果ReadinessProbe探针检测到失败,则Pod的状态被修改。Endpoint Controller将从Service的Endpoint中删除包含该容器所在Pod的Endpoint。

4.1 存活性探测

livenessProbe:存活性探测,最主要是用来探测pod是否需要重启–决定把pod删除重新创建

在spec的containers中增加
与image同级
exec探针:

        livenessProbe:
          exec:
            command:
            - cat
            - /tmp/healthy
          initialDelaySeconds: 5
          periodSeconds: 10
          timeoutSeconds: 2
          failureThreshold: 3

http探针:

    livenessProbe:
      httpGet:
        path: /health
        port: 8080
        scheme: HTTP
    initialDelaySeconds: 5
    periodSeconds: 10
    timeoutSeconds: 2
    failureThreshold: 3

tcp探针:

  livenessProbe:
      tcpSocket:
        port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10
  timeoutSeconds: 2 
  failureThreshold: 3

参数说明

说法1:

initialDelaySeconds:容器启动后第一次执行探测是需要等待多少秒。

periodSeconds:执行探测的频率。默认是10秒,最小1秒。

timeoutSeconds:探测超时时间。默认1秒,最小1秒。

successThreshold:探测失败后,最少连续探测成功多少次才被认定为成功。默认是1。对于liveness必须是1。最小值是1。

failureThreshold:探测成功后,最少连续探测失败多少次才被认定为失败。默认是3。最小值是1。

说法2:

initialDelaySeconds: 表示容器启动之后延迟多久进行liveness探测

timeoutSeconds:每次执行探测的超时时间

periodSeconds:探测的周期时间

successThreshold:最少连续几次探测成功的次数,满足该次数则认为success。

failureThreshold:最少连续几次探测失败的次数,满足该次数则认为fail作

举例:

initialDelaySeconds: 5 #告诉kubelet在第一次执行probe之前要的等待5秒钟

periodSeconds: 10 #kubelet要每隔10秒执行一次liveness probe 检查

timeoutSeconds: 2 #超长时长,默认为1s,最小值也为1s

failureThreshold: 3 #处于成功状态时,探测操作至少连续多少次的失败才被视为检测不通过,默认为3,最小为1

探针检测命令是在容器中执行 cat /tmp/healthy 命令。
如果命令执行成功,将返回0,kubelet就会认为该容器是活着的并且很健康。如果返回非0值,kubelet就会杀掉这个容器并重启它。

4.2 就绪性探测


readinessProbe:就绪性探测,用来探测pod是否已经能够提供服务–决定是否参与分配流量

在spec的containers中增加
与image同级

        readinessProbe:
          tcpSocket:
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 10

或者

        readinessProbe:
          httpGet:
            path: /api/nowater/version
            port: 8080
            httpHeaders:
            - name : X-Custom-Header
              value: Awesome
          initialDelaySeconds: 5
          periodSeconds: 10

periodSeconds 规定kubelet要每隔10秒执行一次readinessProbe 检查。

initialDelaySeconds 告诉kubelet在第一次执行probe之前要的等待5秒钟。
探针检测命令是检查tcp连接 端口80 是否畅通。
也可以检查某个http 请求是否返回成功码。
如果返回一个成功的返回码,kubelet就会认定该容器服务是可用的。
如果返回失败的返回码,kubelet将该pod改为not ready,将会被从service的load balancer中移除。

注意:任何大于200小于400的返回码都会认定是成功的返回码。其他返回码都会被认为是失败的返回码。

由于设置了readinessProbe,虽然pod已经启动起来了,但是并不会立即投入使用,会出现了 READY: 0/1 的情况
并且有pod出现了一直持续Terminating状态,因为滚动更新的限制,至少要保证有pod可用

再查看curl的状态,image的版本平滑更新到2.0.0,没有出现报错的状况

[zzq@host3 ~]$ while :; do curl http://api.test.com/api/nowater/version; sleep 1; done 
----------
{"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
----------
{"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
----------
{"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
----------
{"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
----------
{"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
----------
{"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
----------
{"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
----------
{"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
----------
{"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
----------

4.3 调整参数设置

在spec下面添加滚动升级策略:
与template同级

minReadySeconds: 5
strategy:
  # indicate which strategy we want for rolling update
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 1

minReadySeconds:

这个值默认是0,Kubernetes会假设该容器启动起来后就可以提供服务了,会导致用户访问异常
这里需要估一个比较合理的值,从容器启动到应用正常提供服务
如果没有设置该值,Kubernetes会假设该容器启动起来后就提供服务了
如果没有设置该值,在某些极端情况下可能会造成服务不正常运行

maxSurge:
升级过程中最多可以比原先设置多出的POD数量
例如:maxSurage=1,replicas=5,则表示Kubernetes会先启动1一个新的Pod后才删掉一个旧的POD,整个升级过程中最多会有5+1个POD。

maxUnavaible:
升级过程中最多有多少个POD处于无法提供服务的状态
当maxSurge不为0时,该值也不能为0
例如:maxUnavaible=1,则表示Kubernetes整个升级过程中最多会有1个POD处于无法服务的状态。

4.4 格式如下

1 滚动更新的定义和目标-LMLPHP

4.5 回滚

查看Deployment的升级历史:

kubectl rollout history deployment nginx-deploy

查看指定版本的升级历史

kubectl rollout history deployment nginx-deploy --revision=3

假如现在要直接回退到当前版本的前一个版本:

kubectl rollout undo deployment nginx-deploy
deployment "nginx-deploy" rolled back

当然也可以用revision回退到指定的版本:

kubectl rollout undo deployment nginx-deploy --to-revision=2
deployment "nginx-deploy" rolled back

现在可以用命令查看Deployment现在的状态了。

5 k8s滚动更新的原理


部署(Deployment)自动创建副本集(ReplicaSet),副本集根据参数策略可以精确地控制每次替换的Pod数量,从而可以很好的实现滚动更新。具体来说,k8s每次使用一个新的副本控制器(replication controller)来替换已存在的副本控制器,从而始终使用一个新的Pod模板来替换旧的pod模板。

大致步骤如下:

1、创建一个新的replication controller。
2、增加或减少pod副本数量,直到满足当前批次期望的数量。
3、删除旧的replication controller。

5.1 部署概况

使用命令查看部署概况

kubectl get deployment

1 滚动更新的定义和目标-LMLPHP


上图包含的几个滚动发布过程标量,说明如下:

DESIRED 最终期望处于READY状态的副本数
CURRENT 当前的副本总数
UP-TO-DATE 当前完成更新的副本数
AVAILABLE 当前可用的副本数

5.2 部署详情

使用命令查看部署详情

kubectl describe deployment familytree-api

1 滚动更新的定义和目标-LMLPHP

从上图可以看到,k8s精确地控制着整个发布过程,滚动进行,直到所有副本全部更新。其实,k8s提供了两个参数maxSurge和maxUnavailable来精确地控制每次滚动的pod数量,如下:

maxSurge 滚动更新过程中运行操作期望副本数的最大pod数,可以为绝对数值(eg:5),但不能为0;也可以为百分数(eg:10%)。默认为25%。
maxUnavailable 滚动更新过程中不可用的最大pod数,可以为绝对数值(eg:5),但不能为0;也可以为百分数(eg:10%)。默认为25%。
如果未指定这两个可选参数,则k8s使用默认配置,如下:

kubectl get deployment familytree-api -o yaml

1 滚动更新的定义和目标-LMLPHP


剖析部署helloworldapi的标准输出:

当前的副本总数 = 3 + 3 * 25% = 4,所以CURRENT为4。

当前可用的副本数 = 3 - 3 * 25% = 2,所以AVAILABLE为2。

整个滚动过程是通过控制两个副本集来完成的,新的副本集:familytree-api-c6fbb4499;旧的副本集: familytree-api-66f79b747c 。

理想状态下的滚动过程:

创建了一个新的副本集,并为其分配1个新版本的pod,使副本总数达到4,一切正常。
通知旧副本集,销毁1个旧版本的pod,使可用副本总数保持到2,一起正常。
当1个副本销毁成功后,通知新副本集,再新增1个新版本的pod,使副本总数达到4,一切正常。

只要销毁成功,新副本集,就会创造新的pod,一直循环,直到旧的副本集pod数量为0。
有时整个滚动过程也是不理想的,不符合上述的规律,会有一些混乱。

k8s最终都会使服务全部更新到期望状态,始终保持最大的副本总数和可用副本总数的不变性

6 deployment的常用命令和参数说明


查看部署状态

kubectl rollout status deployment/review-demo --namespace=scm

kubectl describe deployment/review-demo --namespace=scm


或者这种写法

kubectl rollout status deployments review-demo --namespace=scm

kubectl describe deployments review-demo --namespace=scm

升级

kubectl set image deployment/review-demo review-demo=library/review-demo:0.0.1 --namespace=scm

或者

kubectl edit deployment/review-demo --namespace=scm

编辑.spec.template.spec.containers[0].image的值

终止升级

kubectl rollout pause deployment/review-demo --namespace=scm

继续升级

kubectl rollout resume deployment/review-demo --namespace=scm

回滚

kubectl rollout undo deployment/review-demo --namespace=scm

查看deployments版本

kubectl rollout history deployments --namespace=scm

回滚到指定版本

kubectl rollout undo deployment/review-demo --to-revision=2 --namespace=scm

升级历史

kubectl describe deployment/review-demo --namespace=scm

Name: review-demo
Namespace: scm
CreationTimestamp: Tue, 31 Jan 2017 16:42:01 +0800
Labels: app=review-demo
Selector: app=review-demo
Replicas: 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
OldReplicaSets: <none>
NewReplicaSet: review-demo-2741031620 (3/3 replicas created)
Events: FirstSeen LastSeen Count From SubobjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ -------
1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set review-demo-2741031620 to 1
1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set review-demo-1914295649 to 2
1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set review-demo-2741031620 to 2
1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set review-demo-1914295649 to 1
1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set review-demo-2741031620 to 3
1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set review-demo-1914295649 to 0

deployment文件

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: review-demo
namespace: scm
labels:
app: review-demo
spec:
replicas: 3 #
minReadySeconds: 60 #滚动升级时60s后认为该pod就绪
strategy:
rollingUpdate: ##由于replicas为3,则整个升级,pod个数在2-4个之间
maxSurge: 1 #滚动升级时会先启动1个pod
maxUnavailable: 1 #滚动升级时允许的最大Unavailable的pod个数
template:
metadata:
labels:
app: review-demo
spec: terminationGracePeriodSeconds: 60 ##k8s将会给应用发送SIGTERM信号,可以用来正确、优雅地关闭应用,默认为30秒
containers:
- name: review-demo
image: library/review-demo:0.0.1-SNAPSHOT
imagePullPolicy: IfNotPresent
livenessProbe: #kubernetes认为该pod是存活的,不存活则需要重启
httpGet: path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60 ## equals to the maximum startup time of the application + couple of seconds
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
readinessProbe: #kubernetes认为该pod是启动成功的
httpGet: path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 30 ## equals to minimum startup time of the application timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
resources: # keep request = limit to keep this container in guaranteed class requests:
cpu: 50m
memory: 200Mi
limits:
cpu: 500m
memory: 500Mi
env:
- name: PROFILE
value: "test"
ports:
- name: http
containerPort: 8080

几个重要参数说明
maxSurge与maxUnavailable
maxSurge: 1 表示滚动升级时会先启动1个pod
maxUnavailable: 1 表示滚动升级时允许的最大Unavailable的pod个数
由于replicas为3,则整个升级,pod个数在2-4个之间

terminationGracePeriodSeconds
k8s将会给应用发送SIGTERM信号,可以用来正确、优雅地关闭应用,默认为30秒。

如果需要更优雅地关闭,则可以使用k8s提供的pre-stop lifecycle hook 的配置声明,将会在发送SIGTERM之前执行。

livenessProbe与readinessProbe
livenessProbe是kubernetes认为该pod是存活的,不存在则需要kill掉,然后再新启动一个,以达到replicas指定的个数。

readinessProbe是kubernetes认为该pod是启动成功的,这里根据每个应用的特性,自己去判断,可以执行command,也可以进行httpGet。比如对于使用java web服务的应用来说,并不是简单地说tomcat启动成功就可以对外提供服务的,还需要等待spring容器初始化,数据库连接连接上等等。对于spring boot应用,默认的actuator带有/health接口,可以用来进行启动成功的判断。

其中readinessProbe.initialDelaySeconds可以设置为系统完全启动起来所需的最少时间,livenessProbe.initialDelaySeconds可以设置为系统完全启动起来所需的最大时间+若干秒。

这几个参数配置好了之后,基本就可以实现近乎无缝地平滑升级了。对于使用服务发现的应用来说,readinessProbe可以去执行命令,去查看是否在服务发现里头应该注册成功了,才算成功。


参考:
原文链接:https://blog.csdn.net/zzq900503/article/details/101221899

04-09 20:18