简介
当kubernetes集群中的某个服务需要升级时,传统的做法是,先将要更新的服务下线,业务停止后再更新版本和配置,然后重新启动并提供服务。如果业务集群规模较大时,这个工作就变成了一个挑战,而且先全部了停止,再逐步升级的方式会导致服务较长时间不可用。kubernetes提供了滚动更新(rolling-update)的方式来解决上述问题。
简单来说,滚动更新就是针对多实例服务的一种不中断服务的更新升级方式。一般情况下,对于多实例服务,滚动更新采用对各个实例逐个进行单独更新而非同一时刻对所有实例进行全部更新的方式。
对于k8s集群部署的service来说,rolling update就是指一次仅更新一个pod,并逐个进行更新,而不是在同一时刻将该service下面的所有pod shutdown,避免业务中断。
Service、Deployment、RS、RC和Pod之间的关系
对于我们要部署的应用来说,一般是由多个抽象的service组成。在kubernetes中,一个service通过label selector match出一个pods集合,这些Pods作为service的endpoint,是真正承载业务的实体。而pod在集群内的部署、调度、副本数则是通过Deployment或者RC这些更高级别的抽象来管理的。如下图:
新版本的Kubernetes推荐用Deployment替代ReplicationController,在Deployment这个概念下在保持Pod副本数上实际发挥作用的是隐藏在背后的Replica Set。
因此,我们可以看到Kubernetes上Service的rolling update实质上是对Service所match出来的Pod集合的Rolling update,而控制Pod部署、调度和副本调度的却又恰恰是Deployment和replication controller,因此后两者才是kubernetes service rolling update真正要面对的实体。
使用kubectl rolling-update更新
使用kubectl rolling-update命令的方式,主要是针对使用RC创建的pods。
先来看下面一个示例,创建一个包含4个nginx副本的RC nginx-demo-v1-rc.yml:apiVersion: v1kind: ReplicationControllermetadata: name: nginx-demo-v1spec: replicas: 4 selector: app: nginx-demo ver: v1 template: metadata: labels: app: nginx-demo ver: v1 spec: containers: - name: nginx-demo image: nginx:1.10.1 ports: - containerPort: 80 protocol: TCP env: - name: NGX_DEMO_VER value: v1
创建一个service,nginx-demo-svc.yml内容如下:
apiVersion: v1kind: Servicemetadata: name: nginx-demo-svcspec: ports: - port: 80 protocol: TCP selector: app: nginx-demo
创建rc和service:
kubectl create -f nginx-demo-v1-rc.ymlkubectl create -f nginx-demo-svc.yml
创建完成以后,可以通过访问任一Pod的环境变量查看NGX_DEMO_VER的值,为v1
现在我们创建一个nginx-demo-v2-rc.yml的文件,来升级现有的pod:
apiVersion: v1kind: ReplicationControllermetadata: name: nginx-demo-v2spec: replicas: 4 selector: app: nginx-demo ver: v2 template: metadata: labels: app: nginx-demo ver: v2 spec: containers: - name: nginx-demo image: nginx:1.11.9 ports: - containerPort: 80 protocol: TCP env: - name: NGX_DEMO_VER value: v2
执行更新操作:
kubectl rolling-update nginx-demo-svc -f nginx-demo-v2-rc.yml
需要注意的是,在执行滚动升级时,两个版本的yml文件区别:
- RC的名字不能与旧的RC名字相同
- 在selector中应至少有一个label与旧的RC的label不同,以标识其为新的RC。
我们可以通过如下操作来查看更新的完整过程:
kubectl rolling-update nginx-demo-v1 --udpate-period=10s -f nginx-demo-v2-rc.yml
当所有旧的pod被新的Pod替换完成以后,更新完成。
使用kubectl rolling-update实现滚动更新的不足:
rolling-update的逻辑是由kubectl发出N条命令到APIServer完成的,很可能因为网络原因导致update中断
需要创建一个新的rc,名字与要更新的rc不能一样
回滚还需要执行rolling-update,只是用老的版本替换新的版本
service执行的rolling-update在集群中没有记录,后续无法跟踪rolling-update历史
现如今,RC的方式已经被Deployment替代。
Deployment的rolling-update
kubernetes的Deployment是一个更高级别的抽象。Deployment会创建一个Replica Set,用来保证Deployment中的Pod的副本数。要rolling-update deployment中的Pod,只需要修改Deployment自己的yml文件并应用即可。这个修改会创建一个新的Replica Set,在增加这个新RS的pod数的同时,减少旧RS的pod,直至完全升级。而这一切都发生在Server端,并不需要kubectl参与。
创建一个Deployment yml文件nginx-demo-dm.yml:
apiVersion: extensions/v1beta1kind: Deploymentmetadata: name: nginx-demospec: replicas: 4 selector: matchLabels: app: nginx-demo minReadySeconds: 10 template: metadata: labels: app: nginx-demo version: v1 spec: containers: - name: deployment-demo image: nginx:1.10.1 ports: - containerPort: 80 protocol: TCP
创建该deployment:
kubect create -f nginx-demo-dm.yml --record
然后我们可以直接修改该deployment文件,如下 :
apiVersion: extensions/v1beta1kind: Deploymentmetadata: name: nginx-demospec: replicas: 4 selector: matchLabels: app: nginx-demo minReadySeconds: 10 template: metadata: labels: app: nginx-demo version: v2 spec: containers: - name: deployment-demo image: nginx:1.11.9 ports: - containerPort: 80 protocol: TCP
一共就改了两个地方,将version改为了v2,将nginx镜像从1.10.1改到了1.11.9,执行如下操作应用更改:
kubectl apply -f nginx-demo-dm.yml --record
这个时候,我们可以通过执行kubectl get rs
来查看到rs的变化,以确认是否在执行升级。也可以通过kubectl describe deployment nginx-demo
来查看详细的rolling-update
的过程。还可以通过kubectl rollout status deployment/nginx-demo
来查看更新状态。
除了使用apply方式来应用更改以外,还有另外一种方式可以直接升级。就是通过kubectl edit nginx-demo-dm.yml
来编辑deployment文件,保存以后,不需要执行apply,就会自动完成升级。
我们可以注意到,在执行deployment的操作时,使用了一个--record参数,这个参数是用来告诉apiserver记录update的历史。可以通过如下命令来查看update历史:
kubectl rollout history deployment nginx-demo
查看指定revision的详细信息:
kubectl rollout history deployment hello-deployment --revision=2
需要说明的是,在升级完成以后,旧的RS也不会被删除,这些信息都会存储到server端,以方便回滚。
deployment下的pod的回滚操作相当简单,直接执行rollout undo即可将deployment回滚到record中记录的上一个revision:kubectl rollout undo deployment nginx-demo
执行如下操作,回滚到指定版本:
kubectl rollout undo deployment hello-deployment --to-version=2