服务滚动升级

当有镜像发布新版本,新版本服务上线时如何实现服务的滚动和平滑升级?

如果你使用 ReplicationController 创建的 pod 可以使用 kubectl rollingupdate 命令滚动升级,如果使用的是 Deployment 创建的 Pod 可以直接修改 yaml 文件后执行 kubectl apply 即可。

Deployment 已经内置了 RollingUpdate strategy,因此不用再调用 kubectl rollingupdate 命令,升级的过程是先创建新版的 pod 将流量导入到新 pod 上后销毁原来的旧的 pod。

Rolling Update 适用于 DeploymentReplication Controller,官方推荐使用 Deployment 而不再使用 Replication Controller。

使用 ReplicationController 时的滚动升级请参考官网说明:https://kubernetes.io/docs/tasks/run-application/rolling-update-replication-controller/

ReplicationController 与 Deployment 的关系

ReplicationController 和 Deployment 的 RollingUpdate 命令有些不同,但是实现的机制是一样的,关于这两个 kind 的关系我引用了 ReplicationController 与 Deployment 的区别 中的部分内容如下,详细区别请查看原文。

ReplicationController

Replication Controller 为 Kubernetes 的一个核心内容,应用托管到 Kubernetes 之后,需要保证应用能够持续的运行,Replication Controller 就是这个保证的 key,主要的功能如下:

  • 确保 pod 数量:它会确保 Kubernetes 中有指定数量的 Pod 在运行。如果少于指定数量的 pod,Replication Controller 会创建新的,反之则会删除掉多余的以保证 Pod 数量不变。
  • 确保 pod 健康:当 pod 不健康,运行出错或者无法提供服务时,Replication Controller 也会杀死不健康的 pod,重新创建新的。
  • 弹性伸缩 :在业务高峰或者低峰期的时候,可以通过 Replication Controller 动态的调整 pod 的数量来提高资源的利用率。同时,配置相应的监控功能(Hroizontal Pod Autoscaler),会定时自动从监控平台获取 Replication Controller 关联 pod 的整体资源使用情况,做到自动伸缩。
  • 滚动升级:滚动升级为一种平滑的升级方式,通过逐步替换的策略,保证整体系统的稳定,在初始化升级的时候就可以及时发现和解决问题,避免问题不断扩大。

Deployment

Deployment 同样为 Kubernetes 的一个核心内容,主要职责同样是为了保证 pod 的数量和健康,90% 的功能与 Replication Controller 完全一样,可以看做新一代的 Replication Controller。但是,它又具备了 Replication Controller 之外的新特性:

  • Replication Controller 全部功能:Deployment 继承了上面描述的 Replication Controller 全部功能。
  • 事件和状态查看:可以查看 Deployment 的升级详细进度和状态。
  • 回滚:当升级 pod 镜像或者相关参数的时候发现问题,可以使用回滚操作回滚到上一个稳定的版本或者指定的版本。
  • 版本记录: 每一次对 Deployment 的操作,都能保存下来,给予后续可能的回滚使用。
  • 暂停和启动:对于每一次升级,都能够随时暂停和启动。
  • 多种升级方案:Recreate:删除所有已存在的 pod, 重新创建新的; RollingUpdate:滚动升级,逐步替换的策略,同时滚动升级时,支持更多的附加参数,例如设置最大不可用 pod 数量,最小升级间隔时间等等。

创建测试镜像

我们来创建一个特别简单的 web 服务,当你访问网页时,将输出一句版本信息。通过区分这句版本信息输出我们就可以断定升级是否完成。

所有配置和代码见 manifests/test/rolling-update-test 目录。

Web 服务的代码 main.go

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net/http"
  6. )
  7. func sayhello(w http.ResponseWriter, r *http.Request) {
  8. fmt.Fprintf(w, "This is version 1.") // 这个写入到 w 的是输出到客户端的
  9. }
  10. func main() {
  11. http.HandleFunc("/", sayhello) // 设置访问的路由
  12. log.Println("This is version 1.")
  13. err := http.ListenAndServe(":9090", nil) // 设置监听的端口
  14. if err != nil {
  15. log.Fatal("ListenAndServe:", err)
  16. }
  17. }

创建 Dockerfile

  1. FROM alpine:3.5
  2. ADD hellov2 /
  3. ENTRYPOINT ["/hellov2"]

注意修改添加的文件的名称。

创建 Makefile

修改镜像仓库的地址为你自己的私有镜像仓库地址。

修改 Makefile 中的 TAG 为新的版本号。

  1. all: build push clean
  2. .PHONY: build push clean
  3. TAG = v1
  4. # Build for linux amd64
  5. build:
  6. GOOS=linux GOARCH=amd64 go build -o hello${TAG} main.go
  7. docker build -t sz-pg-oam-docker-hub-001.tendcloud.com/library/hello:${TAG} .
  8. # Push to tenxcloud
  9. push:
  10. docker push sz-pg-oam-docker-hub-001.tendcloud.com/library/hello:${TAG}
  11. # Clean
  12. clean:
  13. rm -f hello${TAG}

编译

  1. make all

分别修改 main.go 中的输出语句、Dockerfile 中的文件名称和 Makefile 中的 TAG,创建两个版本的镜像。

测试

我们使用 Deployment 部署服务来测试。

配置文件 rolling-update-test.yaml

  1. apiVersion: extensions/v1beta1
  2. kind: Deployment
  3. metadata:
  4. name: rolling-update-test
  5. spec:
  6. replicas: 3
  7. template:
  8. metadata:
  9. labels:
  10. app: rolling-update-test
  11. spec:
  12. containers:
  13. - name: rolling-update-test
  14. image: sz-pg-oam-docker-hub-001.tendcloud.com/library/hello:v1
  15. ports:
  16. - containerPort: 9090
  17. ---
  18. apiVersion: v1
  19. kind: Service
  20. metadata:
  21. name: rolling-update-test
  22. labels:
  23. app: rolling-update-test
  24. spec:
  25. ports:
  26. - port: 9090
  27. protocol: TCP
  28. name: http
  29. selector:
  30. app: rolling-update-test

部署 service

  1. kubectl create -f rolling-update-test.yaml

修改 traefik ingress 配置

ingress.yaml 文件中增加新 service 的配置。

  1. - host: rolling-update-test.traefik.io
  2. http:
  3. paths:
  4. - path: /
  5. backend:
  6. serviceName: rolling-update-test
  7. servicePort: 9090

修改本地的 host 配置,增加一条配置:

  1. 172.20.0.119 rolling-update-test.traefik.io

注意:172.20.0.119 是我们之前使用 keepalived 创建的 VIP。

打开浏览器访问 http://rolling-update-test.traefik.io 将会看到以下输出:

  1. This is version 1.

滚动升级

只需要将 rolling-update-test.yaml 文件中的 image 改成新版本的镜像名,然后执行:

  1. kubectl apply -f rolling-update-test.yaml

也可以参考 Kubernetes Deployment Concept 中的方法,直接设置新的镜像。

  1. kubectl set image deployment/rolling-update-test rolling-update-test=sz-pg-oam-docker-hub-001.tendcloud.com/library/hello:v2

或者使用 kubectl edit deployment/rolling-update-test 修改镜像名称后保存。

使用以下命令查看升级进度:

  1. kubectl rollout status deployment/rolling-update-test

升级完成后在浏览器中刷新 http://rolling-update-test.traefik.io 将会看到以下输出:

  1. This is version 2.

说明滚动升级成功。

使用 ReplicationController 创建的 Pod 如何 RollingUpdate

以上讲解使用 Deployment 创建的 Pod 的 RollingUpdate 方式,那么如果使用传统的 ReplicationController 创建的 Pod 如何 Update 呢?

举个例子:

  1. $ kubectl -n spark-cluster rolling-update zeppelin-controller --image sz-pg-oam-docker-hub-001.tendcloud.com/library/zeppelin:0.7.1
  2. Created zeppelin-controller-99be89dbbe5cd5b8d6feab8f57a04a8b
  3. Scaling up zeppelin-controller-99be89dbbe5cd5b8d6feab8f57a04a8b from 0 to 1, scaling down zeppelin-controller from 1 to 0 (keep 1 pods available, don't exceed 2 pods)
  4. Scaling zeppelin-controller-99be89dbbe5cd5b8d6feab8f57a04a8b up to 1
  5. Scaling zeppelin-controller down to 0
  6. Update succeeded. Deleting old controller: zeppelin-controller
  7. Renaming zeppelin-controller-99be89dbbe5cd5b8d6feab8f57a04a8b to zeppelin-controller
  8. replicationcontroller "zeppelin-controller" rolling updated

只需要指定新的镜像即可,当然你可以配置 RollingUpdate 的策略。

参考