这篇文章已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已过时。
使用 Kubernetes Events 从控制平面向应用报告错误
在 Box,我们管理着几个大型 Kubernetes 集群,这些集群作为内部平台即服务 (PaaS),为数百个部署的微服务提供支持。这些微服务中的大部分是为超过 80,000 名客户提供 box.com 服务的应用。PaaS 团队还将几个与平台基础设施相关的服务作为控制平面部署。
Box 控制平面的一种用例是公钥基础设施 (PKI) 处理。在我们的基础设施中,需要新 SSL 证书的应用也需要在控制平面中触发一些处理。出于安全原因,我们的大多数应用不允许生成新的 SSL 证书。控制平面具有不同的安全边界和网络访问权限,因此允许生成证书。
| | | 图 1:PKI 流程框图 |
如果应用需要新证书,应用所有者会显式地向应用的 Kubernetes 配置中添加一个自定义资源定义 (CRD) [1]。此 CRD 指定了 SSL 证书的参数:名称、通用名等。控制平面中的一个微服务会观察 CRD 并触发 SSL 证书生成的处理 [2]。证书准备好后,同一个控制平面服务会将其作为 Kubernetes Secret 发送给 API 服务器 [3]。之后,应用容器使用 Kubernetes Secret VolumeMounts 访问其证书 [4]。您可以在我们的 GitHub 示例应用中看到该系统的演示。
本文的其余部分涵盖了控制平面中这种“触发式”处理中的错误场景。特别是,我们特别关注用户输入错误。由于 SSL 证书参数来自应用的配置文件,采用 CRD 格式,那么如果 CRD 规范中出现错误,应该怎么办?即使是打字错误也会导致 SSL 证书创建失败。错误信息可在控制平面中获取,尽管根本原因很可能存在于应用的配置文件中。应用所有者无法访问控制平面的状态或日志。
为应用所有者提供正确的诊断信息以便他们能够修复错误,在大规模环境下成为了一个严重的生产力问题。Box 快速迁移到微服务导致每周都有几个新的部署。许多首次用户,他们不了解基础设施的每一个细节,需要能够成功部署他们的服务并轻松排查问题。作为基础设施的所有者,我们不想成为瓶颈,需要从控制平面日志中读取错误并将它们传递给应用所有者。如果所有者配置中的某些内容导致其他地方出错,所有者需要获得充分的诊断信息。这些错误数据必须自动流动,无需人工干预。
经过深思熟虑和反复试验,我们发现 Kubernetes Events 非常适合自动沟通这类错误。如果错误信息放在 Pod 的事件流中,它就会显示在 kubectl describe 的输出中。即使是初级用户也可以执行 kubectl describe pod 并获得错误诊断。
我们尝试将控制平面服务的状态网页作为 Kubernetes Events 的替代方案。我们认为状态页面可以在每次处理 SSL 证书后更新,应用所有者可以探测状态页面并从中获取诊断信息。在最初尝试状态页面后,我们发现它不如 Kubernetes Events 解决方案有效。状态页面对应用所有者来说是一个新的需要学习的界面,一个新的需要记住的网址,以及在排查问题时需要切换到的另一个独立工具。另一方面,Kubernetes Events 清晰地显示在 kubectl describe 的输出中,这很容易被开发者识别。
下面是一个简化示例,展示了我们如何在不同的服务之间使用 Kubernetes Events 进行错误报告。我们已经开源了一个代表上述控制平面服务的示例 golang 应用。它监视 CRD 的更改并进行输入参数检查。如果发现错误,会生成一个 Kubernetes Event 并更新相关 Pod 的事件流。
示例应用执行此代码来设置 Kubernetes Event 生成
// eventRecorder returns an EventRecorder type that can be
// used to post Events to different object's lifecycles.
func eventRecorder(
kubeClient \*kubernetes.Clientset) (record.EventRecorder, error) {
eventBroadcaster := record.NewBroadcaster()
eventBroadcaster.StartLogging(glog.Infof)
eventBroadcaster.StartRecordingToSink(
&typedcorev1.EventSinkImpl{
Interface: kubeClient.CoreV1().Events("")})
recorder := eventBroadcaster.NewRecorder(
scheme.Scheme,
v1.EventSource{Component: "controlplane"})
return recorder, nil
}
一次性设置完成后,以下代码生成与 Pod 相关的事件
ref, err := reference.GetReference(scheme.Scheme, &pod)
if err != nil {
glog.Fatalf("Could not get reference for pod %v: %v\n",
pod.Name, err)
}
recorder.Event(ref, v1.EventTypeWarning, "pki ServiceName error",
fmt.Sprintf("ServiceName: %s in pki: %s is not found in"+
" allowedNames: %s", pki.Spec.ServiceName, pki.Name,
allowedNames))
可以通过运行示例应用来理解进一步的实现细节。
如前所述,这是应用所有者相关的 kubectl describe 输出。
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------
....
1d 1m 24 controlplane Warning pki ServiceName error ServiceName: appp1 in pki: app1-pki is not found in allowedNames: [app1 app2]
....
我们展示了 Kubernetes Events 的一个实际用例。在配置错误的情况下,向程序员提供自动化反馈显著提高了我们的排查效率。将来,我们计划在其他类似用例的各种应用中使用 Kubernetes Events。最近创建的sample-controller 示例也在类似场景中使用了 Kubernetes Events。很高兴看到有更多的示例应用来指导社区。我们很高兴继续探索 Events 和 Kubernetes API 其他部分的用例,以便让我们的工程师开发更轻松。
如果您有想要分享的 Kubernetes 经验,请提交您的故事。如果您在组织中使用 Kubernetes 并希望更直接地表达您的经验,请考虑加入 CNCF 最终用户社区,Box 和许多志同道合的公司都是其中的一员。
特别感谢 Greg Lyons 和 Mohit Soni 的贡献。