工作负载
我们的应用以容器的形式在Kubernetes下的Pods中运行;但是,直接管理单个Pod的工作量将会非常繁琐(例如,一个Pod失败了,我们要挑选一个node节点再运行一个新的Pod来替换它,尤其是微服务框架下应用服务Pod数量非常大)。Kubernetes定义并实现了更高一层的抽象资源-工作负载。Kubernetes控制平面根据我们定义的工作负载对象规约自动管理Pod对象,其中最常见的就是Deployment。
Deployment用例
首先我们看下ReplicaSet这个控制器,它直接管理Pod,确保指定数量的Pod副本始终处于运行状态,当Pod终止时,ReplicaSet会自动创建新的Pod实例以保持预设的副本数量。
Deployment是一个更高级的抽象,提供了滚动更新和版本回滚功能,并且管理ReplicaSet。这意味着,我们可能永远不需要操作ReplicaSet对象,而是使用Deployment,并在spec部分定义我们的应用。
当我们创建一个Deployment对象,也就自动创建了一个或多个ReplicaSet对象,然后自动创建一个或多个Pod及需要的副本。
以下是一个Deployment的示例,我们看一下参数的定义:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: example
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
name: nginx-demo
namespace: example
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.1
ports:
- containerPort: 80
.spec.template就是上一节我们介绍的Pod定义的模版,被嵌套在Deployment模版中,这是常用做法(注意Pod被.spec.template.labels打上了标签,值为app: nginx);
.spec.selector字段定义创建的ReplicaSet如何查找要管理的Pod,这里通过.spec.selector.matchLabels匹配标签值为app: nginx的Pod;
.spec.replicas表示创建三个Pod副本(Pod副本根据默认规则和自定义的规则,可能分散在不同的node服务器上);
.metadata下是Deployment的一些基本定义:字段,名称,所在的命名空间,标签等;
同样的,我们执行kubectl命名kubectl apply -f deployment-demo.yaml就可以一键部署:
kubectl apply -f deployment-demo.yaml
每隔几秒执行一次kubectl get deployments -n example 可以看到副本数量的变化:
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 0/3 0 0 1sNAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 1/3 0 0 2sNAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 2/3 0 0 2sNAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 10s
再查看Deployment创建的ReplicaSet(rs),运行kubectl get rs -n example 查看输出:
NAME DESIRED CURRENT READY AGE
nginx-deployment-25455e5167 3 3 3 15s
最后运行kubectl get pods -n example查看Pod的状态:
NAME READY STATUS RESTARTS AGE
nginx-deployment-25455e5167-1ac7i 1/1 Running 0 21s
nginx-deployment-25455e5167-kssfz 1/1 Running 0 21s
nginx-deployment-25455e5167-qmczt 1/1 Running 0 21s
从带哈希字符串的名称,也可以看到他们的关联关系:
nginx-deployment > nginx-deployment-25455e5167 > nginx-deployment-25455e5167-1ac7i
Deployment更新
常用操作
当Deployment Pod模板(即 .spec.template)发生改变时(例如模板的标签或容器镜像被更新),会触发Deployment上线动作。其他更改(例如对Deployment执行扩缩容的操作)不会触发上线动作。
我们以应用容器镜像版本为例(从nginx:1.14.1升级为nginx:1.14.2),介绍更新Deployment的几种方法如下:
1.编辑deployment-demo.yaml的内容,然后执行kubectl apply -f deployment-demo.yaml
2.执行命令行 kubectl set image deployment/nginx-deployment nginx=nginx:1.14.2
3.执行命令行 kubectl edit deployment/nginx-deployment 然后编辑内容,保存
然后我们执行kubectl rollout status deployment/nginx-deployment查看上线状态,输出类似:
Waiting for rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment "nginx-deployment" successfully rolled out
滚动更新机制
Deployment确保在更新时仅关闭一定数量的Pod。默认情况下,它确保至少所需Pod的75%处于运行状态(最大不可用比例为25%),新版本的默认值有调整。
更新Deployment时,Kubernetes创建了一个新的ReplicaSet(nginx-deployment-25455a9568),并将其扩容为1,等待其就绪;然后将旧ReplicaSet缩容到 2, 将新的ReplicaSet扩容到2以便至少有3个Pod可用且最多创建4个Pod。然后,使用相同的滚动更新策略继续对新的ReplicaSet扩容并对旧的ReplicaSet缩容。 最后,有3个可用的副本在新的ReplicaSet中,旧ReplicaSet将缩容到0。
Rollover机制
实际使用过程中,可能出现的情况:当Deployment正在上线时又被更新。这种情况下,Deployment会针对更新创建一个新的ReplicaSet并开始对其扩容,之前正在被扩容的ReplicaSet会被Rollover,添加到旧ReplicaSet列表并开始缩容。
例如,我正在更新一个Deployment以生成nginx:1.14.2的5个副本,但又有一个小伙伴更新Deployment要创建5个nginx:1.16.1的副本,此时只有3个nginx:1.14.2的副本已被创建。这种情况下,Deployment会立即开始杀死3个nginx:1.14.2的Pod,并开始创建nginx:1.16.1的Pod。它不会等待nginx:1.14.2的5个副本都创建完成后才开始执行变更动作。
回滚Rolling Back
当我们部署的新版本出现问题,暂时无法修复的情况下,可以执行回滚操作(默认情况下,Deployment的所有上线版本记录都保留在系统中,以便可以随时回滚)。同样的,当Deployment Pod模板(即 .spec.template)发生改变时,Kubernetes才会记录新修订版本,Deployment的扩缩容操作不会创建Deployment上线版本。
首先检查历史版本:
kubectl rollout history deployment/nginx-deployment
输出结果类似:
deployments "nginx-deployment"
REVISION CHANGE-CAUSE
1 kubectl apply --filename=deployment-demo.yaml
2 kubectl set image deployment/nginx-deployment nginx=nginx:1.14.1
3 kubectl set image deployment/nginx-deployment nginx=nginx:1.14.2
4 kubectl set image deployment/nginx-deployment nginx=nginx:heiheihei
确认想要回滚的版本详细信息(可选)
kubectl rollout history deployment/nginx-deployment --revision=3
输出结果类似:
deployments "nginx-deployment" revision 3
Labels: app=nginx
pod-template-hash=25455a9568
Annotations: kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.14.2
Containers:
nginx:
Image: nginx:1.14.2
Port: 80/TCP
QoS Tier:
cpu: BestEffort
memory: BestEffort
Environment Variables: <none>
No volumes.
执行回滚操作:
kubectl rollout undo deployment/nginx-deployment --to-revision=3
最后执行必要的检查。
扩缩容
关于扩缩容,会在HPA资源介绍的时候详细说明,下面通过命令行方式简单看一下:
手动扩缩容,副本数的目标是10个:
kubectl scale deployment/nginx-deployment --replicas=10
自动扩缩容,pod的cpu使用率不超过80%的情况下,自动保持副本数在10到15之间
kubectl autoscale deployment/nginx-deployment --min=10 --max=15 --cpu-percent=80
暂停、恢复Deployment上线
有时我们在上线时需要暂停更新,以便执行一些程序修补等,避免触发一些不必要的操作,执行如下命令即可:
暂停
kubectl rollout pause deployment/nginx-deployment
恢复
kubectl rollout resume deployment/nginx-deployment
注意,暂停不是把Deployment和Pod的功能暂停了,应用是正常对外提供服务的,暂停的只是更新操作。
金丝雀部署Canary Deployment
金丝雀部署是逐步发布新版本应用程序的策略。它允许我们在将新版本完全替换旧版本之前,先将新版本部署到一小部分用户,以便在真实环境中测试其稳定性和性能。
而Kubernetes的Deployment更新默认是RollingUpdate滚动升级,或者手动修改为Recreate(在创建新 Pod 之前,所有现有的 Pod 会被杀死),更新策略都是针对副本数量的并且自动化的,无法满足金丝雀部署的需求。
常用的方法举例:
1.准备新的Deployment,其中Pod标签labels也设置为相同的值,例如本文中的app: nginx,容器镜像版本是新版本nginx:1.8.0,副本数.spec.replicas是1;
2.前端有个Service的selector是app: nginx,所以经过这个Service网络流量会有一小部分转发到新的Pod上,大部分流量还是在旧的Pod上;
3.观察业务新版本的稳定性和性能,新的Deployment的副本数+1,旧的Deployment的副本数-1;
4.重复步骤3,直到新版本完全替换旧版本;