在上篇我们讲到了较为傻瓜初级的弹性伸缩和滚动更新,那么接下来我们来看看较为高级的智能的滚动更新。本节的知识点呢是K8S的liveness和readiness探测,也就是说利用健康检查来做更为智能化的弹性扩容和滚动更新。
那为什么说是比较智能化呢,因为在实际生产环境中会遇到这样那样的问题,比如:容器里面应用挂了或者说新启动的容器里面应用还没有就绪等等,所以说就需要进行探测来检验容器是否满足需求。
那么一般的检测分为几种,比如:进程检测、业务检测。
进程检测呢很好理解,也就是说通过检测容器进程来验证容器内应用是否存活。Kubelet会定期通过Docker Daemon获取所有Docker进程的运行情况,如果发现某个Docker容器未正常运行,则重新启动该容器进程。目前,进程级的健康检查都是默认启用的。
业务检测呢也好理解,有些人会问,有了进程检测不就挺好的么,为什么要进行业务检测? 因为在很多实际场景下,仅仅使用进程级健康检查还远远不够。有时,从Docker的角度来看,容器进程依旧在运行;但是如果从应用程序的角度来看,假设应用代码处于死锁状态的话,那每次调度到这个容器的时候永远都无法正常响应用户的业务。比如对于使用java web服务的应用来说,并不是简单地说tomcat启动成功就可以对外提供服务的,还需要等待spring容器初始化,数据库连接连接上等等。
为了解决以上问题,Kubernetes引人了一个在容器内执行的活性探针(liveness probe)的概念,以支持用户自己实现应用业务级的健康检查。这些检查项由Kubelet代为执行,以确保用户的应用程序正确运转,至于什么样的状态才算“正确”,则由用户自己定义。Kubernetes支持3种类型的应用健康检查动作,分别为HTTP Get、Container Exec和TCP Socket。个人感觉exec的方式还是最通用的,因为不是每个服务都有http服务,但每个服务都可以在自己内部定义健康检查的job,定期执行,然后将检查结果保存到一个特定的文件中,外部探针就不断的查看这个健康文件就OK了。
介绍完活性探针(liveness probe)之后我们来看看就绪探针(readiness probe),就绪探针是来确定容器是否已经就绪可以接受访问,只有当Pod中的容器都处于就绪状态时kubelet才会认定该Pod处于就绪状态,至于什么样的状态才算 ”就绪”,还是由用户自己定义。该状态的作用就是控制哪些Pod可以作为service的后端,如果Pod处于非就绪状态,那么它们将会被从service的load balancer中移除。
介绍到此处是不是觉得我们的弹性伸缩和滚动更新如果加上刚才介绍的 ”两针神器”就会变得更加智能化了。那下面我们来看看这两个探针如何在应用到弹性伸缩和滚动更新上。
开始之前可以先看看进程探测,我们基于busybox镜像创建一个Pod,下面是yml文件。
该配置文件给Pod配置了一个容器。periodSeconds 规定kubelet要每隔5秒执行一次liveness probe。 initialDelaySeconds 告诉kubelet在第一次执行probe之前要的等待5秒钟。探针检测命令是在容器中执行 cat /tmp/healthy 命令。如果命令执行成功,将返回0,kubelet就会认为该容器是活着的并且很健康。如果返回非0值,kubelet就会杀掉这个容器并重启它。
可以看到,日志显示/tmp/healthy不存在,探测失败所以容器重启
OK,那下面来进行业务探测的场景,比如:弹性伸缩,因为在实际场景中我们由于业务的需求可能需要临时扩容新建N个容器,那么这个时候就需要业务探测来检查哪个容器就没就绪,如果就绪的话就把它作为service的后端,下面的yml文件
OK,可以看到我的测试失败了,因为nginx里面没有/healthz,所以探测反馈404,证明我的业务现在还没就绪所以就没把它加入到service后端。
该配置文件定义了一个容器,readinessProbe 指定kubelete需要每隔5秒执行一次readiness Probe。initialDelaySeconds 指定kubelet在该执行第一次探测之前需要等待10秒钟。该探针将向容器中的server的80端口发送一个HTTP GET请求。如果server的/healthz路径的handler返回一个成功的返回码,kubelet就会认定该容器是活着的并且很健康。如果返回失败的返回码,kubelet将杀掉该容器并重启它。
注意:任何大于200小于400的返回码都会认定是成功的返回码。其他返回码都会被认为是失败的返回码。
OK,下面来看看滚动更新,因为在实际场景中程序应用肯定避免不了进行更新,我们在上一篇文章中讲述了简单的傻瓜式滚动更新,下面我们来看看较为智能化高级的滚动更新,也就是加上了业务探测,依旧是v1、v2的例子,下面是yml文件。
这里模拟的是一个失败的滚动更新,在我们的设定中,新副本始终都无法通过Readiness探测,可以看到我在上面新建pod的时候在容器里面新建了一个目录,但是过一会就删除了,所以说V2我在进行滚动升级的时候失败了。不过幸运的是健康检查帮我们屏蔽了有问题的副本,同时也保留了原有的副本,业务并没有因为更新失败而受到影响。
OK,那既然更新失败了,那就根据上篇文章所学的,我们执行下回滚,利用kubectl rollout undo
最后注意下,滚动更新是可以在yml文件里面通过参数maxSurge和maxUnavailable来控制副本替换的数量,本文参考了Kubernetes官网和每天5分钟玩转K8S。