learn-tech/专栏/Kubernetes入门实战课/18Deployment:让应用永不宕机.md
2024-10-16 06:37:41 +08:00

14 KiB
Raw Blame History

                        因收到Google相关通知网站将会择期关闭。相关通知内容
                        
                        
                        18 Deployment让应用永不宕机
                        你好我是Chrono。

在上一节课里我们使用kubeadm搭建了一个由两个节点组成的小型Kubernetes集群比起单机的minikube它更接近真实环境在这里面做实验我们今后也更容易过渡到生产系统。

有了这个Kubernetes环境接下来我们就在“初级篇”里学习的Pod知识基础上深入研究一些由Pod衍生出来的其他API对象。

今天要看的API对象名字叫“Deployment”顾名思义它是专门用来部署应用程序的能够让应用永不宕机多用来发布无状态的应用是Kubernetes里最常用也是最有用的一个对象。

为什么要有Deployment

在[第13讲]里我们学习了API对象Job和CronJob它们代表了生产环境中的离线业务通过对Pod的包装向Pod添加控制字段实现了基于Pod运行临时任务和定时任务的功能。

那么除了“离线业务”另一大类业务——也就是“在线业务”在Kubernetes里应该如何处理呢

我们先看看用Pod是否就足够了。因为它在YAML里使用“containers”就可以任意编排容器而且还有一个“restartPolicy”字段默认值就是 Always可以监控Pod里容器的状态一旦发生异常就会自动重启容器。

不过“restartPolicy”只能保证容器正常工作。不知你有没有想到如果容器之外的Pod出错了该怎么办呢比如说有人不小心用 kubectl delete 误删了Pod或者Pod运行的节点发生了断电故障那么Pod就会在集群里彻底消失对容器的控制也就无从谈起了。

还有我们也都知道在线业务远不是单纯启动一个Pod这么简单还有多实例、高可用、版本更新等许多复杂的操作。比如最简单的多实例需求为了提高系统的服务能力应对突发的流量和压力我们需要创建多个应用的副本还要即时监控它们的状态。如果还是只使用Pod那就会又走回手工管理的老路没有利用好Kubernetes自动化运维的优势。

其实解决的办法也很简单因为Kubernetes已经给我们提供了处理这种问题的思路就是“单一职责”和“对象组合”。既然Pod管理不了自己那么我们就再创建一个新的对象由它来管理Pod采用和Job/CronJob一样的形式——“对象套对象”。

这个用来管理Pod实现在线业务应用的新API对象就是Deployment。

如何使用YAML描述Deployment

我们先用命令 kubectl api-resources 来看看Deployment的基本信息

kubectl api-resources

NAME SHORTNAMES APIVERSION NAMESPACED KIND deployments deploy apps/v1 true Deployment

从它的输出信息里可以知道Deployment的简称是“deploy”它的apiVersion是“apps/v1”kind是“Deployment”。

所以依据前面学习Pod、Job的经验你就应该知道Deployment的YAML文件头该怎么写了

apiVersion: apps/v1 kind: Deployment metadata: name: xxx-dep

当然了,我们还是可以使用命令 kubectl create 来创建Deployment的YAML样板免去反复手工输入的麻烦。

创建Deployment样板的方式和Job也差不多先指定类型是Deployment简写deploy然后是它的名字再用 --image 参数指定镜像名字。

比如下面的这条命令,我就创建了一个名字叫 ngx-dep 的对象,使用的镜像是 nginx:alpine

export out="--dry-run=client -o yaml" kubectl create deploy ngx-dep --image=nginx:alpine $out

得到的Deployment样板大概是下面的这个样子

apiVersion: apps/v1 kind: Deployment metadata: labels: app: ngx-dep name: ngx-dep

spec: replicas: 2 selector: matchLabels: app: ngx-dep

template: metadata: labels: app: ngx-dep spec: containers: - image: nginx:alpine name: nginx

把它和Job/CronJob对比一下你会发现有相似也有不同。相似的地方是都有“spec”“template”字段“template”字段里也是一个Pod不同的地方在于它的“spec”部分多了 replicas、selector 这两个新字段聪明的你应该会猜到这或许就会是Deployment特殊能力的根本。

没错这两个新字段就是Deployment实现多实例、高可用等功能的关键所在。

Deployment的关键字段

先看 replicas 字段。它的含义比较简单明了就是“副本数量”的意思也就是说指定要在Kubernetes集群里运行多少个Pod实例。

有了这个字段就相当于为Kubernetes明确了应用部署的“期望状态”Deployment对象就可以扮演运维监控人员的角色自动地在集群里调整Pod的数量。

比如Deployment对象刚创建出来的时候Pod数量肯定是0那么它就会根据YAML文件里的Pod模板逐个创建出要求数量的Pod。

接下来Kubernetes还会持续地监控Pod的运行状态万一有Pod发生意外消失了数量不满足“期望状态”它就会通过apiserver、scheduler等核心组件去选择新的节点创建出新的Pod直至数量与“期望状态”一致。

这里面的工作流程很复杂,但对于我们这些外部用户来说,设置起来却是非常简单,只需要一个 replicas 字段就搞定了,不需要再用人工监控管理,整个过程完全自动化。

下面我们再来看另一个关键字段 selector它的作用是“筛选”出要被Deployment管理的Pod对象下属字段“matchLabels”定义了Pod对象应该携带的label它必须和“template”里Pod定义的“labels”完全相同否则Deployment就会找不到要控制的Pod对象apiserver也会告诉你YAML格式校验错误无法创建。

这个 selector 字段的用法初看起来好像是有点多余为了保证Deployment成功创建我们必须在YAML里把label重复写两次一次是在“selector.matchLabels”另一次是在“template.matadata”。像在这里你就要在这两个地方连续写 app: ngx-dep

... spec: replicas: 2 selector: matchLabels: app: ngx-dep

template: metadata: labels: app: ngx-dep ...

你也许会产生疑问为什么要这么麻烦为什么不能像Job对象一样直接用“template”里定义好的Pod就行了呢

这是因为在线业务和离线业务的应用场景差异很大。离线业务中的Pod基本上是一次性的只与这个业务有关紧紧地绑定在Job对象里一般不会被其他对象所使用。

而在线业务就要复杂得多了因为Pod永远在线除了要在Deployment里部署运行还可能会被其他的API对象引用来管理比如负责负载均衡的Service对象。

所以Deployment和Pod实际上是一种松散的组合关系Deployment实际上并不“持有”Pod对象它只是帮助Pod对象能够有足够的副本数量运行仅此而已。如果像Job那样把Pod在模板里“写死”那么其他的对象再想要去管理这些Pod就无能为力了。

好明白了这一点那我们该用什么方式来描述Deployment和Pod的组合关系呢

Kubernetes采用的是这种“贴标签”的方式通过在API对象的“metadata”元信息里加各种标签labels我们就可以使用类似关系数据库里查询语句的方式筛选出具有特定标识的那些对象。通过标签这种设计Kubernetes就解除了Deployment和模板里Pod的强绑定把组合关系变成了“弱引用”。

虽然话是这么说但对于很多Kubernetes的初学者来说理解Deployment里的spec定义还是一个难点。

所以我还是画了一张图用不同的颜色来区分Deployment YAML里的字段并且用虚线特别标记了 matchLabels 和 labels 之间的联系希望能够帮助你理解Deployment与被它管理的Pod的组合关系。

如何使用kubectl操作Deployment

把Deployment的YAML写好之后我们就可以用 kubectl apply 来创建对象了:

kubectl apply -f deploy.yml

要查看Deployment的状态仍然是用 kubectl get 命令:

kubectl get deploy

它显示的信息都很重要:

READY表示运行的Pod数量前面的数字是当前数量后面的数字是期望数量所以“2/2”的意思就是要求有两个Pod运行现在已经启动了两个Pod。 UP-TO-DATE指的是当前已经更新到最新状态的Pod数量。因为如果要部署的Pod数量很多或者Pod启动比较慢Deployment完全生效需要一个过程UP-TO-DATE就表示现在有多少个Pod已经完成了部署达成了模板里的“期望状态”。 AVAILABLE要比READY、UP-TO-DATE更进一步不仅要求已经运行还必须是健康状态能够正常对外提供服务它才是我们最关心的Deployment指标。 最后一个AGE就简单了表示Deployment从创建到现在所经过的时间也就是运行的时间。

因为Deployment管理的是Pod我们最终用的也是Pod所以还需要用 kubectl get pod 命令来看看Pod的状态

kubectl get pod

从截图里你可以看到被Deployment管理的Pod自动带上了名字命名的规则是Deployment的名字加上两串随机数其实是Pod模板的Hash值

到现在对象创建成功Deployment和Pod的状态也都没问题可以正常服务我们是时候检验一下Deployment部署的效果了看看是否如前面所说的Deployment部署的应用真的可以做到“永不宕机”

来尝试一下吧,让我们用 kubectl delete 删除一个Pod模拟一下Pod发生故障的情景

kubectl delete pod ngx-dep-6796688696-jm6tt

然后再查看Pod的状态

kubectl get pod

你就会“惊喜”地发现被删除的Pod确实是消失了但Kubernetes在Deployment的管理之下很快又创建出了一个新的Pod保证了应用实例的数量始终是我们在YAML里定义的数量。

这就证明Deployment确实实现了它预定的目标能够让应用“永远在线”“永不宕机”。

在Deployment部署成功之后你还可以随时调整Pod的数量实现所谓的“应用伸缩”。这项工作在Kubernetes出现之前对于运维来说是一件很困难的事情而现在由于有了Deployment就变得轻而易举了。

kubectl scale 是专门用于实现“扩容”和“缩容”的命令,你只要用参数 --replicas 指定需要的副本数量Kubernetes就会自动增加或者删除Pod让最终的Pod数量达到“期望状态”。

比如下面的这条命令就把Nginx应用扩容到了5个

kubectl scale --replicas=5 deploy ngx-dep

但要注意, kubectl scale 是命令式操作扩容和缩容只是临时的措施如果应用需要长时间保持一个确定的Pod数量最好还是编辑Deployment的YAML文件改动“replicas”再以声明式的 kubectl apply 修改对象的状态。

因为Deployment使用了 selector 字段这里我就顺便提一下Kubernetes里 labels 字段的使用方法吧。

之前我们通过 labels 为对象“贴”了各种“标签”,在使用 kubectl get 命令的时候,加上参数 -l使用 ==、!=、in、notin 的表达式,就能够很容易地用“标签”筛选、过滤出所要查找的对象(有点类似社交媒体的 #tag 功能效果和Deployment里的 selector 字段是一样的。

看两个例子第一条命令找出“app”标签是 nginx 的所有Pod第二条命令找出“app”标签是 ngx、nginx、ngx-dep 的所有Pod

kubectl get pod -l app=nginx kubectl get pod -l 'app in (ngx, nginx, ngx-dep)'

小结

好了今天我们学习了Kubernetes里的一个重要的对象Deployment它表示的是在线业务和Job/CronJob的结构类似也包装了Pod对象通过添加额外的控制功能实现了应用永不宕机你也可以再对比一下[第13讲]来加深对它的理解。

我再简单小结一下今天的内容:

Pod只能管理容器不能管理自身所以就出现了Deployment由它来管理Pod。 Deployment里有三个关键字段其中的template和Job一样定义了要运行的Pod模板。 replicas字段定义了Pod的“期望数量”Kubernetes会自动维护Pod数量到正常水平。 selector字段定义了基于labels筛选Pod的规则它必须与template里Pod的labels一致。 创建Deployment使用命令 kubectl apply应用的扩容、缩容使用命令 kubectl scale。

学了Deployment这个API对象我们今后就不应该再使用“裸Pod”了。即使我们只运行一个Pod也要以Deployment的方式来创建它虽然它的 replicas 字段值是1但Deployment会保证应用永远在线。

另外作为Kubernetes里最常用的对象Deployment的本事还不止这些它还支持滚动更新、版本回退自动伸缩等高级功能这些在“高级篇”里我们再详细学习。

课下作业

最后是课下作业时间,给你留两个思考题:

如果把Deployment里的 replicas 字段设置成0会有什么效果有什么意义呢 你觉得Deployment能够应用在哪些场景里有没有什么缺点或者不足呢

欢迎在留言区分享你的想法。

这一章我们学习的Kubernetes高级对象对云计算、集群管理非常重要。多多思考打好基础我们继续深入。下节课再见。