Backstage Kubernetes 插件排查指南:Service 实体不显示集群资源的原因与修复
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
本文面向 Backstage 中 Kubernetes 插件的使用与运维场景,讲解一个高频问题的完整排查链路:当你在软件目录(Software Catalog)的 Service 实体中打开 Kubernetes 标签页却发现一片空白时,如何用后端 API 直接验证集群连通性、如何定位标签选择器(label selector)与注解不匹配的根因,以及如何通过为 K8s 资源添加backstage.io/kubernetes-id标签或使用backstage.io/kubernetes-label-selector注解来彻底修复。读完本文,你将掌握一套可复现、可验证的排障流程,并能结合当前仓库源码理解 Backstage 背后的资源匹配原理。
问题现象:Kubernetes 未显示在 Service 实体上
在 Backstage 中接入 Kubernetes 插件后,最常见的"不工作"表现就是:某个 Service 实体页面上的 Kubernetes 标签页始终为空,既不报错也不展示任何 Pod、Service、Deployment 等资源。出现这种情况时,集群并不一定没有连接上,更常见的原因是实体注解与集群资源上的标签无法匹配——Backstage 正是依靠这套"注解 ↔ 标签"的对应关系来决定要为哪个实体拉取哪些资源。
下面按照"先验证后端、再检查标签、最后修复注解"的顺序展开。
第一步:用 curl 直接探测后端 API,确认集群侧数据
排查的第一步是绕过前端 UI,直接向 Backstage 后端的 Kubernetes 插件路由发起请求,验证后端能否从集群取回资源。官方推荐的探测方式如下(将占位符替换为你的实际值):
curl --location --request POST '{{backstage-backend-url}}:{{backstage-backend-port}}/api/kubernetes/services/:service-entity-name' \ --header 'Content-Type: application/json' \ --data-raw '{ "entity": { "metadata": { "name": <service-entity-name> } } } '其中:
{{backstage-backend-url}}/{{backstage-backend-port}}:Backstage 后端服务的地址与端口,即app-config.yaml中backend.baseUrl对应的主机与端口;:service-entity-name:你在软件目录中创建的那个 Service 实体的metadata.name;- 请求体中的
entity.metadata.name用于告知后端要查询哪个实体。
如果后端与集群连接正常,且标签匹配正确,响应中应包含来自 Kubernetes 的资源列表:
# curl response { "items": [ { "cluster": { "name": <cluster-name> }, "resources": [ { "type": "services", "resources": [ { "metadata": { "creationTimestamp": "2022-03-13T13:52:46.000Z", "labels": { "app": <k8s-app-name>, "backstage": <selector>, "backstage.io/kubernetes-id": <service-entity-name> }, "name": <k8s-app-name>, "namespace": <namespace> }, .... } ] }, .... { "type": "pods", "resources": [ ,,,, ] } ], "errors": [] } ] }如何解读这份响应
items[]数组对应"为该实体匹配到的每一个集群";- 每个集群下的
resources[]按资源类型(services、pods、deployments等)分组返回,type字段标明类型; - 每个资源的
metadata.labels中可以看到backstage.io/kubernetes-id标签——这正是 Backstage 用来把实体与资源关联起来的键; errors字段如果非空,则说明该集群在某些资源类型的抓取上发生了错误(例如鉴权失败、RBAC 权限不足、Metrics API 不可用等),这本身也是下一步排查的重要线索。
如果items为空或resources为空,说明后端没有为这个实体找到任何匹配的集群资源——问题大概率出在"标签/注解不匹配",而不是集群断连。
该端点在前端是如何被调用的
从当前仓库源码看,这个探测端点由 KubernetesRouter.ts 注册:router.post('/services/:serviceId', ...)会先校验调用方权限(kubernetesResourcesReadPermission),再将请求体中的实体引用解析为真实的 Catalog 实体,最后调用objectsProvider.getKubernetesObjectsByEntity完成资源抓取。注意该端点已在源码中被标注为// @deprecated,仓库中新增了POST /resources/workloads/query与POST /resources/custom/query等路由(见 resourcesRoutes.ts),但上述 curl 探测方式仍可用于快速验证后端返回结构与标签匹配情况。对应端点的行为验证可参考仓库测试 KubernetesRouter.test.ts 中describe('post /services/:serviceId')的用例。
第二步:理解根因——注解与标签的匹配逻辑
当 Catalog 实体的注解(annotation)与集群资源的标签(label)不一致时,Kubernetes 标签页就不会显示任何内容。要理解这一点,需要先看 Backstage 后端是如何决定"拉取哪些资源"的。
从源码看匹配流程
资源匹配的核心逻辑位于 KubernetesFanOutHandler.ts 的fanOutRequests方法中,关键代码可以概括为两步:
- 确定实体名(entityName):优先读取实体注解
backstage.io/kubernetes-id;如果该注解不存在,则回退使用实体的metadata.name:
const entityName = entity.metadata?.annotations?.['backstage.io/kubernetes-id'] || entity.metadata?.name;- 构造标签选择器(labelSelector):优先读取实体注解
backstage.io/kubernetes-label-selector;如果该注解不存在,则默认构造为backstage.io/kubernetes-id=<entityName>:
const labelSelector: string = entity.metadata?.annotations?.[ KUBERNETES_LABEL_SELECTOR_QUERY_ANNOTATION ] || `${KUBERNETES_ANNOTATION}=${entityName}`;最终,这个labelSelector会被传给 fetcher,以labelSelector为条件去对应集群拉取各类型资源(见 KubernetesFetcher.ts 中fetchObjectsForService的实现)。
注解常量定义
上述两个注解键在 catalog-entity-constants.ts 中被统一定义:
KUBERNETES_ANNOTATION = 'backstage.io/kubernetes-id'——用于把 Catalog 实体与对应的 Kubernetes 资源关联起来;KUBERNETES_LABEL_SELECTOR_QUERY_ANNOTATION = 'backstage.io/kubernetes-label-selector'——用于指定一个完整的 Kubernetes 标签选择器查询串,例如app=my-app,environment=production。
小结:Kubernetes 标签页为空,几乎都是因为"实体上的注解(决定 labelSelector)"与"集群资源上的标签"对不上,导致 K8s API 按选择器查询时返回了空列表。
第三步:修复方案一——为 K8s 资源打上backstage.io/kubernetes-id标签
推荐做法是:给所有需要关联到该实体的 Kubernetes 对象(Service、Deployment、Ingress 等)加上backstage.io/kubernetes-id标签,标签值等于 Catalog 实体的名称。示例:
# k8s related yaml (service.yaml, deployment.yaml, ingress.yaml) metadata: creationTimestamp: '2022-03-13T13:52:46.000Z' labels: app: <k8s-app-name> env: <environment> backstage.io/kubernetes-id: <service-entity-name> name: <k8s-app-name> namespace: <namespace>字段说明:
app、env等是你自己的业务标签,与 Backstage 无关;backstage.io/kubernetes-id是关键——它的值必须与 Catalog 实体名(<service-entity-name>)一致;name(<k8s-app-name>)与backstage.io/kubernetes-id的值可以不同;但如果你希望 Kubernetes 侧与 Backstage 侧的名称保持一致、方便运维排查,官方建议两者使用同一个名字。
仓库中的测试夹具也印证了这一约定:例如 deploy-healthy.json 中的 Deployment 同时在其metadata.labels和 Pod 模板标签中携带"backstage.io/kubernetes-id": "dice-roller",而对应的 Catalog 实体名正是dice-roller。这意味着打标签时不能只打在 Deployment 上,还要注意 Pod 模板(spec.template.metadata.labels)等资源同样需要携带该标签,否则即使 Deployment 被选中,由其派生的 Pod 也可能匹配不上,导致资源类型显示不全。
第四步:修复方案二——在 Catalog 实体上使用 label selector 注解
如果由于历史原因无法统一修改所有 K8s 资源的标签(例如资源归属其他团队、标签体系已经固定),可以使用backstage.io/kubernetes-label-selector注解,在 Catalog 实体一侧指定任意的标签选择器:
# catalog-info.yaml (backstage) annotations: backstage.io/kubernetes-label-selector: '<label-selector>'其中<label-selector>是标准的 Kubernetes 标签选择器表达式,例如:
annotations: backstage.io/kubernetes-label-selector: 'app=dice-roller,environment=production'该注解的值会被直接作为 K8s API 查询时的labelSelector参数使用,因此你可以利用 K8s 标签选择器完整的表达能力(等值匹配、集合匹配in/notin、存在性判断exists/!等)。一旦配置了此注解,后端就会优先采用它构造选择器,而不再使用默认的backstage.io/kubernetes-id=<entityName>。
两者如何选择
| 场景 | 推荐方案 |
|---|---|
| 集群资源由你方统一管理,可以修改 YAML | 方案一:为资源打backstage.io/kubernetes-id标签(最简单、最直观) |
| 资源标签体系固定、无法改动,或一个实体需要匹配多种标签组合 | 方案二:在catalog-info.yaml中使用backstage.io/kubernetes-label-selector |
| 两者都配置 | label selector 注解优先生效(见上文源码逻辑) |
进阶提示:命名空间注解与更多排查方向
除了上面两个核心注解,源码中还支持backstage.io/kubernetes-namespace注解(见 KubernetesFanOutHandler.ts),用于将资源抓取范围限定到指定命名空间,适合实体资源较多时缩小查询范围、提升性能。
如果完成了标签/注解修复后标签页依然为空,建议按以下顺序继续排查:
- 检查
errors字段:重新执行文首的 curl,看响应中items[].errors是否出现鉴权失败(如401/403)、Metrics API 不可用或 RBAC 权限不足等错误; - 检查集群连通性配置:确认
app-config.yaml中kubernetes.clusterLocatorMethods下的集群 URL、authProvider、serviceAccountToken等是否正确,完整配置示例见 configuration.md; - 检查命名空间:如果实体注解了
backstage.io/kubernetes-namespace,确认资源确实位于该命名空间; - 确认实体名称:牢记默认选择器是
backstage.io/kubernetes-id=<entityName>,其中entityName优先取注解值、其次取metadata.name,不要混淆这两者。
结语
Kubernetes 标签页为空是 Backstage Kubernetes 插件接入中最常见的"假故障"——集群连接正常,只是 Catalog 实体与集群资源之间缺少匹配关系。通过本文的 curl 探测法你可以快速定位问题层级,而backstage.io/kubernetes-id标签与backstage.io/kubernetes-label-selector注解则提供了两套互补的修复路径。理解了 KubernetesFanOutHandler.ts 中的匹配逻辑,你就能在任何"资源不显示"的场景下快速判断:问题出在标签、注解还是命名空间。更多配置细节可进一步阅读 Kubernetes 配置文档 与 Kubernetes 功能总览。
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考