2022-基于 eBPF 的 Kubernetes 问题排查全景图

当 Kubernetes 成为云原生事实标准,可观测性挑战随之而来

当前,云原生技术以容器技术为基础,通过标准可扩展的调度、网络、存储、容器运行时接口来提供基础设施。同时,通过标准可扩展的声明式资源和控制器来提供运维能力,两层标准化推动了开发与运维关注点分离,各领域进一步提升规模化和专业化,达到成本、效率、稳定性的全面优化。

在这样的大技术背景下,越来越对的公司引入了云原生技术来开发、运维业务应用。正因为云原生技术带来了越发纷繁复杂的可能性,业务应用出现了微服务众多、多语言开发、多通信协议的鲜明特征。同时,云原生技术本身将复杂度下移,给可观测性带来了更多挑战:

K8s 可观测性挑战

混沌的微服务架构,多语言和多网络协议混杂

业务架构因为分工问题,容易出现服务数量多,调用协议和关系非常复杂的现象,导致的常见问题包括:

  • 无法准确清晰了解、掌控全局的系统运行架构;
  • 无法回答应用之间的连通性是否正确;
  • 多语言、多网络调用协议带来埋点成本呈线性增长,且重复埋点 ROI 低,开发一般将这类需求优先级降低,但可观测数据又不得不采集。

下沉的基础设施能力屏蔽实现细节,问题定界越发困难

基础设施能力继续下沉,开发和运维关注点继续分离,分层后彼此屏蔽了实现细节,数据方面不好关联了,出现问题后不能迅速地定界问题出现在哪一层。开发同学只关注应用是否正常工作,并不关心底层基础设施细节,出现问题后需要运维同学协同排查问题。运维同学在问题排查过程中,需要开发同学提供足够的上下游来推进排查,否则只拿到“某某应用延迟高”这么笼统的表述,这很难有进一步结果。所以,开发同学和运维同学之间需要共同语言来提高沟通效率,Kubernetes 的 Label、Namespace 等概念非常适合用来构建上下文信息。

繁多监测系统,造成监测界面不一致

复杂系统带来的一个严重副作用就是监测系统繁多。数据链路不关联、不统一,监测界面体验不一致。很多运维同学或许大多都有过这样的体验:定位问题时浏览器打开几十个窗口,在 Grafana、控制台、日志等各种工具之间来回切换,不仅非常耗时巨大,且大脑能处理的信息有限,问题定位效率低下。如果有统一的可观测性界面,数据和信息得到有效地组织,减少注意力分散和页面切换,来提高问题定位效率,把宝贵时间投入到业务逻辑的构建上去。


为了解决上述问题,我们需要使用一种支持多语言,多通信协议的技术,并在产品层面尽可能覆盖软件栈端到端的可观测性需求,通过调研,我们提出一种立足于容器界面和底层操作系统,向上关联应用性能监测的可观测性解决思路。

问题解决思路

Kubernetes 可观测性全景视角

有了上述产品能力,基于阿里巴巴在容器、Kubernetes 方面有着丰富且极具深度的实践,我们将这些宝贵生产实践归纳、转化成产品能力,以帮助用户更有效、更快捷地定位生产环境问题。使用这个排查全景图可以通过以下方法:

  • 大体结构上是以服务和 Deployment(应用)为入口,大多数开发只需要关注这一层就行了。重点关注服务和应用是否错慢,服务是否连通,副本数是否符合预期等
  • 再往下一层是提供真正工作负载能力的 Pod。Pod 重点关注是否有错慢请求,是否健康,资源是否充裕,下游依赖是否健康等
  • 最底下一层是节点,节点为 Pod 和服务提供运行环境和资源。重点关注节点是否健康,是否处于可调度状态,资源是否充裕等。

全景图

网络问题

网络是 Kubernetes 中最棘手、最常见的问题,因为以下几个原因给我们定位生产环境网络问题带来麻烦:

  • Kubernetes 的网络架构复杂度高,节点、Pod、容器、服务、VPC 交相辉映,简直能让你眼花缭乱;
  • 网络问题排查需要一定的专业知识,大多数对网络问题都有种天生的恐惧;
  • 分布式 8 大谬误告诉我们网络不是稳定的、网络拓扑也不一成不变的、延时不可忽视,造成了端到端之间的网络拓扑不确定性。

Kubernetes 环境下场景的网络问题有:

  • conntrack 记录满问题;
  • IP 冲突;
  • CoreDNS 解析慢、解析失败;
  • 节点没开外网。(对,你没听错);
  • 服务访问不通;
  • 配置问题(LoadBalance 配置、路由配置、device 配置、网卡配置);
  • 网络中断造成整个服务不可用。

网络问题千千万万,但万变不离其宗的是网络有其表征其是否正常运行的”黄金指标“:

  • 网络流量和带宽;
  • 丢包数(率)和重传数(率);
  • RTT。

下面的示例展示了因网络问题导致的慢调用问题。从 gateway 来看发生了慢调用,查看拓扑发现调下游 product 的 RT 比较高,但是 product 本身的黄金指标来看 product 本身服务并没有问题,进一步查看两者之间的网络状况,发现 RTT 和重传都比较高,说明网络性能恶化了,导致了整体的网络传输变慢,TCP 重传机制掩盖了这个事实,在应用层面感知不到,日志也没法看出问题所在。这时候网络的黄金指标有助于定界出问题,从而加速了问题的排查。

网络重传延迟高

节点问题

Kubernetes 做了大量工作,尽可能确保提供给工作负载和服务的节点是正常的,节点控制器 7x24 小时地检查节点的状态,发现影响节点正常运行的问题后,将节点置为 NotReady 或不可调度,通过 kubelet 把业务 Pod 从问题节点中驱逐出去。这是 Kubernetes 的第一道防线,第二道防线是云厂商针对节点高频异常场景设计的节点自愈组件,如阿里云的 node repairer:发现问题节点后,执行排水驱逐、置换机器,从而做到自动化地保障业务正常运行。即便如此,节点在长期使用过程中不可避免地会产生各种奇奇怪怪的问题,定位起来比较费时耗力。常见问题分类和级别:

常见问题与分类

针对这些繁杂的问题,总结出如下排查流程图:

排查流程图

以一个 CPU 打满为例:

1、节点状态 OK,CPU 使用率超过了 90%

节点 CPU 过高

2、查看对应的 CPU 的三元组:使用率、TopN、时序图,首先每个核的使用率都很高,进而导致整体 CPU 使用高;接下来我们自然要知道谁在疯狂地使用 CPU,从 TopN 列表来看有个 Pod 一枝独秀地占用 CPU;最后我们得确认下 CPU 飙高是什么时候开始的。

TopN

服务响应慢

造成服务响应非常多,场景可能的原因有代码设计问题、网络问题、资源竞争问题、依赖服务慢等原因。在复杂的 Kubernetes 环境下,定位慢调用可以从两个方案去入手:首先,应用自身是否慢;其次,下游或网络是否慢;最后检查下资源的使用情况。如下图所示,Kubernetes 监测分别从横向和纵向来分析服务性能:

  • 横向:主要是端到端层面来看,首先看自己服务的黄金指标是否有问题,再逐步看下游的网络指标。注意如果从客户端来看调用下游耗时高,但从下游本身的黄金指标来看是正常的,这时候非常有可能是网络问题或者操作系统层面的问题,此时可以用网络性能指标(流量、丢包、重传、RTT 等)来确定。

  • 纵向:确定应用本身对外的延时高了,下一步就是确定具体哪个原因了,确定哪一步/哪个方法慢可以用火焰图来看。如果代码没有问题,那么可能执行代码的环境是有问题的,这时可以查看系统的 CPU/Memory 等资源是否有问题来做进一步排查。

服务响应慢

下面举个 SQL 慢查询的例子(如下图)。在这个例子中网关调用 product 服务, product 服务依赖了 MySQL 服务,逐步查看链路上的黄金指标,最终发现 product 执行了一条特别复杂的 SQL,关联了多张表,导致 MySQL 服务响应慢。MySQL 协议基于 TCP 之上的,我们的 eBPF 探针识别到 MySQL 协议后,组装、还原了 MySQL 协议内容,任何语言执行的 SQL 语句都能采集到。

MySQL 探针

第二个例子是应用本身慢的例子,这时候自然会问具体哪一步、哪个函数造成了慢, ARMS 应用监控支持的火焰图通过对 CPU 耗时定期采样(如下图),帮助快速定位到代码级别问题。

应用本身问题

应用/Pod 状态问题

Pod 负责管理容器,容器是真正执行业务逻辑的载体。同时 Pod 是 Kubernetes 调度的最小单元,所以 Pod 同时拥有了业务和基础设施的复杂度,需要结合着日志、链路、系统指标、下游服务指标综合来看。Pod 流量问题是生产环境高频问题,比如数据库流量陡增,当环境中有成千上万个 Pod 时,排查流量主要来自哪个 Pod 就显得特别困难。

Pod 异常场景和排查

接下来我们看一个典型的案例:下游服务在发布过程中灰度了一个 Pod,该 Pod 因代码原因响应非常慢,导致上游都超时了。之所以能做到 Pod 级别的可观测,是因为我们用 ebpf 的技术来采集 Pod 的流量、黄金指标,因此可以通过拓扑、大盘的方式方便地查看 Pod 与 Pod、Pod 与服务、Pod 与外部的流量。

RT 较高导致延迟