不久前,我以惨痛的代价吸取了一个重要的 Kubernetes 教训。故事始于 Kubernetes Operator,这是一种打包、部署和管理 Kubernetes 应用程序的方法。我犯错的地方是集群中的垃圾回收,它会清理不再具有所有者对象(稍后会详细介绍)的对象。
任务
去年,我所在的团队被分配开发一个 Kubernetes Operator。对于团队中的大多数人来说,这是他们第一次接触 Operator SDK(软件开发工具包)和 Kubernetes 控制器,它们是驱动 Kubernetes 的控制循环。我们都阅读了一些关于 Operator SDK 的基本介绍信息,并遵循了 快速入门指南,以使用 Go 编程语言构建 Operator。我们学习了一些基本原则和一些有用的技巧,并且渴望应用它们。
我们的任务是开发一个 Operator,用于安装、配置和确保多个项目的生产就绪状态。目标是实现自动化管理大量的实例,并最大限度地减少我们的 站点可靠性工程 (SRE) 团队的手动工作。这不是一项容易的任务,但我们喜欢挑战以及我们可以使用的技术,因此我们进展迅速,并在此过程中打破了一些规则。
Bug
最初,我们正在追逐概念验证的实现,因此我们记录了一些 Bug,并计划稍后修复它们。

我将要描述的 Bug 非常适合这个 艾森豪威尔矩阵 (正如史蒂芬·柯维所倡导的)的第二象限——不紧急,但重要。实际上非常重要——我们 Operator 创建的所有命名空间有时会在没有任何用户请求的情况下终止。这种情况并不经常发生,所以我们决定稍后修复它。
最终,我从我们的积压工作中挑选了这个 Bug,并开始寻找根本原因。Operator 肯定无法终止命名空间,因为我们在当时的的代码中没有任何 Delete API 调用,事后看来,这是第一个线索。我开始了我的侦探工作,首先调高了 Kubernetes API 服务器 的日志记录级别,并确保日志被安全地保存。然后我等待问题再次发生。
一旦问题在我设置的环境中发生,我就在日志中搜索以下字符串组合:"requestURI":"/api/v1/namespaces/my-namespace"
+ "verb":"delete"
。
从我的搜索结果中,我找到了执行命名空间删除操作的实体:"user":{"username":"system:serviceaccount:kube-system:generic-garbage-collector"
。
现在我知道了命名空间是如何被删除的,但我不知道为什么。我打开了 Kubernetes 垃圾回收文档,但由于我不是一个有耐心的人,我只是粗略地浏览了关于 ownerReference
字段的基本信息,然后继续思考为什么会发生这种情况。
我们正在我们创建的命名空间上使用 ownerReference
元数据字段。所有者是我们自己的资源,由 自定义资源 API 定义。当我们的自定义资源被删除时,它通过 ownerReference
拥有的相关命名空间也会被删除。相关对象的这种删除操作使卸载步骤变得轻而易举。
我没有看到这有什么问题,所以我继续阅读日志以寻找更多线索。我注意到,当 kube-controller-manager
Pod 重新启动时,命名空间会被删除。重启的原因对我来说是有道理的:kube-controller-manager
Pod 在主节点上运行,我们的开发集群中只有一个主节点,并且我们使用的实例大小的主节点负载非常高。
所以我尝试自己重现这个问题。我删除了 kube-controller-manager
Pod,一个新的 Pod 启动了,我检查了它的日志。一旦我看到一些关于垃圾回收的日志,我终于把两者联系起来,并回到垃圾回收文档。结果就在那里:
“注意: 设计上不允许跨命名空间的所有者引用。这意味着:1) 命名空间作用域的依赖项只能指定同一命名空间中的所有者,以及集群作用域的所有者。2) 集群作用域的依赖项只能指定集群作用域的所有者,但不能指定命名空间作用域的所有者。”
我们的自定义资源是命名空间作用域的,但命名空间是集群作用域的。即使我们使用的所有者引用在设计上是不允许的,Kubernetes API 服务器也会创建命名空间。因此,命名空间是在带有所有者引用的情况下创建的,然后它们必须被删除,因为垃圾回收遵循已记录的规则。
经验教训
我学到的技术教训很简单:不要使用命名空间作用域资源拥有集群作用域资源或不同命名空间中资源的所有者引用。当您使用这些“设计上不允许”的所有者引用时,每当 kube-controller-manager pod
启动时,您的 Kubernetes 资源将被垃圾回收例程删除。
但我学到的更重要的教训是不要低估文档。如果我第一次阅读文档时更有耐心,我肯定会节省一些时间。
您可能还会认为,如果我们在无效的所有者引用添加到代码库时遵循我的建议,我们就可以避免这种情况。但事实证明,当时还没有记录下来。2019 年 2 月的 pull request 添加了这个重要的注释,那时 Kubernetes 早已成为一项成熟的技术。
我认为这突显了一个事实,即文档总是有改进的空间。让我们一起努力,从阅读 为 Kubernetes 文档做贡献的指南 开始。
评论已关闭。