kubernete编排技术二:deployment
作者 | 朱晋君
来源 | 君哥聊技术(ID:gh_1f109b82d301)
kubernete中的控制器模式,是指用一种对象来控制另一种对象,这个控制器是由组件kube-controller-manager来实现的。deployment就是一种控制器,用来控制pod的创建、水平扩展收缩、滚动更新。deployment是kubernete中一个对无状态pod的控制器,本文来介绍一下。
deployment简介
先回顾一下我们之前定义的yaml文件,这个文件定义了2个pod副本(replicas: 2),这个deployment的编排逻辑就是带app: springboot-mybatis这个label的数量只能有2个,超过就需要删除,少了就需要创建。
apiVersion: apps/v1
kind: Deployment
metadata:
name: springboot-mybatis-deployment
spec:
selector:
matchLabels:
app: springboot-mybatis
replicas: 2
template:
metadata:
labels:
app: springboot-mybatis
spec:
containers:
- name: spingboot-mybatis
imagePullPolicy: IfNotPresent
image: zjj2006forever/springboot-mybatis:1.2
ports:
- containerPort: 8300
上面yaml文件中的template,被称作pod模板,而template上面的代码部分,就是控制器的定义。这儿的控制器其实就是上面的控制器部分对下面pod模板的控制。创建时,控制器会从etcd查找label是springboot-mybatis的pod个数,因为是0,所以会创建2个;如果运行过程中,有一个pod挂了,会再创建一个。
pod水平扩展和收缩
上一节的yaml文件定义了一个2个pod的deployment控制器,这样会生成2个pod。在我们实际工作中,有时会遇到突增流量,比如促销活动之类的,这时候我们可以用增加pod来应对。最直接的方式就是修改replicas,比如改为replicas: 4。但是生成环境中,这样效率太低了,我们可以执行如下命令:
kubectl scale deployment springboot-mybatis-deployment --replicas=4
之后我们查看集群上的pod,这2个AGE=10s的pod就是后来扩展出来的。
[root@master k8s]# kubectl get pods
NAME READY STATUS RESTARTS AGE
springboot-mybatis-deployment-5b78f66997-d5fk9 1/1 Running 0 61s
springboot-mybatis-deployment-5b78f66997-hcmm4 1/1 Running 0 10s
springboot-mybatis-deployment-5b78f66997-k5xw8 1/1 Running 0 61s
springboot-mybatis-deployment-5b78f66997-pvfdv 1/1 Running 0 11s
我们查看其中一个新pod的状态,跟之前的pod创建过程完全一样。
[root@master k8s]# kubectl describe pod springboot-mybatis-deployment-5b78f66997-hcmm4
Name: springboot-mybatis-deployment-5b78f66997-hcmm4
Namespace: default
Priority: 0
Node: worker1/192.168.59.141
Start Time: Wed, 08 Jul 2020 06:01:08 -0400
Labels: app=springboot-mybatis
pod-template-hash=5b78f66997
Annotations: <none>
Status: Running
IP: 10.244.1.25
IPs:
IP: 10.244.1.25
Controlled By: ReplicaSet/springboot-mybatis-deployment-5b78f66997
Containers:
spingboot-mybatis:
Container ID: docker://ef433a219549b32e1d01899ea6ac581c1bfffa7d01e1c165647be52f9f79ff5b
Image: zjj2006forever/springboot-mybatis:1.2
Image ID: docker-pullable://zjj2006forever/springboot-mybatis@sha256:bf43bc9d1d4bdb33d82a206282c8f82be3679847d1011806b72e3634ebe67564
Port: 8300/TCP
Host Port: 0/TCP
State: Running
Started: Wed, 08 Jul 2020 06:01:11 -0400
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-qk5nc (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
default-token-qk5nc:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-qk5nc
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/springboot-mybatis-deployment-5b78f66997-hcmm4 to worker1
Normal Pulled 107s kubelet, worker1 Container image "zjj2006forever/springboot-mybatis:1.2" already present on machine
Normal Created 106s kubelet, worker1 Created container spingboot-mybatis
Normal Started 106s kubelet, worker1 Started container spingboot-mybatis
查看replicas
[root@master k8s]# kubectl get rs
NAME DESIRED CURRENT READY AGE
springboot-mybatis-deployment-5b78f66997 4 4 4 3m59s
检测一下应用运行正常
[root@worker1 ~]# curl http://10.244.1.24:18082/actuator/info
{"app":{"name":"springboot-mybatis","description":"two-datasource-test","version":"1.0","spring-boot-version":"2.1.6"}}
如果流量降下来了,我们可以收缩pod数量,执行命令
kubectl scale deployment nginx-deployment --replicas=2
查看pod数量,后来扩展的2个pod被删除了
[root@master k8s]# kubectl get pods
NAME READY STATUS RESTARTS AGE
springboot-mybatis-deployment-5b78f66997-d5fk9 1/1 Running 0 11m
springboot-mybatis-deployment-5b78f66997-k5xw8 1/1 Running 0 11m
可以看出,这种控制器模式,是由deployment控制ReplicaSet,ReplicaSet控制pod。
滚动更新
在实际开发工作中,升级服务上线是经常的事情。以本文中的应用为例,之前的deployment使用的镜像是zjj2006forever/springboot-mybatis:1.2,如果我们升级镜像版本到1.3,需要怎么操作呢?
注:镜像的升级版本我已经打好并且发布到dockerhub,感兴趣的同学自行拉取测试。
这个升级操作我们可以直接修改yaml文件,也可以执行如下命令进行修改,这个命令的原理是直接编辑etcd里面的springboot-mybatis-deployment控制器对象,可以跟编辑文件一样进行编辑
kubectl edit deployment/springboot-mybatis-deployment
编辑完成保存后,就会触发滚动更新。执行下面命令查看滚动更新的日志。
[root@master ~]# kubectl rollout status deployment/springboot-mybatis-deployment
Waiting for deployment "springboot-mybatis-deployment" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "springboot-mybatis-deployment" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "springboot-mybatis-deployment" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "springboot-mybatis-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "springboot-mybatis-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "springboot-mybatis-deployment" successfully rolled out
这时我们再次查看pod,发现新的pod已经创建出来了
[root@master k8s]# kubectl get pods
NAME READY STATUS RESTARTS AGE
springboot-mybatis-deployment-69787d89c4-8kplf 1/1 Running 0 30s
springboot-mybatis-deployment-69787d89c4-hv4zl 1/1 Running 0 3m11s
从下面springboot-mybatis-deployment的描述Events中可以看出滚动更新的过程
[root@master ~]# kubectl describe deployment springboot-mybatis-deployment
Name: springboot-mybatis-deployment
Namespace: default
CreationTimestamp: Wed, 08 Jul 2020 06:00:17 -0400
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 2
kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"springboot-mybatis-deployment","namespace":"default"},"sp...
Selector: app=springboot-mybatis
Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=springboot-mybatis
Containers:
spingboot-mybatis:
Image: zjj2006forever/springboot-mybatis:1.3
Port: 8300/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: springboot-mybatis-deployment-69787d89c4 (2/2 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 30m deployment-controller Scaled up replica set springboot-mybatis-deployment-5b78f66997 to 2
Normal ScalingReplicaSet 29m deployment-controller Scaled up replica set springboot-mybatis-deployment-5b78f66997 to 4
Normal ScalingReplicaSet 19m deployment-controller Scaled down replica set springboot-mybatis-deployment-5b78f66997 to 2
Normal ScalingReplicaSet 6m18s deployment-controller Scaled up replica set springboot-mybatis-deployment-69787d89c4 to 1
Normal ScalingReplicaSet 3m37s deployment-controller Scaled down replica set springboot-mybatis-deployment-5b78f66997 to 1
Normal ScalingReplicaSet 3m37s deployment-controller Scaled up replica set springboot-mybatis-deployment-69787d89c4 to 2
Normal ScalingReplicaSet 3m32s deployment-controller Scaled down replica set springboot-mybatis-deployment-5b78f66997 to 0
上面event中最后4行就是滚动更新的过程。很明显,滚动更新过程是创建一个新的pod,删除一个旧的pod,直到新的pod全部创建,旧的pod的全部删除。可以"滚动更新"这个词非常形象。滚动更新有如下优势:
1.新pod启动失败整个过程就会停止,剩下的旧pod依然可以提供服务。
2.为了保证服务不中断,deployment控制器会保证一次删除pod的比例和一次创建的pod的比例,默认都是yaml中replicas的25%。
3.这2个比例也可以通过参数来配置,看下面的yaml。maxSurge指每次滚动过程中可以创建多少个pod,maxUnavailable指一次更新中可以删除几个pod,这2个参数值也可以用百分数表示,指的是滚动过程中一次创建或删除的pod数量占replicas配置的pod的比例。
apiVersion: apps/v1
kind: Deployment
metadata:
name: springboot-mybatis-deployment
spec:
selector:
matchLabels:
app: springboot-mybatis
replicas: 2
template:
metadata:
labels:
app: springboot-mybatis
spec:
containers:
- name: spingboot-mybatis
imagePullPolicy: IfNotPresent
image: zjj2006forever/springboot-mybatis:1.3
ports:
- containerPort: 8300
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
失败回滚
如果我们升级应用的过程中,pod启动失败,这时候我们为了不中断服务,必须回滚。比如之前的yaml文件中,我们定义的springboot-mybatis镜像版本是1.3,如果我们改成1.4版本,dockerhub上并没有这个版本,拉取失败导致pod启动失败。修改yaml文件中镜像版本到1.4后,执行更新命令
kubectl apply -f springboot-mybatis.yaml
这时deployment就会触发滚动更新,但是这时因为镜像拉取失败,pod创建失败,如下结果中第4行
[root@master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
springboot-mybatis-deployment-5b78f66997 0 0 0 15h
springboot-mybatis-deployment-69787d89c4 2 2 2 14h
springboot-mybatis-deployment-7d6fc78b87 1 1 0 80s
我们再查看deployment的状态,如下,镜像版本是1.4,这时我们可以触发回滚操作。
[root@master ~]# kubectl describe deployment springboot-mybatis-deployment
Name: springboot-mybatis-deployment
Namespace: default
CreationTimestamp: Wed, 08 Jul 2020 06:00:17 -0400
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 3
kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"springboot-mybatis-deployment","namespace":"default"},"sp...
Selector: app=springboot-mybatis
Replicas: 2 desired | 1 updated | 3 total | 2 available | 1 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=springboot-mybatis
Containers:
spingboot-mybatis:
Image: zjj2006forever/springboot-mybatis:1.4
Port: 8300/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True ReplicaSetUpdated
OldReplicaSets: springboot-mybatis-deployment-69787d89c4 (2/2 replicas created)
NewReplicaSet: springboot-mybatis-deployment-7d6fc78b87 (1/1 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 14h deployment-controller Scaled up replica set springboot-mybatis-deployment-69787d89c4 to 1
Normal ScalingReplicaSet 14h deployment-controller Scaled down replica set springboot-mybatis-deployment-5b78f66997 to 1
Normal ScalingReplicaSet 14h deployment-controller Scaled up replica set springboot-mybatis-deployment-69787d89c4 to 2
Normal ScalingReplicaSet 14h deployment-controller Scaled down replica set springboot-mybatis-deployment-5b78f66997 to 0
Normal ScalingReplicaSet 3m3s deployment-controller Scaled up replica set springboot-mybatis-deployment-7d6fc78b87 to 1
下面命令可以查看deployment的升级历史
[root@master k8s]# kubectl rollout history deployment/springboot-mybatis-deployment
deployment.apps/springboot-mybatis-deployment
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 <none>
从上面的deployment的describe中我们可以看出目前revision是3(deployment.kubernetes.io/revision: 3),我们应该恢复到2,如下命令
kubectl rollout undo deployment/springboot-mybatis-deployment --to-revision=2
执行之后,再看deployment状态,镜像版本已经恢复到zjj2006forever/springboot-mybatis:1.3
[root@master k8s]# kubectl describe deployment
Name: springboot-mybatis-deployment
Namespace: default
CreationTimestamp: Wed, 08 Jul 2020 06:00:17 -0400
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 4
kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"springboot-mybatis-deployment","namespace":"default"},"sp...
Selector: app=springboot-mybatis
Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=springboot-mybatis
Containers:
spingboot-mybatis:
Image: zjj2006forever/springboot-mybatis:1.3
Port: 8300/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: springboot-mybatis-deployment-69787d89c4 (2/2 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 14h deployment-controller Scaled up replica set springboot-mybatis-deployment-69787d89c4 to 1
Normal ScalingReplicaSet 14h deployment-controller Scaled down replica set springboot-mybatis-deployment-5b78f66997 to 1
Normal ScalingReplicaSet 14h deployment-controller Scaled up replica set springboot-mybatis-deployment-69787d89c4 to 2
Normal ScalingReplicaSet 14h deployment-controller Scaled down replica set springboot-mybatis-deployment-5b78f66997 to 0
Normal ScalingReplicaSet 15m deployment-controller Scaled up replica set springboot-mybatis-deployment-7d6fc78b87 to 1
Normal ScalingReplicaSet 35s deployment-controller Scaled down replica set springboot-mybatis-deployment-7d6fc78b87 to 0
最后,从replicas的结果中我们可以看出,每次无论升级成功失败都会有一个replicas创建出来,为了限制保留的历史版本个数,可以通过参数spec.revisionHistoryLimit来进行设置。
[root@master k8s]# kubectl get rs
NAME DESIRED CURRENT READY AGE
springboot-mybatis-deployment-5b78f66997 0 0 0 15h
springboot-mybatis-deployment-69787d89c4 2 2 2 14h
springboot-mybatis-deployment-7d6fc78b87 0 0 0 14m
总结
deployment是kubernete中非常重要的一个编排技术,不过它有一个局限,只能编排无状态的pod,比如一个应用的集群,集群中的节点都是平等的,不存在主备关系。但是我们日常的开发中,好多pod是有状态的,比如一个部署在kubernete中的redis集群,会存在主从节点,这样启动的时候pod就存在顺序,deployment就不能满足了。