云计算时代操作系统Kubernetes之容器生命周期管理(中)

通过上篇文章的学习,希望读者对容器的生命周期有全面的了解,今天咱来实战一下,给我们熟悉的容器实例增加liveness probe,让它们更加完整。由于yunpan-ssl这个POD中的两个容器实例都是暴露的HTTP的访问端口,因此我们给这个POD中的容器增加HTTP类型的liveness probe就显得顺理成章了。

仔细分析我们前边演示用的yunpan-ssl这个pod,其中springcloud应用k8ssample没有定义任何健康检查的端点,而Envoy容器定义了供外部进行健康检查的端点,在实际的项目中,一般我们需要给应用定义健康检查的端点,并从外部来监控应用的健康状态。为了让后续的讨论顺利展开,我们先更新yunpan-ssl这个YAML文件,给它把健康检查的配置加上,组中的YAML如下图所示的yunpan-liveness.yaml:

《图1.1 给应用程序增加liveness probe配置》

如上图所示,我们给springcloud应用程序和Envoy代理容器分别配置了liveness probe,接下来我们详细介绍这两个liveness probe的配置信息。

【如何给容器配置Liveness Probe】

我们给yunpan这个容器配置了基于HTTP模式的健康检查,Kubernetes会定期给路径/v1/yunpan/k8s/hello发送HTTP GET请求来检查服务是否可访问,如果应用返回的HTTP状态码介于200和399之间,那么Kubernetes就认为应用程序运行健康。

图1.1中针对probe配置的选项不多,因此大部分我们没有明确指定的会使用默认值。应用容器启动后,第一个健康检查的HTTP GET请求会在10s后发送,并且之后每10秒重一次健康检查的探测请求。如果应用程序在1s内未返回,就认为此次健康检查失败,并且如果连续有三次失败,Kubernetes会认为这个容器不健康了,会终止这个容器的运行。

对于Envoy容器来说,管理接口提供了/ready接口来对外提供健康状态,具体来说,我们可以通过在管理端口9901上来访问这个健康状态接口。如笔者在图1.1中所示,我们甚至可以在配置liveness probe的时候,健康检查的目标端口号可以配置成端口的别名,而不是数字。

另外我们在除了配置HTTP路劲和端口号之外,我们还需要配置一些额外的字段,先看下图:

《图1.2 Liveness Probe的配置选项》

如上图所示,除了路径和端口号配置之外,还有initialDelaySeconds,periodSeconds,timeoutSeconds,failureThreshold四个额外的配置项。参数initialDelaySeconds定义了容器启动后Kubernetes多久之后进行第一次健康检查;periodSeconds参数设定两次连续的探测之间的间隔;timeoutSeconds的意思是健康探测请求多久未响应就被认为失败,而failureThreshold设置连续多少次失败,Kubernetes会认为容器运行失败,重新启动容器实例。

介绍完配置项之后,我们来实战一下,看看配置的liveness probe具体是如何工作的。首先我们需要在自己的集群中部署yunpan-liveness这个YAML文件,然后运行kubectl port-forward 8085 8443 9001,并且通过其他的窗口验证应用是否正常运行,在应用正常运行的情况下,请继续阅读。

容器实例配置的liveness probe会在容器启动和正常运行后不久便开始探测工作,由于我们的两个容器实例都很健康,因此每次健康检查的结构都是成功。这应该是我们部署到生产环境应用的常态。那么我们如何能找到健康探测运行的蛛丝马迹呢?在Kubernetes平台上,唯一能看到观察到liveness probe运行的在容器的日志中,我们的SpringCloud应用程序k8ssample会在每次处理完请求后,给控制台打印一条信息,因此探测请求也会被捕捉到。我们可以通过命令:kubectl logs kubia-liveness -c kubia -f来观察探测请求。

对于Envoy容器实例来说,健康检查请求发送给Envoy的管理接口,但是这个接口并未将请求信息打印到控制台,而是打印到了日志文件/var/log/envoy.admin.log日志文件中,我们可以通过命令:kubectl exec kubia-liveness -c envoy -- tail -f /var/log/envoy.admin.log来打印日志信息,从打印出的日志信息中观看健康检查请求是否在工作。如下图所示:

图1.3 通过Envoy容器的日志来验证健康检查是否工作

由于这些验证操作非常简单和直白,因此笔者就不占用太多的笔墨来解释了,接下来我们上主菜,看看当容器的状态不健康的时候,会发生什么。

【Liveness probe运行失败后Kubernetes的处理机制】

对于开发和运维同学来说,我们希望liveness probe的探测状态永远保持健康,这样我们就不会被写复盘材料,但是代码可能没有缺陷,架构设计不可能100%的容错,墨菲定律时刻主宰着我们周围的世界。我们配置健康检查,逻辑很严谨,每个选项都很合理,但是它真的工作吗?就如同大部分电商平台都会有应急预案,你知道应急预案最大的误区是什么吗?并不是应急预案写的不够完整,不够丰富,每一项应急预案的执行步骤不是不够清晰,监控指标不是不够准确,基于笔者多年的经验,这里最大的问题是:你真的验证过这些应急预案吗?或者反过来说,如果出现线上故障,这些个应急预案真的能执行吗?

因此在开发阶段,probe健康检查返回成功状态其实挺无趣的,我们来看看当probe失败的时候,我们宣称的机制是否能够奏效。由于我们的程序主要对外暴露的是HTTPS访问协议,因此我们来验证一下,如果Envoy容器出故障,健康监测返回fail,Kubernetes是不是会重启这个容器POD,从用户的角度,我们会观察到什么情况。

我们首先在新打开的窗口运行kubectl get events -w命令,然后我们需要通过Evnoy容器提供的管理接口来修改容器的ready接口返回成功或者失败,因此如果要让这个容器健康检查失败,只需要执行curl命令:curl -X POST localhost:9901/healthcheck/fail,当命令执行后,我们就会从event观察窗口看到warning事件,标识遇到的错误和HTTP状态码,如下图所示:

《图1.4 通过Envoy控制台force容器健康检查失败的Event事件》

由于我们把连续失败参数failureThreshold设置为3,因此一次健康检查失败是不足以让Kubernetes判定容器实例不健康,但是如果我们不及时将容器的健康状态重置会健康,那么就会在三次连续的健康检查失败之后,Kubernetes重启Envoy容器实例。我们可以通过命令curl -X POST localhost:9901/healthcheck/ok将容器的健康检察重置。

如果我们让Envoy容器的健康状态长时间保持失败,那么Kubernetes在三次连续的健康检查请求失败之后,会重启容器,我们在事件窗口可以看到如下的输出:

《图1.4 连续三次健康检查失败后,事件输出显示容器被重启》

如上图所示,我们在事件日志中看到容器重启的输出,而容器实例是否被重启这个事实,我们也可以通过kubectl get pod yunpan-lliveness的输出来验证,你可以在自己的机器上执行一下,重启次数会加1,在笔者的本地环境中,由于整个集群刚刚启动,因此RESTARTS列的值是1。

如果我们想进一步了解我们的主容器实例到底是正常运行结束还是被强制kill(比如健康探测三次连续失败,被强制重启了),我们可以获取POD对象的完整yaml文件来观察。请在自己的集群上执行kubectl describe pod yunpan-liveness,从输出的yaml文件中,找到envoy容器部分,我们可以从exitcode这个字段上,看到容器到底是正常退出还是被kill杀掉。具体来说,0代表正常的有序退出,而137代表的是被退出重启。

注:exitcode的规律是,128+n代表进程被外部的信号n杀掉,而137=128+9,因此当容器进程被强制重启的时候,使用的操作系统命令是KILL,而KILL的信号是9,这是我们运维Kubernetes集群会经常遇到的exitcode。另外一个经常遇到的是134,134=128+15,而15是TERM信号,当我们通过shell容器来登陆我们的应用容易的时候,这个shell容器的exitcode一般就是134,表示容器有序退出并重启。

接下来我们来检查Envoy容器实例的日志文件,看看是否如我们所说的那样。笔者在前边的文章中介绍过如何查看POD中有多个容器的情况下,某个容器日志的方法,具体来说是使用--container或者-c选项。我们期望在出现健康检查时,由于容器实例收到了TERM信号,因此日志中应该出现TERM信号的记录,请看下图:

《图1.5 容器健康检查失败后被重启,日志中捕捉到了SIGTERM》

如上图日志截图片段所示,Kubernetes在容器三次连续的健康检查失败后,会发送SIGTERM信号给容器进程,让容器进程可以优雅的退出,而不是强制通过kill来杀掉容器进程,这样可能造成数据不一致,或者业务数据丢失。当容器被重启后,健康检查被重置,因此我们可以看到容器的健康检查返回200这样的健康状态码。好了,相信通过上边这些内容的实践,你对给自己的应用配置liveness probe已经轻车熟路了。接下来,我们来看看如何使用TCP协议和exec类型的probe,在很多高性能应用部署场景下,会非常有用。

【如何使用tcpSocket和exec应用健康检查机制?】

如果我们的应用没有暴露HTTP健康检查接口,那么我们仍然可以使用tcpScoket或者exec类型的liveness健康检查机制,来监控我们的应用。当我们给应用配置了tcpSocket类型的健康检查之后,Kubernetes会按照配置的频率,试图和应用的指定端口建立TCP连接,如果连接可以建立,那么就认为应用是健康的,如果无法建立连接,那么就认为应用健康检查失败。如下图所示,笔者创建了创建了一个新的yaml文件,并配置了tcpSocket类型的liveness probe健康检查:

《图1.6 tcpSocket类型的健康检查》

如上图所示,Kubernetes建立连接的目标端口是1234,并且这个探测每2s运行一次,如果出现一次健康检查失败,那么Kubernetes就会认为这个容器不健康,进而发送SIGTERM(15)来让容器优雅退出并重启启动。接下来我们看看如何使用exec模式的健康检查liveness probe模式。

如果应用程序也没有暴露供外部系统健康检查的TCP端口,但是提供了一个可以在应用中执行的命令,通过执行这个命令和检查运行后的状态码,我们也可以知道应用的健康情况。对于这样的应用,我们就可以使用exec类型的liveness健康检查,如下图所示,命令会在容器中执行,因此这个命令必须已经安装在容器的文件系统中(或者命令的可执行文件被拷贝到容器的文件系统中)。

《图1.7 exec类型健康检查执行的命令运行在容器中》

如下是我们基于exec健康检查定义的POD的部署YAML文件:

《图1.8 exec类型的健康检查》

从上图可以看到,我们在容器中运行的命令是/usr/bin/healthcheck,这个命令每秒运行一次,并且这个命令必须在1秒内返回,要不然Kubernetes会认为这次健康检查失败。如果命令运行后返回的状态码为0,那么容器就认为是健康的,如果返回了非0的状态码,那么就认为不健康。我们配置failureThreshold=1,那么只要有一次健康检查失败,那么就Kubernetes就认为容器实例不健康,会发送SIGTERM(15)来让容器实例优雅退出并重亲启动。

默认情况下liveness probe给应用程序20-30秒的时候来进行启动,如果超过这个时间,那么可能会被liveness probe报告应用异常造成重新启动,如果重新启动还是需要较长时间,那么容器就会反复的启动,因为liveness probe没有任何机会得到成功的健康检查响应,因此应用就是反复的在启动,进入到无限的死循环。

聪明的你肯定想到解决方案了,既然应用需要较长的时间来启动,那么我们可以把这三个参数initialDelaySeconds,periodSeconds或者failureThreshold相应的进行调整,来应对应用需要较长时间启动的场景。但是你必须非常小心,因为软件架构没有一招鲜,我们增大periodSeconds * failureThreshold的结果就是应用真的出现异常后,需要等待很久的时间才会重启,可能会造成负面的影响。

因此通过调整这三个参数来解决应用启动需要较长时间这种做法,显然不可取。为了解决这个问题,Kubernetes提供了startup probe的机制。startup probe在应用启动的时候,优先级比liveness probe的优先级高,因此当应用启动的时候,只有在startup probe运行成功后,Kubernetes才将应用的健康检查切换到liveness probe,这样当应用在运行过程中,如果出现故障,就能很快重启恢复。

假设我们的springcloud应用程序k8ssample需要1分多钟才能启动起来,但是我们希望应用在健康检查失败后10秒钟内重新启动,以下是满足如上运维需求的YAML文件配置:

《图1.9 配置了startup probe的YAML文件》

如上图所示,stratup probe和liveness probe使用了相同的健康状态接口,并且给与应用程序120s的空间来进行启动,应用启动完成后,liveness probe会持续监控应用的健康状态,每5s进行一次健康检查,如果健康检查连续两次失败,那么就继续容器重启。

上图中startup probe的配置会让Kubernetes每10s进行一次健康检查,并且会重试12次。同liveness probe不同的是,startup probe很容易就失败,而失败并不可怕,只是告诉我们应用还没有完全启动而已,一次成功的startup probe返回就预示着应用程序已经成功启动了,Kubernetes会接着将健康检查切换到liveness probe上,这个时候liveness会基于自己的配置来以频率更高的方式对应用的健康状态进行监控。如下图所示:

《图1.10 组合使用startup 和 liveness probe可以提升应用的运行健康》

如上图所示,liveness probe和startup probe结合使用,会让我们的应用程序更加健壮。读者需要注意的是,如果startup probe失败的次数到达上限,容器会被重启。

本篇文章中使用过的probe接口其实不能算生产ready的健康检查机制,我们必须给自己的应用提供可以准确返回运行健康状态的health接口,因此接下来我们来聊聊如何实现健康检查接口。当我们给自己的应用实现健康检查接口的时候,你务必谨慎和小心,笔者见过太多的案例,由于这个健康检查接口实现的不好,导致应用其实健康,但是这个接口返回失败,造成大量无谓的重启,浪费资源。那么如何实现liveness probe的健康检查接口呢?

结合笔者的过往的经验,我们一般可以实现/healthz的接口,这个接口实现代码中,检查应用的核心组件是不是运行健康,并且这个接口应该添加到白名单中,不经过授权就能访问。如果需要授权,那么当授权服务挂了之后,会产生级联的容器重启错误。另外,这个接口中请务必保证只检查应用自己的组件,不要检查外部的依赖的服务。比如前端服务就不应该由于后台数据库服务不可用而返回健康检查失败的错误,因为这种情况下,重启前端服务的pod无济于事。

因为无论你重启多少遍,只要后台数据库服务的问题没有解决,POD会继续失败,继续重启,如此反复。想想一下如果我们的多个相互依赖的服务如此配置,那么一个服务出现问题,会造成级联的容器重启,那是多么恐怖的一种景象啊。

最后,我们要让/healthz接口足够轻量级,不能使用太多的CPU和内存资源,这个不难理解,监看检查的执行频率会非常高,并且要在先对比较严格的时间内返回,才算成功。健康检查接口使用太多的资源会影响正常应用的运行稳定性。这里有个经验给大家分享一下,千万不要在健康检查的实现代码中重试,而应该设置合理的failureThreshold参数,自己在代码中重试健康检查会有其他潜在的异常场景,这个大家一定要遵守。

好了,今天的内容就真没多了,下一遍我们来看看如何在容器应用启动和退出的时候,执行一些必须的操作,比如资源清理等,敬请期待!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容