做者 | 莫源 阿里巴巴技术专家node
首先来看一下,整个需求的来源:当把应用迁移到 Kubernetes 以后,要如何去保障应用的健康与稳定呢?其实很简单,能够从两个方面来进行加强:docker
从可观测性上来说,能够在三个方面来去作加强:shell
当出现了问题以后,首先要作的事情是要下降影响的范围,进行问题的调试与诊断。最后当出现问题的时候,理想的情况是:能够经过和 K8s 集成的自愈机制进行完整的恢复。性能优化
本小节为你们介绍 Liveness probe 和 eadiness probe。bash
Liveness probe 也叫就绪指针,用来判断一个 pod 是否处在就绪状态。当一个 pod 处在就绪状态的时候,它才可以对外提供相应的服务,也就是说接入层的流量才能打到相应的 pod。当这个 pod 不处在就绪状态的时候,接入层会把相应的流量从这个 pod 上面进行摘除。微信
来看一下简单的一个例子:网络
以下图其实就是一个 Readiness 就绪的一个例子:app
当这个 pod 指针判断一直处在失败状态的时候,其实接入层的流量不会打到如今这个 pod 上。less
当这个 pod 的状态从 FAIL 的状态转换成 success 的状态时,它才可以真实地承载这个流量。
Liveness 指针也是相似的,它是存活指针,用来判断一个 pod 是否处在存活状态。当一个 pod 处在不存活状态的时候,会出现什么事情呢?jvm
这个时候会由上层的判断机制来判断这个 pod 是否须要被从新拉起。那若是上层配置的重启策略是 restart always 的话,那么此时这个 pod 会直接被从新拉起。
接下来看一下 Liveness 指针和 Readiness 指针的具体的用法。
Liveness 指针和 Readiness 指针支持三种不一样的探测方式:
从探测结果来说主要分为三种:
那在 kubelet 里面有一个叫 ProbeManager 的组件,这个组件里面会包含 Liveness-probe 或 Readiness-probe,这两个 probe 会将相应的 Liveness 诊断和 Readiness 诊断做用在 pod 之上,来实现一个具体的判断。
下面介绍这三种方式不一样的检测方式的一个 yaml 文件的使用。
首先先看一下 exec,exec 的使用其实很是简单。以下图所示,你们能够看到这是一个 Liveness probe,它里面配置了一个 exec 的一个诊断。接下来,它又配置了一个 command 的字段,这个 command 字段里面经过 cat 一个具体的文件来判断当前 Liveness probe 的状态,当这个文件里面返回的结果是 0 时,或者说这个命令返回是 0 时,它会认为此时这个 pod 是处在健康的一个状态。
那再来看一下这个 httpGet,httpGet 里面有一个字段是路径,第二个字段是 port,第三个是 headers。这个地方有时须要经过相似像 header 头的一个机制作 health 的一个判断时,须要配置这个 header,一般状况下,可能只须要经过 health 和 port 的方式就能够了。
第三种是 tcpSocket,tcpSocket 的使用方式其实也比较简单,你只须要设置一个检测的端口,像这个例子里面使用的是 8080 端口,当这个 8080 端口 tcp connect 审核正常被创建的时候,那 tecSocket,Probe 会认为是健康的一个状态。
此外还有以下的五个参数,是 Global 的参数。
接下来对 Liveness 指针和 Readiness 指针进行一个简单的总结。
Liveness 指针是存活指针,它用来判断容器是否存活、判断 pod 是否 running。若是 Liveness 指针判断容器不健康,此时会经过 kubelet 杀掉相应的 pod,并根据重启策略来判断是否重启这个容器。若是默认不配置 Liveness 指针,则默认状况下认为它这个探测默认返回是成功的。
Readiness 指针用来判断这个容器是否启动完成,即 pod 的 condition 是否 ready。若是探测的一个结果是不成功,那么此时它会从 pod 上 Endpoint 上移除,也就是说从接入层上面会把前一个 pod 进行摘除,直到下一次判断成功,这个 pod 才会再次挂到相应的 endpoint 之上。
对于检测失败上面来说 Liveness 指针是直接杀掉这个 pod,而 Readiness 指针是切掉 endpoint 到这个 pod 之间的关联关系,也就是说它把这个流量从这个 pod 上面进行切掉。
Liveness 指针适用场景是支持那些能够从新拉起的应用,而 Readiness 指针主要应对的是启动以后没法当即对外提供服务的这些应用。
在使用 Liveness 指针和 Readiness 指针的时候有一些注意事项。由于不管是 Liveness 指针仍是 Readiness 指针都须要配置合适的探测方式,以避免被误操做。
接下来给你们讲解一下在 K8s 中常见的问题诊断。
首先要了解一下 K8s 中的一个设计理念,就是这个状态机制。由于 K8s 是整个的一个设计是面向状态机的,它里面经过 yaml 的方式来定义的是一个指望到达的一个状态,而真正这个 yaml 在执行过程当中会由各类各样的 controller来负责总体的状态之间的一个转换。
好比说上面的图,其实是一个 Pod 的一个生命周期。刚开始它处在一个 pending 的状态,那接下来可能会转换到相似像 running,也可能转换到 Unknown,甚至能够转换到 failed。而后,当 running 执行了一段时间以后,它能够转换到相似像 successded 或者是 failed,而后当出如今 unknown 这个状态时,可能因为一些状态的恢复,它会从新恢复到 running 或者 successded 或者是 failed 。
其实 K8s 总体的一个状态就是基于这种相似像状态机的一个机制进行转换的,而不一样状态之间的转化都会在相应的 K8s对象上面留下来相似像 Status 或者像 Conditions 的一些字段来进行表示。
像下面这张图其实表示的就是说在一个 Pod 上面一些状态位的一些展示。
好比说在 Pod 上面有一个字段叫 Status,这个 Status 表示的是 Pod 的一个聚合状态,在这个里面,这个聚合状态处在一个 pending 状态。
而后再往下看,由于一个 pod 里面有多个 container,每一个 container 上面又会有一个字段叫 State,而后 State 的状态表示当前这个 container 的一个聚合状态。那在这个例子里面,这个聚合状态处在的是 waiting 的状态,那具体的缘由是由于什么呢?是由于它的镜像没有拉下来,因此处在 waiting 的状态,是在等待这个镜像拉取。而后这个 ready 的部分呢,目前是 false,由于它这个进行目前没有拉取下来,因此这个 pod 不可以正常对外服务,因此此时 ready 的状态是未知的,定义为 false。若是上层的 endpoint 发现底层这个 ready 不是 true 的话,那么此时这个服务是没有办法对外服务的。
再往下是 condition,condition 这个机制表示是说:在 K8s 里面有不少这种比较小的这个状态,而这个状态之间的聚合会变成上层的这个 Status。那在这个例子里面有几个状态,第一个是 Initialized,表示是否是已经初始化完成?那在这个例子里面已是初始化完成的,那它走的是第二个阶段,是在这个 ready 的状态。由于上面几个 container 没有拉取下来相应的镜像,因此 ready 的状态是 false。
而后再往下能够看到这个 container 是否 ready,这里能够看到是 false,而这个状态是 PodScheduled,表示说当前这个 pod 是不是处在一个已经被调度的状态,它已经 bound 在如今这个 node 之上了,因此这个状态也是 true。
那能够经过相应的 condition 是 true 仍是 false 来判断总体上方的这个状态是不是正常的一个状态。而在 K8s 里面不一样的状态之间的这个转换都会发生相应的事件,而事件分为两种: 一种叫作 normal 的事件,一种是 warning 事件。你们能够看见在这第一条的事件是有个 normal 事件,而后它相应的 reason 是 scheduler,表示说这个 pod 已经被默认的调度器调度到相应的一个节点之上,而后这个节点是 cn-beijing192.168.3.167 这个节点之上。
再接下来,又是一个 normal 的事件,表示说当前的这个镜像在 pull 相应的这个 image。而后再往下是一个 warning 事件,这个 warning 事件表示说 pull 这个镜像失败了。
以此类推,这个地方表示的一个状态就是说在 K8s 里面这个状态机制之间这个状态转换会产生相应的事件,而这个事件又经过相似像 normal 或者是 warning 的方式进行暴露。开发者能够经过相似像经过这个事件的机制,能够经过上层 condition Status 相应的一系列的这个字段来判断当前这个应用的具体的状态以及进行一系列的诊断。
本小节介绍一下常见应用的一些异常。首先是 pod 上面,pod 上面可能会停留几个常见的状态。
第一个就是 pending 状态,pending 表示调度器没有进行介入。此时能够经过 kubectl describe pod 来查看相应的事件,若是因为资源或者说端口占用,或者是因为 node selector 形成 pod 没法调度的时候,能够在相应的事件里面看到相应的结果,这个结果里面会表示说有多少个不知足的 node,有多少是由于 CPU 不知足,有多少是因为 node 不知足,有多少是因为 tag 打标形成的不知足。
那第二个状态就是 pod 可能会停留在 waiting 的状态,pod 的 states 处在 waiting 的时候,一般表示说这个 pod 的镜像没有正常拉取,缘由多是因为这个镜像是私有镜像,可是没有配置 Pod secret;那第二种是说可能因为这个镜像地址是不存在的,形成这个镜像拉取不下来;还有一个是说这个镜像多是一个公网的镜像,形成镜像的拉取失败。
第三种是 pod 不断被拉起,并且能够看到相似像 backoff 。这个一般表示说 pod 已经被调度完成了,可是启动失败,那这个时候一般要关注的应该是这个应用自身的一个状态,并非说配置是否正确、权限是否正确,此时须要查看的应该是 pod 的具体日志。
第四种 pod 处在 running 状态,可是没有正常对外服务。那此时比较常见的一个点就多是因为一些很是细碎的配置,相似像有一些字段可能拼写错误,形成了 yaml 下发下去了,可是有一段没有正常地生效,从而使得这个 pod 处在 running 的状态没有对外服务,那此时能够经过 apply-validate-f pod.yaml 的方式来进行判断当前 yaml 是不是正常的,若是 yaml 没有问题,那么接下来可能要诊断配置的端口是不是正常的,以及 Liveness 或 Readiness 是否已经配置正确。
最后一种就是 service 没法正常工做的时候,该怎么去判断呢?那比较常见的 service 出现问题的时候,是本身的使用上面出现了问题。由于 service 和底层的 pod 之间的关联关系是经过 selector 的方式来匹配的,也就是说 pod 上面配置了一些 label,而后 service 经过 match label 的方式和这个 pod 进行相互关联。若是这个 label 配置的有问题,可能会形成这个 service 没法找到后面的 endpoint,从而形成相应的 service 没有办法对外提供服务,那若是 service 出现异常的时候,第一个要看的是这个 service 后面是否是有一个真正的 endpoint,其次来看这个 endpoint 是否能够对外提供正常的服务。
本节讲解的是在 K8s 里面如何进行应用的远程调试,远程调试主要分为 pod 的远程调试以及 service 的远程调试。还有就是针对一些性能优化的远程调试。
首先把一个应用部署到集群里面的时候,发现问题的时候,须要进行快速验证,或者说修改的时候,可能须要相似像登录进这个容器来进行一些诊断。
好比说能够经过 exec 的方式进入一个 pod。像这条命令里面,经过 kubectl exec-it pod-name 后面再填写一个相应的命令,好比说 /bin/bash,表示但愿到这个 pod 里面进入一个交互式的一个 bash。而后在 bash 里面能够作一些相应的命令,好比说修改一些配置,经过 supervisor 去从新拉起这个应用,都是能够的。
那若是指定这一个 pod 里面可能包含着多个 container,这个时候该怎么办呢?怎么经过 pod 来指定 container 呢?其实这个时候有一个参数叫作 -c,如上图下方的命令所示。-c 后面是一个 container-name,能够经过 pod 在指定 -c 到这个 container-name,具体指定要进入哪一个 container,后面再跟上相应的具体的命令,经过这种方式来实现一个多容器的命令的一个进入,从而实现多容器的一个远程调试。
那么 service 的远程调试该怎么作呢?service 的远程调试其实分为两个部分:
在反向列入上面有这样一个开源组件,叫作 Telepresence,它能够将本地的应用代理到远程集群中的一个 service 上面,使用它的方式很是简单。
首先先将 Telepresence 的一个 Proxy 应用部署到远程的 K8s 集群里面。而后将远程单一个 deployment swap 到本地的一个 application,使用的命令就是 Telepresence-swap-deployment 而后以及远程的 DEPLOYMENT_NAME。经过这种方式就能够将本地一个 application 代理到远程的 service 之上、能够将应用在远程集群里面进行本地调试,这个有兴趣的同窗能够到 GitHub 上面来看一下这个插件的使用的方式。
第二个是若是本地应用须要调用远程集群的服务时候,能够经过 port-forward 的方式将远程的应用调用到本地的端口之上。好比说如今远程的里面有一个 API server,这个 API server 提供了一些端口,本地在调试 Code 时候,想要直接调用这个 API server,那么这时,比较简单的一个方式就是经过 port-forward 的方式。
它的使用方式是 kubectl port-forward,而后 service 加上远程的 service name,再加上相应的 namespace,后面还能够加上一些额外的参数,好比说端口的一个映射,经过这种机制就能够把远程的一个应用代理到本地的端口之上,此时经过访问本地端口就能够访问远程的服务。
最后再给你们介绍一个开源的调试工具,它也是 kubectl 的一个插件,叫 kubectl-debug。咱们知道在 K8s 里面,底层的容器 runtime 比较常见的就是相似像 docker 或者是 containerd,不管是 docker 仍是 containerd,它们使用的一个机制都是基于 Linux namespace 的一个方式进行虚拟化和隔离的。
一般状况下 ,并不会在镜像里面带特别多的调试工具,相似像 netstat telnet 等等这些 ,由于这个会形成应用总体很是冗余。那么若是想要调试的时候该怎么作呢?其实这个时候就能够依赖相似于像 kubectl-debug 这样一个工具。
kubectl-debug 这个工具是依赖于 Linux namespace 的方式来去作的,它能够 datash 一个 Linux namespace 到一个额外的 container,而后在这个 container 里面执行任何的 debug 动做,其实和直接去 debug 这个 Linux namespace 是一致的。这里有一个简单的操做,给你们来介绍一下:
这个地方其实已经安装好了 kubectl-debug,它是 kubectl 的一个插件。因此这个时候,你能够直接经过 kubectl-debug 这条命令来去诊断远程的一个 pod。像这个例子里面,当执行 debug 的时候,实际上它首先会先拉取一些镜像,这个镜像里面实际上会默认带一些诊断的工具。当这个镜像启用的时候,它会把这个 debug container 进行启动。与此同时会把这个 container 和相应的你要诊断的这个 container 的 namespace 进行挂靠,也就说此时这个 container 和你是同 namespace 的,相似像网络站,或者是相似像内核的一些参数,其实均可以在这个 debug container 里面实时地进行查看。
像这个例子里面,去查看相似像 hostname、进程、netstat 等等,这些其实都是和这个须要 debug 的 pod 是在同一个环境里面的,因此你以前这三条命令能够看到里面相关的信息。
若是此时进行 logout 的话,至关于会把相应的这个 debug pod 杀掉,而后进行退出,此时对应用其实是没有任何的影响的。那么经过这种方式能够不介入到容器里面,就能够实现相应的一个诊断。
此外它还支持额外的一些机制,好比说我给设定一些 image,而后相似像这里面安装了的是 htop,而后开发者能够经过这个机制来定义本身须要的这个命令行的工具,而且经过这种 image 的方式设置进来。那么这个时候就能够经过这种机制来调试远程的一个 pod。
“ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,作最懂云原生开发者的技术公众号。”
搜索「阿里巴巴云原生公众号」获取更多K8s容器技术内容