使用Kubernetes事件从控制平面向应用程序报告错误

,我们管理着几个大规模的Kubernetes集群,这些集群充当了数百个已部署微服务的内部平台即服务(PaaS)。这些微服务中的大多数是为box.com提供动力的应用程序,可为80,000多个客户提供支持。 PaaS团队还部署了与平台基础架构关联的多项服务,作为 控制平面.

框的控制平面的一个用例是 公钥基础设施 (公钥基础设施)处理。在我们的基础架构中,需要新SSL证书的应用程序还需要触发控制平面中的某些处理。由于安全原因,我们的大多数应用程序均不允许生成新的SSL证书。控制平面具有不同的安全边界和网络访问权限,因此可以生成证书。

| | |图1:PKI流程框图

如果应用程序需要新证书,则应用程序所有者显式添加一个 自定义资源定义 (CRD)到应用程序的Kubernetes配置[1]。此CRD指定SSL证书的参数: 名称,常用名及其他。控制平面中的微服务监视CRD并触发一些处理以生成SSL证书[2]。证书准备就绪后,同一控制平面服务会将其发送到Kubernetes中的API服务器 秘密 [3]。之后,应用程序容器使用Kubernetes访问其证书 秘密卷装 [4]。您可以在我们的系统中看到该系统的工作演示 示例应用 在GitHub上。

本文的其余部分介绍了控制平面中“触发”处理中的错误情况。特别是,我们特别关注用户输入错误。由于SSL证书参数来自CRD格式的应用程序配置文件,因此,如果该CRD规范中存在错误,该怎么办?即使输入错误,也会导致SSL证书创建失败。即使根本原因很可能在应用程序的配置文件中,错误信息也可以在控制面板中获得。应用程序所有者无权访问控制平面的状态或日志。

向应用程序所有者提供正确的诊断,以便她可以纠正错误,这成为大规模生产中的严重问题。 框迅速迁移到微服务,导致每周进行几次新部署。许多不了解基础架构每个细节的首次用户都需要成功地轻松部署服务并解决问题。作为基础结构的所有者,在从控制平面日志中读取错误并将错误传递给应用程序所有者时,我们不想成为瓶颈。如果所有者配置中的某些内容在其他地方导致错误,则所有者需要完全授权的诊断。此错误数据必须在没有任何人为干预的情况下自动流动。

经过大量的思考和实验,我们发现 Kubernetes活动 可以很好地工作来自动传达此类错误。如果错误信息放置在pod的事件流中,则该错误信息会显示在kubectl describe输出中。即使是初学者,也可以执行kubectl describe pod并获得错误诊断。

我们为控制平面服务尝试了一个状态网页,以替代Kubernetes Events。我们确定在处理SSL证书后,状态页可以每次更新,并且应用程序所有者可以探测状态页并从那里获取诊断信息。最初对状态页进行试验后,我们发现它的效果不如Kubernetes Events解决方案有效。状态页面成为应用程序所有者可以学习的新界面,要记住的新网址,以及在进行故障排除时可以将上下文切换到其他工具。另一方面,Kubernetes Events在kubectl describe输出中清晰地显示,开发人员很容易识别出该输出。

这是一个简化的示例,显示了我们如何使用Kubernetes Events在不同服务之间进行错误报告。我们已经开源了 示例golang应用程序 前面提到的控制平面服务的代表。它监视CRD的更改并进行输入参数检查。如果发现错误,则会生成Kubernetes事件,并更新相关Pod的事件流。

示例应用程序执行此操作 设置Kubernetes事件生成:

// 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。最近创建的 样品控制器 该示例还在类似场景中利用了Kubernetes事件。很高兴看到有更多示例应用程序可以指导社区。我们很高兴继续探索事件的其他用例以及Kubernetes API的其余部分,以使我们的工程师更容易开发。

如果您想分享Kubernetes的经验, 提交你的故事。如果您在组织中使用Kubernetes,并且想更直接地表达您的经验,请考虑加入 CNCF最终用户社区 框和数十个志趣相投的公司都属于其中。

特别感谢Greg Lyons和Mohit Soni的贡献。
框的高级软件工程师Hakan Baba