4. 开发者门户工程

开发者门户(Developer Portal)是内部开发者平台面向工程师的统一入口。它把散落在代码仓库、CI/CD、监控、工单和 Wiki 中的信息聚合到一个可检索、可操作、可自助的界面里。Backstage 是当前最主流的开源门户框架,其核心由三块能力构成:软件目录(Software Catalog)负责“有哪些服务、谁负责、依赖谁”;软件模板(Software Templates)负责“如何按标准快速创建新项目”;技术文档中心(TechDocs)负责“文档与代码同源、集中呈现”。本节围绕这三块能力,讲清数据模型、落地方式与关键实现细节。

一、软件目录:用实体模型描述整个软件生态

Backstage 目录的本质是一个实体图(Entity Graph)。每个实体由 YAML 描述文件定义,通常放在代码仓库根目录的 catalog-info.yaml 中,由目录插件定期抓取并建立关系。实体有明确的 kind 与 metadata,最常用的几种如下:

  • Component:一个可部署的服务、库或网站,是目录的主体。
  • System:一组协作完成业务能力的组件集合。
  • API:对外或对内暴露的接口契约,可关联 OpenAPI/AsyncAPI 定义。
  • Resource:数据库、消息队列、缓存等基础设施依赖。
  • Group / User:团队与人员,用于归属和权限。

实体之间通过 relations 自动建立反向关系,例如 ownedBy、partOf、dependsOn、providesApi。下面是一个订单服务的目录描述示例:

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: order-service
  description: 订单创建与状态流转服务
  annotations:
    backstage.io/techdocs-ref: dir:.
    grafana/dashboard-selector: "order-service"
  tags: [java, spring-boot, tier-1]
spec:
  type: service
  lifecycle: production
  owner: group:default/order-team
  system: order-platform
  providesApis:
    - order-api
  dependsOn:
    - resource:default/order-db
    - component:default/payment-service

目录数据来源有两种模式:静态注册(在 app-config.yaml 的 catalog.locations 中列出仓库地址或 GitHub 组织)与动态发现(通过 GitHub/GitLab 发现器自动扫描带 catalog-info.yaml 的仓库)。生产环境建议以动态发现为主、静态注册补充关键系统,避免手工维护遗漏。

目录的价值不在“列清单”,而在关系可查询。例如通过 dependsOn 可以回答“payment-service 故障会影响哪些上游”,通过 ownedBy 可以一键找到值班团队。因此录入时宁可少而准,不要多而乱。

二、软件模板:把“新建服务”变成一次表单提交

软件模板用 kind: Template 定义,本质是“参数表单 + 骨架代码 + 自动化动作”的组合。用户在门户填写表单后,Backstage 的 Scaffolder 会执行一系列 action:拉取模板骨架、替换变量、创建仓库、注册目录实体、触发 CI。一个最小可用的模板结构如下:

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: java-service-template
  title: Java 微服务脚手架
spec:
  parameters:
    - title: 基本信息
      required: [name, owner]
      properties:
        name:
          type: string
          pattern: '^[a-z][a-z0-9-]{2,30}$'
        owner:
          type: string
          ui:field: OwnerPicker
        port:
          type: integer
          default: 8080
  steps:
    - id: fetch
      action: fetch:template
      input:
        url: ./skeleton
        values: { name: '{{ parameters.name }}', port: '{{ parameters.port }}' }
    - id: publish
      action: publish:github
      input:
        repoUrl: github.com?owner=my-org&repo={{ parameters.name }}
    - id: register
      action: catalog:register
      input:
        repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}'
        catalogInfoPath: /catalog-info.yaml

模板设计的关键在于约束前置:用 pattern 限制命名规范,用 OwnerPicker 保证归属合法,用默认值减少必填项。这样生成的项目天然符合目录规范,无需事后补录。模板骨架中应内置 catalog-info.yaml、CI 流水线文件、Dockerfile、健康检查端点和基础可观测性配置,使新服务“出生即合规”。

三、技术文档中心:文档与代码同源

TechDocs 采用“Docs like Code”模式:文档以 Markdown 存放在代码仓库的 docs/ 目录,通过 mkdocs.yml 组织导航,Backstage 在构建时渲染为静态站点并挂载到对应组件页面。实体只需声明 backstage.io/techdocs-ref: dir:.,门户即可自动关联。文档构建可放在 CI 中预生成并推送到对象存储,避免每次浏览都触发构建。

三块能力并非孤立:模板创建组件时自动写入目录实体,目录实体又自动关联其文档。下图展示了从模板实例化到目录与文档联动的完整数据流。

Backstage 门户三能力数据流 开发者 填写模板表单 软件模板 Template 参数校验 · 骨架渲染 publish:github · catalog:register 代码仓库 catalog-info.yaml docs/ + mkdocs.yml 软件目录 Catalog Component / System / API owner · dependsOn · partOf 技术文档中心 TechDocs 渲染 docs/ → 静态站点 门户界面 服务检索 依赖拓扑 文档浏览 一键新建 关键联动关系 ① 模板生成仓库时写入 catalog-info.yaml,实体自动进入目录,无需人工补录。 ② 目录实体通过 techdocs-ref 注解关联 docs/ 目录,文档随代码版本同步更新。 ③ dependsOn / providesApi 建立服务间依赖图,支撑影响面分析与故障定位。 ④ 门户界面统一消费目录与文档数据,形成“检索—理解—操作”闭环。

四、落地要点与常见问题

门户工程最容易失败的地方不是技术选型,而是数据质量与使用习惯。以下几点在实践中反复被验证有效:

  • 目录实体与代码同仓库:catalog-info.yaml 随代码评审,归属和依赖变更可被 review 捕获,避免目录与真实架构脱节。
  • 模板即规范:把命名、目录结构、CI 配置、可观测性埋点固化进模板,新项目默认合规,比事后治理成本低得多。
  • 文档构建放 CI:TechDocs 在流水线中生成并上传,门户只做展示,避免浏览时等待构建。
  • 权限与归属对齐:owner 必须指向真实存在的 Group,否则目录会出现“无主服务”,影响值班与告警路由。
  • 渐进式接入:先接入核心系统与高频服务,再逐步扩展,避免一次性导入大量低质量实体稀释目录价值。
常见误区:把门户当成“又一个 Wiki”。如果目录实体不能反映真实依赖、模板不能真正创建可运行项目、文档不能随代码更新,门户就会退化为静态展示页,工程师很快会回到原来的工具链。门户的衡量指标应是“自助创建成功率”“目录实体覆盖率”“文档更新滞后时间”,而非页面访问量。

综上,Backstage 门户工程的落地路径可以概括为:先用目录把软件生态结构化,再用模板把标准动作自动化,最后用文档中心把知识沉淀到代码旁边。三者共享同一套实体模型,形成从“创建”到“运行”再到“理解”的完整闭环,这也是内部开发者平台区别于普通工具集合的核心特征。