Deployment
1. Update Applications Running in Pods
假设cluster中有一个ReplicationController或ReplicaSet管理多个Pods, 一个Service负责为Pods提供通信接口. 当Pod需要从v1版本提升到v2版本时, 有两个方法实现:
- 删除所有已有的v1 Pods, 再部署v2 Pods
- 启动一个v2 Pod, 并删除一个v1 Pod; 以此往复即可将所有Pod从v1修改为v2
上述两种方法都有其优点和缺陷:
- 全部删除后再部署: 虽然操作简单, 不会占用过多资源; 但Pod所提供的服务会出现空档期: 以v1 Pod被关闭为起点, 以v2 Pod上线为终点
- 新旧版本交替部署: 虽然Pod提供的服务不会产生中断, 但操作复杂, 新旧版本Pod可能会导致数据相互覆盖
1.1 Delete Old Pods and Replace with New Ones
假设cluster中有一个Replication管理三个版本为v1的Pods. 若需将Pod版本提升到v2, 可直接替换ReplicationController的template文件(只修改为v2 container image, 不修改label selector), 然后手动关闭所有v1 Pods, ReplicationController检测到replica count不足后会自动产生新的v2 Pod. 流程如下:
1.2 Spin up New Pods and Delete the Old Ones
若不想出现服务的空档期, 可在生成v2 Pod的同时保留v1 Pod, 分为两种方案:
Blue-green Deployment
创建新的ReplicationController(使用v2 container image和新的label selector)从而自动创建v2 Pods, 将service重定向到v2 Pods后删除旧版ReplicationController和其管理的v1 Pods. 流程如下:Rolling Update
上述做法虽然简单有效且避免了空档期, 但更新时由于Pod数量翻倍, 所以硬件的压力也增大. 为避免资源负担过重, 不能直接生成所有v2 Pods, 需要边生成v2 Pod, 边删除v1 Pod; 缺点在于操作繁琐, 极易产生错误. 流程如下:
2. Perform an Automatic Rolling Update
相对于手动更新ReplicationController中的每个Pod, Kubernetes提供了自动rolling update功能.
2.1 Preparation
首先需要确定Pod中container所运行的程序: 假设container中运行名为kubia的NodeJS app监听8080端口, v1版本kubia接收到HTTP请求后回复"This is v1 running in pod", v2版本kubia接收到HTTP请求后回复"This is v2 running in pod". 创建含有v1 Pod的 ReplicationController:
apiVersion: v1 |
创建Pod对应的Service:
apiVersion: v1 |
测试v1 Pod:
$ curl curl http://130.211.109.222 |
2.2 Perform a Rolling Update with kubectl
调用kubectl rolling-update可实现自动滚动更新Pod:
$ kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2 |
上述命令会保留kubia-v1的ReplicationController, 并创建新的ReplicationController, 名为kubia-v2, 其中Pod使用luksa/kubia:v2 image. 为避免kubia-v2覆盖kubia-v1, Kubernetes会为两个ReplicationController添加名为deployment的selector label, kubia-v1所管理的Pod也会被添加对应的label. 以下是刚调用命令后的情况:
刚开始时kubia-v2的replica count被设置为0, 因此不会创建任何v2 Pod, 这是为了防止创建大量Pod而导致的资源不足. 之后Kubernetes会逐步增大kubia-v2的replica count, 并同时缩小kubia-v1的replica count, 从而达到v2 Pod逐步替代v1 Pod的目标:
最终kubia-v2的replica count会增加到3, 而kubia-v1的replica count会减少到0. 但这种rolling update的方式已经被遗弃, 缺点有以下两点:
- 调用kubectl时, kubectl会向API server发送多条HTTP请求来完成rolling update. 一旦client与master中途断开连接, 则更新中断且无法回滚
- 该方法会向Pod和ReplicationController添加不必要的label
3. Use Deployment for Updating Apps
Deployment作为Kubernetes的一种Resource, 比ReplicationController和ReplicaSet维度更高. Deployment并不直接管理Pod, 而是通过创建一个ReplicaSet来实现管理. 因此用户不必手动操作ReplicaSet, deployment提供了完整的rolling update方案.
3.1 Create a Deployment
Deployment的创建与ReplicationController类似:
apiVersion: apps/v1beta1 |
Deployment的Service和ReplicationController相同:
apiVersion: v1 |
调用kubectl create即可创建Deployment, 其中--record目的为记录kubia的每一次更新, 用于回滚操作:
$ kubectl create -f kubia-deployment-v1.yaml --record |
可调用kubectl rollout status查看deployment的rollout状态:
$ kubectl rollout status deployment kubia |
Deployment会自动创建Pod和ReplicaSet:
$ kubectl get po |
3.2 Update a Deployment
更新Deployment不需要指定新的ReplicaSet名字, 也不需要在乎ReplicaSet的label和replica count. Deployment支持两种更新策略:
- RollingUpdate(默认值): 滚动更新, 边创建新的Pod, 边删除旧的Pod
- Recreate: 先删除所有Pod, 再创建新的Pod
调用kubectl set image可直接替换Deployment中Pod的image:
$ kubectl set image deployment kubia nodejs=luksa/kubia:v2 |
注意: Deployment不会删除旧版本的ReplicaSet, 保留其用于回滚:
$ kubectl get rs |
以下是修改Deployment的几种方式:
- kubectl edit: 使用editor打开Deployment manifest, 修改后保存并推出editor
- kubectl patch: 修改Deployment中的一个或多个属性
- kubectl apply: 使用JSON或YAML文件替换或创建Deployment. 若存在同名Deployment, 则替换; 否则创建新的Deployment
- kubectl replace: 使用JSON或YAML文件替换Deployment. 若不存在同名Deployment, 则报错
- kubectl set image: 修改Deployment中的container image
3.3 Roll back a Deployment
首先创建一个需要回滚的情景: 假设v3 Pod中NodeJS app的前5个HTTP请求返回200 status code, 第6个HTTP请求开始返回500 status code. 调用kubectl set image更新Deployment来部署v3 Pod:
$ kubectl set image deployment kubia nodejs=luksa/kubia:v3 |
调用kubectl rollout undo可回滚至上一个版本:
$ kubectl rollout undo deployment kubia |
之所以Deployment能实现回滚, 因为Deployment会在每次更新时记录并保留ReplicaSet. 即便新的Pod还未部署完毕, 也可回滚至前一个版本. 调用kubectl rollout history可查看所有revision history:
$ kubectl rollout history deployment kubia |
除了回滚到之前一个版本, 还可以标注--to-revision来指明回滚的版本, 例如:
$ kubectl rollout undo deployment kubia --to-revision=1 |
以下是Deployment中ReplicaSet的保存记录:
为防止revision history过长而导致大量旧版replicaSet占用资源, 可设置Deployment的revisionHistoryLimit属性来限制保留的ReplicaSet数量, 默认为10.
3.4 Control the Rate of the Rollout
Deployment提供了两个属性值来控制Deployment更新时资源占用情况:
- maxSurge: 超出desired replica count的Pod个数或比例
- maxUnavailable: 不可用的Pod数量或比例(相对于desired replica count来说)
... |
假设Deployment中ReplicaSet的replica count为3, maxSurge为1, maxUnavailable为0, 过程如下:
假设Deployment中ReplicaSet的replica count为3, maxSurge为1, maxUnavailable为1, 过程如下:
注意: 不可将maxSurge和maxUnavailable都设置为0.
3.5 Pause the Rollout Process
调用kubectl rollout pause可暂停Pod的更新:
$ kubectl set image deployment kubia nodejs=luksa/kubia:v4 |
调用kubectl rollout resume可恢复更新进程:
$ kubectl rollout resume deployment kubia |
注意: 若Deployment被暂停, 则无法回滚该Deployment; 必须恢复运行后才可以回滚.
3.6 Block Rollouts of Bad Versions
Deployment还提供minReadySeconds属性来防止有缺陷的Pod被部署. minReadySeconds会在每个Pod部署之间设置等待时长, 只有上一个Pod处于Ready状态超过一定时间才会创建下一个Pod. 虽然这会导致每次rollout变慢, 但可有效防止意外情况发生.
minReadySeconds通常需要配合Readiness一起使用: 首先需在cluster中创建一个Deployment, Pod中运行的NodeJS app没有任何问题:
apiVersion: apps/v1beta1 |
每个Pod中的container包含一个readiness, 每隔一秒会向8080端口发出HTTP请求. 之后再将Deployment从v2更新至v3: v3版本NodeJS app的前五次HTTP请求返回200 status code, 第六次及之后返回500 status code:
$ kubectl set image deployment kubia nodejs=luksa/kubia:v3 |
v3 Pod(kubia-7ws0i)虽然被创建, 但其状态无法变成Ready. 由于minReadySeconds为10秒, 所以Deployment在创建kubia-7ws0i后至少等待10秒, 且该Pod状态为Ready时才可以创建下一个v3 Pod; 但由于Readiness第六次发送HTTP请求后得到错误回复, 所以kubia-7ws0i的状态永远无法变为Ready, Deployment的rollout update也搁浅:
默认情况下, 若rollout在10分钟内没有完成, 则自动标记为fialed, 理由为ProgressDeadlineExceeded. 若想手动取消本次rollout, 可回滚至上一版本:
$ kubectl rollout undo deployment kubia |