做者 | 声东 阿里云售后技术专家api
导读:阿里云售后技术团队的同窗,天天都在处理各式各样千奇百怪的线上问题。常见的有网络链接失败、服务器宕机、性能不达标及请求响应慢等。但若是要评选的话,什么问题看起来微不足道事实上却让人绞尽脑汁,我相信确定是“删不掉”的问题,好比文件删不掉、进程结束不掉、驱动卸载不了等。这样的问题就像冰山,隐藏在它们背后的复杂逻辑,每每超过咱们的预想。服务器
今天咱们讨论的这个问题,跟 K8s 集群的 Namespace 有关。Namespace 是 K8s 集群资源的“收纳”机制。咱们能够把相关的资源“收纳”到同一个 Namespace 里,以免不相关资源之间没必要要的影响。网络
Namespace 自己也是一种资源。经过集群 API Server 入口,咱们能够新建 Namespace,而对于再也不使用的 Namespace,咱们须要清理掉。Namespace 的 Controller 会经过 API Server,监视集群中 Namespace 的变化,而后根据变化来执行预先定义的动做。app
有时候,咱们会遇到下图中的问题,即 Namespace 的状态被标记成了 "Terminating",但却没有办法被彻底删除。less
由于删除操做是经过集群 API Server 来执行的,因此咱们要分析 API Server 的行为。跟大多数集群组件相似,API Server 提供了不一样级别的日志输出。为了理解 API Server 的行为,咱们将日志级别调整到最高级。而后,经过建立删除 tobedeletedb 这个 Namespace 来重现问题。ide
但惋惜的是,API Server 并无输出太多和这个问题有关的日志。微服务
相关的日志,能够分为两部分:工具
Kube Controller Manager 实现了集群中大多数的 Controller,它在重复获取 tobedeletedb 的信息,基本上能够判断,是 Namespace 的 Controller 在获取这个 Namespace 的信息。性能
和上一节相似,咱们经过开启 Kube Controller Manager 最高级别日志,来研究这个组件的行为。在 Kube Controller Manager 的日志里,能够看到 Namespace 的 Controller 在不断地尝试一个失败了的操做,就是清理 tobedeletedb 这个 Namespace 里“收纳”的资源。测试
这里咱们须要理解一点,就是 Namespace 做为资源的“收纳盒”,实际上是逻辑意义上的概念。它并不像现实中的收纳工具,能够把小的物件收纳其中。Namespace 的“收纳”其实是一种映射关系。
这一点之因此重要,是由于它直接决定了,删除 Namespace 内部资源的方法。若是是物理意义上的“收纳”,那咱们只须要删除“收纳盒”,里边的资源就一并被删除了。而对于逻辑意义上的关系,咱们则须要罗列全部资源,并删除那些指向须要删除的 Namespace 的资源。
怎么样罗列集群中的全部资源呢?这个问题须要从集群 API 的组织方式提及。K8s 集群的 API 不是铁板一块的,它是用分组和版原本组织的。这样作的好处显而易见,就是不一样分组的 API 能够独立迭代,互不影响。常见的分组如 apps,它有 v一、v1beta1 和 v1beta2 三个版本。完整的分组/版本列表,可使用 kubectl api-versions 命令看到。
咱们建立的每个资源,都必然属于某一个 API 分组/版本。如下边 Ingress 为例,咱们指定 Ingress 资源的分组/版本为 networking.k8s.io/v1beta1。
kind: Ingress metadata: name: test-ingress spec: rules: - http: paths: - path: /testpath backend: serviceName: test servicePort: 80
用一个简单的示意图来总结 API 分组和版本。
实际上,集群有不少 API 分组/版本,每一个 API 分组/版本支持特定的资源类型。咱们经过 yaml 编排资源时,须要指定资源类型 kind,以及 API 分组/版本 apiVersion。而要列出资源,咱们须要获取 API 分组/版本的列表。
理解了 API 分组/版本的概念以后,再回头看 Kube Controller Manager 的日志,就会豁然开朗。显然 Namespace 的 Controller 在尝试获取 API 分组/版本列表,当遇到 metrics.k8s.io/v1beta1 的时候,查询失败了。而且查询失败的缘由是 "the server is currently unable to handle the request"。
在上一节中,咱们发现 Kube Controller Manager 在获取 metrics.k8s.io/v1beta1 这个 API 分组/版本的时候失败了。而这个查询请求,显然是发给 API Server 的。因此咱们回到 API Server 日志,分析 metrics.k8s.io/v1beta1 相关的记录。在相同的时间点,咱们看到 API Server 也报了一样的错误 "the server is currently unable to handle the request"。
显然这里有一个矛盾,就是 API Server 明显在正常工做,为何在获取 metrics.k8s.io/v1beta1 这个 API 分组版本的时候,会返回 Server 不可用呢?为了回答这个问题,咱们须要理解一下 API Server 的“外挂”机制。
集群 API Server 有扩展本身的机制,开发者能够利用这个机制,来实现 API Server 的“外挂”。这个“外挂”的主要功能,就是实现新的 API 分组/版本。API Server 做为代理,会把相应的 API 调用,转发给本身的“外挂”。
以 Metrics Server 为例,它实现了 metrics.k8s.io/v1beta1 这个 API 分组/版本。全部针对这个分组/版本的调用,都会被转发到 Metrics Server。以下图,Metrics Server 的实现,主要用到一个服务和一个 pod。
而上图中最后的 apiservice,则是把“外挂”和 API Server 联系起来的机制。下图能够看到这个 apiservice 详细定义。它包括 API 分组/版本,以及实现了 Metrics Server 的服务名。有了这些信息,API Server 就能把针对 metrics.k8s.io/v1beta1 的调用,转发给 Metrics Server。
通过简单的测试,咱们发现,这个问题其实是 API server 和 metrics server pod 之间的通讯问题。在阿里云 K8s 集群环境里,API Server 使用的是主机网络,即 ECS 的网络,而 Metrics Server 使用的是 Pod 网络。这二者之间的通讯,依赖于 VPC 路由表的转发。
以上图为例,若是 API Server 运行在 Node A 上,那它的 IP 地址就是 192.168.0.193。假设 Metrics Server 的 IP 是 172.16.1.12,那么从 API Server 到 Metrics Server 的网络链接,必需要经过 VPC 路由表第二条路由规则的转发。
检查集群 VPC 路由表,发现指向 Metrics Server 所在节点的路由表项缺失,因此 API server 和 Metrics Server 之间的通讯出了问题。
为了维持集群 VPC 路由表项的正确性,阿里云在 Cloud Controller Manager 内部实现了 Route Controller。这个 Controller 在时刻监听着集群节点状态,以及 VPC 路由表状态。当发现路由表项缺失的时候,它会自动把缺失的路由表项填写回去。
如今的状况,显然和预期不一致,Route Controller 显然没有正常工做。这个能够经过查看 Cloud Controller Manager 日志来确认。在日志中,咱们发现,Route Controller 在使用集群 VPC id 去查找 VPC 实例的时候,没有办法获取到这个实例的信息。
可是集群还在,ECS 还在,因此 VPC 不可能不在了。这一点咱们能够经过 VPC id 在 VPC 控制台确认。那下边的问题,就是为何 Cloud Controller Manager 没有办法获取到这个 VPC 的信息呢?
Cloud Controller Manager 获取 VPC 信息,是经过阿里云开放 API 来实现的。这基本上等于从云上一台 ECS 内部,去获取一个 VPC 实例的信息,而这须要 ECS 有足够的权限。目前的常规作法是,给 ECS 服务器授予 RAM 角色,同时给对应的 RAM 角色绑定相应的角色受权。
若是集群组件,以其所在节点的身份,不能获取云资源的信息,那基本上有两种可能性。一是 ECS 没有绑定正确的 RAM 角色;二是 RAM 角色绑定的 RAM 角色受权没有定义正确的受权规则。检查节点的 RAM 角色,以及 RAM 角色所管理的受权,咱们发现,针对 vpc 的受权策略被改掉了。
当咱们把 Effect 修改为 Allow 以后,没多久,全部的 Terminating 状态的 namespace 所有都消失了。
整体来讲,这个问题与 K8s 集群的 6 个组件有关系,分别是 API Server 及其扩展 Metrics Server,Namespace Controller 和 Route Controller,以及 VPC 路由表和 RAM 角色受权。
经过分析前三个组件的行为,咱们定位到,集群网络问题致使了 API Server 没法链接到 Metrics Server;经过排查后三个组件,咱们发现致使问题的根本缘由是 VPC 路由表被删除且 RAM 角色受权策略被改动。
K8s 集群 Namespace 删除不掉的问题,是线上比较常见的一个问题。这个问题看起来无关痛痒,但实际上不只复杂,并且意味着集群重要功能的缺失。这篇文章全面分析了一个这样的问题,其中的排查方法和原理,但愿对你们排查相似问题有必定的帮助。
“阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,作最懂云原生开发者的技术圈。”