kubernetes-pod的升级与回滚

当集群中的某个服务需要升级时,我们需要停止目前与该服务相关的所有Pod,然后下载新版本镜像并创建新的Pod。如果集群规模比较大,则这个工作变成了一个挑战,而且先全部停止然后逐步升级的方式会导致较长时间的服务不可用。Kubernetes提供了滚动升级功能来解决上述问题。如果Pod是通过Deployment创建的,则用户可以在运行时修改Deployment的Pod定义(spec.template)或镜像名称,并应用到Deployment对象上,系统即可完成Deployment的自动更新操作。如果在更新过程中发生了错误,则还可以通过回滚操作恢复Pod的版本。

Deployment的升级

在Deployment的定义中,可以通过spec.strategy指定Pod更新的策略,目前支持两种策略:Recreate(重建)和RollingUpdate(滚动更新),默认值为RollingUpdate。
kubernetes-pod的升级与回滚
现在Pod镜像需要被更新为Nginx:1.9.1,我们可以通过kubectl set image命令为Deployment设置新的镜像名称:
kubernetes-pod的升级与回滚
另一种更新的方法是使用kubectl edit命令修改Deployment的配置,将spec.template.spec.containers[0].image从Nginx:1.7.9更改为Nginx:1.9.1:
kubernetes-pod的升级与回滚
一旦镜像名(或Pod定义)发生了修改,则将触发系统完成Deployment所有运行Pod的滚动升级操作。

查看当前运行的Pod,名称已经更新了:[插图]查看Pod使用的镜像,已经更新为Nginx:1.9.1了。

那么,Deployment是如何完成Pod更新的呢?我们可以使用kubectl describe deployments/nginx-deployment命令仔细观察Deployment的更新过程。初始创建Deployment时,系统创建了一个ReplicaSet(nginx-deployment-4087004473),并按用户的需求创建了3个Pod副本。当更新Deployment时,系统创建了一个新的ReplicaSet(nginx-deployment-3599678771),并将其副本数量扩展到1,然后将旧的ReplicaSet缩减为2。之后,系统继续按照相同的更新策略对新旧两个ReplicaSet进行逐个调整。最后,新的ReplicaSet运行了3个新版本Pod副本,旧的ReplicaSet副本数量则缩减为0。

在整个升级的过程中,系统会保证至少有两个Pod可用,并且最多同时运行4个Pod,这是Deployment通过复杂的算法完成的。Deployment需要确保在整个更新过程中只有一定数量的Pod可能处于不可用状态。在默认情况下,Deployment确保可用的Pod总数至少为所需的副本数量(DESIRED)减1,也就是最多1个不可用(maxUnavailable=1)。Deployment还需要确保在整个更新过程中Pod的总数量不会超过所需的副本数量太多。在默认情况下,Deployment确保Pod的总数最多比所需的Pod数多1个,也就是最多1个浪涌值(maxSurge=1)。Kubernetes从1.6版本开始,maxUnavailable和maxSurge的默认值将从1、1更新为所需副本数量的25%、25%。

这里需要注意多重更新(Rollover)的情况。如果Deployment的上一次更新正在进行,此时用户再次发起Deployment的更新操作,那么Deployment会为每一次更新都创建一个ReplicaSet,而每次在新的ReplicaSet创建成功后,会逐个增加Pod副本数,同时将之前正在扩容的ReplicaSet停止扩容(更新),并将其加入旧版本ReplicaSet列表中,然后开始缩容至0的操作。

还需要注意更新Deployment的标签选择器(Label Selector)的情况。通常来说,不鼓励更新Deployment的标签选择器,因为这样会导致Deployment选择的Pod列表发生变化,也可能与其他控制器产生冲突。如果一定要更新标签选择器,那么请务必谨慎,确保不会出现其他问题。关于Deployment标签选择器的更新的注意事项如下。

Deployment的回滚

有时(例如新的Deployment不稳定时)我们可能需要将Deployment回滚到旧版本。在默认情况下,所有Deployment的发布历史记录都被保留在系统中,以便于我们随时进行回滚(可以配置历史记录数量)。假设在更新Deployment镜像时,将容器镜像名误设置成Nginx:1.91(一个不存在的镜像),则这时Deployment的部署过程会卡住。

由于执行过程卡住,所以需要执行Ctrl-C命令来终止这个查看命令。查看ReplicaSet,可以看到新建的ReplicaSet(nginx-deployment-3660254150):[插图]再查看创建的Pod,会发现新的ReplicaSet创建的1个Pod被卡在镜像拉取过程中。
为了解决上面这个问题,我们需要回滚到之前稳定版本的Deployment。

首先,kubectl rollout history命令检查这个Deployment部署的历史记录
kubernetes-pod的升级与回滚

注意,在创建Deployment时使用–record参数,就可以在CHANGE-CAUSE列看到每个版本使用的命令了。另外,Deployment的更新操作是在Deployment进行部署(Rollout)时被触发的,这意味着当且仅当Deployment的Pod模板(即spec.template)被更改时才会创建新的修订版本,例如更新模板标签或容器镜像。其他更新操作(如扩展副本数)将不会触发Deployment的更新操作,这也意味着我们将Deployment回滚到之前的版本时,只有Deployment的Pod模板部分会被修改。如果需要查看特定版本的详细信息,则可以加上–revision=参数:

现在我们决定撤销本次发布并回滚到上一个部署版本

kubernetes-pod的升级与回滚
当然,也可以使用–to-revision参数指定回滚到的部署版本号
kubernetes-pod的升级与回滚
使用kubectl rolling-update命令完成RC的滚动升级对于RC的滚动升级,Kubernetes还提供了一个kubectl rolling-update命令进行实现。该命令创建了一个新的RC,然后自动控制旧的RC中的Pod副本数量逐渐减少到0,同时新的RC中的Pod副本数量从0逐步增加到目标值,来完成Pod的升级。需要注意的是,系统要求新的RC与旧的RC都在相同的命名空间内。