写点什么

深入浅出 Kubernetes 实践篇 (三):命名空间删除问题

  • 2020-03-30
  • 本文字数:3677 字

    阅读完需:约 12 分钟

深入浅出Kubernetes 实践篇 (三):命名空间删除问题

阿里云售后技术团队的同学,每天都在处理各式各样千奇百怪的线上问题。常见的有,网络连接失败,服务器宕机,性能不达标,请求响应慢等。但如果要评选,什么问题看起来微不足道事实上却足以让人绞尽脑汁,我相信答案肯定是“删不掉”的问题。比如文件删不掉,进程结束不掉,驱动卸载不了等。


这样的问题就像冰山,影藏在它们背后的复杂逻辑,往往超过我们的预想。

背景

今天我们讨论的这个问题,跟 Kubernetes 集群的命名空间有关。命名空间是 Kubernetes 集群资源的“收纳”机制。我们可以把相关的资源,“收纳”到同一个命名空间里,以避免不相关资源之间不必要的影响。


命名空间本身也是一种资源。通过集群 API Server 入口,我们可以新建命名空间,而对于不再使用的命名空间,我们需要清理掉。命名空间的 Controller 会通过 API Server,监视集群中命名空间的变化,然后根据变化来执行预先定义的动作。



有时候,我们会遇到下图中的问题,即命名空间的状态被标记成了“Terminating”,但却没有办法被完全删除。


从集群入口开始

因为删除操作是通过集群 API Server 来执行的,所以我们要分析 API Server 的行为。跟大多数集群组件类似,API Server 提供了不同级别的日志输出。为了理解 API Server 的行为,我们将日志级别调整到最高级。然后,通过创建删除 tobedeletedb 这个命名空间来重现问题。


但可惜的是,API Server 并没有输出太多和这个问题有关的日志。


相关的日志,可以分为两部分。一部分是命名空间被删除的记录,记录显示客户端工具是 kubectl,以及发起操作的源 IP 地址是 192.168.0.41,这符合预期;另外一部分是 Kube Controller Manager 在重复的获取这个命名空间的信息。



Kube Controller Manager 实现了集群中大多数的 Controller,它在重复获取 tobedeletedb 的信息,基本上可以判断,是命名空间的 Controller 在获取这个命名空间的信息。

Controller 在做什么?

和上一节类似,我们通过开启 Kube Controller Manager 最高级别日志,来研究这个组件的行为。在 Kube Controller Manager 的日志里,可以看到命名空间的 Controller 在不断地尝试一个失败了的操作,就是清理 tobedeletedb 这个命名空间里“收纳”的资源。



怎么样删除“收纳盒”里的资源


这里我们需要理解一点,就是命名空间作为资源的“收纳盒”,其实是逻辑意义上的概念。它并不像现实中的收纳工具,可以把小的物件收纳其中。命名空间的“收纳”实际上是一种映射关系。



这一点之所以重要,是因为它直接决定了,删除命名空间内部资源的方法。如果是物理意义上的“收纳”,那我们只需要删除“收纳盒”,里边的资源就一并被删除了。而对于逻辑意义上的关系,我们则需要罗列所有资源,并删除那些指向需要删除的命名空间的资源。


API、Group、Version


怎么样罗列集群中的所有资源呢,这个问题需要从集群 API 的组织方式说起。Kubernetes 集群的 API 不是铁板一块的,它是用分组和版本来组织的。这样做的好处显而易见,就是不同分组的 API 可以独立的迭代,互不影响。常见的分组如 apps,它有 v1,v1beta1 和 v1beta2 这三个版本。完整的分组/版本列表,可以使用 kubectl api-versions 命令看到。



我们创建的每一个资源,都必然属于某一个 API 分组/版本。以下边 Ingress 为例,我们指定 Ingress 资源的分组/版本为 networking.k8s.io/v1beta1。


kind: Ingressmetadata:  name: test-ingressspec:  rules:  - http:      paths:      - path: /testpath        backend:          serviceName: test          servicePort: 80
复制代码


用一个简单的示意图来总结 API 分组和版本。



实际上,集群有很多 API 分组/版本,每个 API 分组/版本支持特定的资源类型。我们通过 yaml 编排资源时,需要指定资源类型 kind,以及 API 分组/版本 apiVersion。而要列出资源,我们需要获取 API 分组/版本的列表。


Controller 为什么不能删除命名空间里的资源


理解了 API 分组/版本的概念之后,再回头看 Kube Controller Manager 的日志,就会豁然开朗。显然命名空间的 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。


节点与 Pod 之间的通信

经过简单的测试,我们发现,这个问题实际上是 API server 和 metrics server pod 之间的通信问题。在阿里云 Kubernetes 集群环境里,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 之间的通信出了问题。

Route Controller 为什么不工作?

为了维持集群 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 全部都消失了。


问题大图

总体来说,这个问题与 Kubernetes 集群的 6 个组件有关系,分别是 API Server 及其扩展 Metrics Server,Namespace Controller 和 Route Controller,以及 VPC 路由表和 RAM 角色授权。



通过分析前三个组件的行为,我们定位到,集群网络问题导致了 API Server 无法连接到 Metrics Server;通过排查后三个组件,我们发现导致问题的根本原因是 VPC 路由表被删除且 RAM 角色授权策略被改动。

后记

Kubernetes 集群命名空间删除不掉的问题,是线上比较常见的一个问题。这个问题看起来无关痛痒,但实际上不仅复杂,而且意味着集群重要功能的缺失。这篇文章全面分析了一个这样的问题,其中的排查方法和原理,希望对大家排查类似问题有一定的帮助。


作者简介


罗建龙(花名声东),阿里云技术专家。多年操作系统和图形显卡驱动调试和开发经验。目前专注云原生领域,容器集群和服务网格。


相关阅读


深入浅出Kubernetes 实践篇 (一):节点就绪问题之一


深入浅出Kubernetes 实践篇 (二):节点就绪问题之二


2020-03-30 17:141086

评论

发布
暂无评论
发现更多内容

随机森林-概述

烧灯续昼2002

Python 机器学习 算法 sklearn 11月月更

JavaScript基本数据类型和引用数据类型详解

肥晨

js 11月月更 数据结构js

SQL 碎碎念,你可能用不到但不能不知道的数据库技巧(2)

百里丶落云

数据库 后端 11月月更

云原生训练营课程总结

Rex

hadoop2.7集群初始化之后没有DataNode的问题

程序员欣宸

大数据 hadoop 11月月更

chrome调试工具之network

格斗家不爱在外太空沉思

浏览器 11月月更 浏览器调试

Java中的static关键字详解

共饮一杯无

Java 关键字 11月月更

【LeetCode】爱生气的书店老板Java题解

Albert

算法 LeetCode 11月月更

云原生系列四:Yelp 如何在 Kubernetes 上运行 Kafka

叶秋学长

kafka Kubernetes 云原生 11月月更

微服务架构组件总结篇

邱学喆

负载均衡 Spring Cloud 注册中心 配置中心 熔断服务

低代码实现探索(五十一)组件的配置

零道云-混合式低代码平台

vue中网络请求axios的使用和封装

格斗家不爱在外太空沉思

Vue axios 11月月更

图片懒加载

源字节1号

软件开发

WEB-INF/web.xml泄露

我是一个茶壶

WEB安全 11月月更 源码泄露

不借助 Fiori client,直接在手机浏览器里调用 SAP UI5 BarcodeScanner 实现条形码扫描的可能性?

汪子熙

sdk SAP Fiori SAP UI5 11月月更

超全!前端面试题大汇总

肥晨

前端面试题 11月月更 超全前端面试题

pyside6 qml 支持更新液位的伪3D圆柱

Mr_No爱学习

Grafana 最新开源项目 Phlare 速体验

Grafana 爱好者

云原生 可观测性 Grafana 11月月更

下一代TCP: 网络演进的平台

俞凡

TCP 网络

工赋开发者社区 | 研究了代码质量后,开发速度提高了 2 倍,bug 减少了 15 倍

工赋开发者社区

Vue-Vuex学习

格斗家不爱在外太空沉思

vue.js vuex 11月月更

易观千帆 | 2022年9月银行APP月活跃用户规模盘点

易观分析

金融 手机银行

QUIC不是TCP的替代品

俞凡

TCP 网络 QUIC

【C语言】case 关键字

謓泽

11月月更

JAVA中如何实现代码优化(技巧讲解)

叶秋学长

Java 优化 技巧总结 11月月更

【愚公系列】2022年11月 微信小程序-Request网络请求的封装

愚公搬代码

11月月更

Java中的内部类与匿名内部类详解

共饮一杯无

Java 内部类 11月月更 匿名内部类

Redis 的 Sentinel 系统

月明风清

redis Sentinel 系统 哨兵机制

学生管理系统考试试卷存储方案设计

乖乖IvyShine

2022-11-04:给定一个正数n,表示有多少个节点 给定一个二维数组edges,表示所有无向边 edges[i] = {a, b} 表示a到b有一条无向边 edges一定表示的是一个无环无向图,也

福大大架构师每日一题

算法 rust 福大大

【kafka运维】TopicCommand运维脚本

石臻臻的杂货铺

kafka Kafka实战 kafka运维 11月月更

深入浅出Kubernetes 实践篇 (三):命名空间删除问题_文化 & 方法_罗建龙(声东)_InfoQ精选文章