Joe Blog

摘要

Kubernetes Deployment滚动更新机制,提供了滚动进度查询,滚动历史记录,回滚等能力,无疑是使用Kubernetes进行应用滚动发布的首选

rolling update的相关项

以下面的frontend Deployment为例

apiVersion: v1
kind: Deployment
metadata:
  name: frontend
spec:
  minReadySeconds: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 3
      maxUnavailable: 2
  replicas: 6
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google-samples/gb-frontend:v4
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 80

针对这个yml文件中滚到更新的部分

...
spec:
  minReadySeconds: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 3
      maxUnavailable: 2
...

因此可以通过这些指标计算得到:

在Deployment rollout时,需要保证Available(Ready) Pods数不低于 desired pods replicas - maxUnavailable; 保证所有的Pods数不多于 desired pods replicas + maxSurge

滚动更新过程

Note: A Deployment’s rollout is triggered if and only if the Deployment’s pod template (that is, .spec.template) is changed, for example if the labels or container images of the template are updated. Other updates, such as scaling the Deployment, do not trigger a rollout.

继续以上面的Deployment为例子,并考虑最常用的情况:

.spec.template.spec.containers[0].image from gcr.io/google-samples/gb-frontend:v4 to gcr.io/google-samples/gb-frontend:v5

kubectl set image deploy frontend php-redis=gcr.io/google-samples/gb-frontend:v5 --record

set image之后,导致Deployment’s Pod Template hash发生变化,就会触发rollout

过程:

理解rollout pause和resume

You can pause a Deployment before triggering one or more updates and then resume it. This will allow you to apply multiple fixes in between pausing and resuming without triggering unnecessary rollouts.

kubectl rollout pause只会用来停止触发下一次rollout,正在执行的滚动历程是不会停下来的,而是会继续正常的进行滚动,直到完成。等下一次,用户再次触发rollout时,Deployment就不会真的去启动执行滚动更新了,而是等待用户执行了kubectl rollout resume,流程才会真正启动执行。

For example, with a Deployment that was just created:

$ kubectl get deploy
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx     3         3         3            3           1m
$ kubectl get rs
NAME               DESIRED   CURRENT   READY     AGE
nginx-2142116321   3         3         3         1m

Pause by running the following command:

$ kubectl rollout pause deployment/nginx-deployment
deployment "nginx-deployment" paused

Then update the image of the Deployment:

$ kubectl set image deploy/nginx-deployment nginx=nginx:1.9.1
deployment "nginx-deployment" image updated

Notice that no new rollout started:

$ kubectl rollout history deploy/nginx-deployment
deployments "nginx"
REVISION  CHANGE-CAUSE
1   <none>

$ kubectl get rs
NAME               DESIRED   CURRENT   READY     AGE
nginx-2142116321   3         3         3         2m

You can make as many updates as you wish, for example, update the resources that will be used:

$ kubectl set resources deployment nginx -c=nginx --limits=cpu=200m,memory=512Mi
deployment "nginx" resource requirements updated

The initial state of the Deployment prior to pausing it will continue its function, but new updates to the Deployment will not have any effect as long as the Deployment is paused.

Eventually, resume the Deployment and observe a new ReplicaSet coming up with all the new updates:

$ kubectl rollout resume deploy/nginx-deployment
deployment "nginx" resumed
$ kubectl get rs -w
NAME               DESIRED   CURRENT   READY     AGE
nginx-2142116321   2         2         2         2m
nginx-3926361531   2         2         0         6s
nginx-3926361531   2         2         1         18s
nginx-2142116321   1         2         2         2m
nginx-2142116321   1         2         2         2m
nginx-3926361531   3         2         1         18s
nginx-3926361531   3         2         1         18s
nginx-2142116321   1         1         1         2m
nginx-3926361531   3         3         1         18s
nginx-3926361531   3         3         2         19s
nginx-2142116321   0         1         1         2m
nginx-2142116321   0         1         1         2m
nginx-2142116321   0         0         0         2m
nginx-3926361531   3         3         3         20s
^C
$ kubectl get rs
NAME               DESIRED   CURRENT   READY     AGE
nginx-2142116321   0         0         0         2m
nginx-3926361531   3         3         3         28s

Note: You cannot rollback a paused Deployment until you resume it

ReplicaSet和rollout history的关系

You can set .spec.revisionHistoryLimit field in a Deployment to specify how many old ReplicaSets for this Deployment you want to retain.

Setting the kubectl flag –record to true allows you to record current command in the annotations of the resources being created or updated.

By default, all revision history will be kept. In a future version, it will default to switch to 2.

默认情况下,所有通过kubectl xxxx –record都会被kubernetes记录到etcd进行持久化,这无疑会占用资源,最重要的是,时间久了,当你kubectl get rs时,会有成百上千的垃圾RS返回给你,那时你可能就眼花缭乱了。

最好通过设置Deployment的.spec.revisionHistoryLimit来限制最大保留的revision number,比如15个版本,回滚的时候一般只会回滚到最近的几个版本就足够了。

Note: Explicitly setting this field to 0, will result in cleaning up all the history of your Deployment thus that Deployment will not be able to roll back

回滚是如何进行的

用户通过执行rollout undo并指定--to-revison,可以将Deployment回滚到指定的revision。

kubectl rollout undo deploy frontend --to-revision=7

回滚的时候也是按照滚动的机制进行的,同样要遵守maxSurge和maxUnavailable的约束。并不是一次性将所有的Pods删除,然后再一次性创建新的Pods。