freeCodeCamp.org

How to Build an Internal Developer Platform: A Complete Guide to Backstage, ArgoCD, and Crossplane

8.5内容质量

TL;DR · AI 摘要

构建内部开发平台需采用Backstage、ArgoCD和Crossplane三层架构,实现基础设施自动化与自服务流程。

核心要点

  • IDP采用三层架构:基础设施层(Crossplane)、交付层(ArgoCD)、门户层(Backstage)
  • 通过ApplicationSets实现多环境GitOps交付,减少环境配置差异
  • FinOps集成使资源成本可追溯至团队和成本中心

结构提纲

按章节快速跳转。

  1. 揭示传统平台工程流程的痛点,提出IDP作为自动化解决方案的必要性

  2. 阐述基础设施层、交付层、门户层的协同工作原理及实施顺序

  3. ArgoCD实践

    演示如何通过ApplicationSets实现跨环境的GitOps持续交付

  4. Crossplane应用

    讲解将AWS资源定义为Kubernetes自定义资源的基础设施即代码方案

  5. Backstage集成

    展示软件目录与模板如何构建开发者自助服务平台

  6. 基于CNCF模型量化IDP自动化水平与平台工程能力

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • IDP构建指南
    • 三层架构
      • 基础设施层(Crossplane)
      • 交付层(ArgoCD)
      • 门户层(Backstage)
    • 核心工具
      • GitOps
      • Kubernetes
      • FinOps
    • 实施路径
      • 环境自动化
      • 成本追踪
      • 成熟度评估

金句 / Highlights

值得收藏与分享的关键句。

#Platform Engineering#Kubernetes#GitOps#CNCF
打开原文

如何构建内部开发平台:Backstage、ArgoCD 和 Crossplane 完整指南

2026年7月17日

/

#平台工程

Ayobami Adejumo

每个快速发展的工程团队最终都会遇到同样的瓶颈。

开发者需要一个新的预发布环境,于是他们提交了一个工单。平台团队将其加入队列。

两周后,环境终于创建完成。但与上一个环境的配置略有不同,命名规范与生产环境不一致,缺少了之前环境的可观测性堆栈。开发者进行部署时,出现了问题。没有人知道原因。

问题不在于工单队列,而在于缺乏一个平台:一条铺设好的道路,让开发者能够自主获取一致、可审计且安全的基础设施、部署和环境,而无需为每个请求都依赖平台工程师。

内部开发平台(IDP)解决了这个问题。不是通过将平台工程师从流程中移除,而是将他们的工作重心从执行单个请求,转向构建能够自动执行这些请求的系统。

本手册将基于2026年构成IDP核心的三个CNCF工具,构建一个生产级的IDP:Backstage 作为开发者门户和软件目录,ArgoCD 作为GitOps持续交付引擎,Crossplane 作为原生Kubernetes基础设施控制平面。

最终,平台上的开发者将能够无需提交任何工单,即可完成云数据库的配置、将应用部署到预发布环境,以及在目录中注册新服务。

目录

  • 你将学到的内容
  • 先决条件
  • 第1部分:IDP架构——三层模型
  • 第2部分:ArgoCD——GitOps基础
  • 第3部分:Crossplane——以Kubernetes资源形式定义基础设施
  • 第4部分:Backstage——开发者门户
  • 第5部分:整合它们——黄金路径
  • 第6部分:FinOps集成——在IDP上实现成本归属
  • 第7部分:平台成熟度模型——衡量你构建的内容
  • 最佳实践总结
  • 资源

你将学到的内容

  • 三层IDP架构及其各层必须按特定顺序实现的原因
  • 如何安装和配置ArgoCD并使用ApplicationSets实现多环境GitOps交付
  • 如何使用Crossplane Compositions将云基础设施定义为Kubernetes自定义资源
  • 如何部署和配置Backstage并集成软件目录和软件模板
  • 如何将Backstage、ArgoCD和Crossplane整合为一条统一的自助服务黄金路径
  • 如何在IDP上实现成本归属,使通过该平台创建的每个资源都携带团队和成本中心元数据
  • 如何使用CNCF平台工程成熟度模型衡量IDP的成熟度

让我们开始构建。

  • Node.js 18或更高版本以及Yarn(用于Backstage)
  • 一个你控制的GitHub组织(用于GitOps仓库和Backstage的GitHub集成)

配套仓库:

code
git clone https://github.com/aayostem/platform-toolkit
cd platform-toolkit

该仓库包含本指南中引用的所有清单文件、Helm值文件、Crossplane Compositions和Backstage模板。每个部分都对应仓库中的一个目录。

预计耗时:对于经验丰富的平台工程师来说,完整实现需要一到两天时间。第1至3部分可以在上午完成,并产出一个可用的GitOps交付层。

第1部分:IDP架构——三层模型

1.1 IDP的本质

内部开发者平台(IDP)不是一个工具,而是一个产品:平台团队构建和维护的一组工具、工作流程和抽象,使应用开发人员能够快速推进而无需直接管理基础设施。

这一区别至关重要,因为它影响着每一个架构决策。工具是安装和配置的,而产品是为用户设计的,根据反馈进行迭代,并通过用户是否实际采用来衡量。那些构建开发者喜爱的IDP的平台团队,思考方式像产品经理而非系统管理员。

DORA 2025报告发现,目前近90%的企业已拥有某种形式的内部平台。但拥有平台与拥有开发者实际使用的平台是两回事。

调查显示,开发者对内部平台的满意度差异巨大。满意团队与不满意团队之间的差距直接与平台团队是否将IDP视为有路线图和用户研究的产品,还是视为有工单队列的基础设施项目相关。

本指南中的三个工具——Backstage、ArgoCD和Crossplane——是2026年生产环境IDP最广泛采用的开源技术栈。但连接它们的架构本身与工具同样重要。

1.2 三层架构

生产环境IDP包含三个明确的层级,每个层级都有单一职责:

code
层1:开发者界面(Backstage)
├── 软件目录 —— 所有服务、API和资源的清单
├── 软件模板 —— 触发供应工作流程的自助服务表单
├── TechDocs —— 与每个目录实体共存的文档
└── 插件 —— 与ArgoCD、Kubernetes、PagerDuty、Grafana的集成

层2:交付层(ArgoCD)
├── GitOps同步 —— 持续将集群状态与Git保持一致
├── ApplicationSets —— 从单一定义中进行多环境部署
├── 发布管理 —— 带健康检查的渐进式交付
└── 审计追踪 —— 每个部署变更都链接到Git提交

层3:基础设施层(Crossplane)
├── 组合资源 —— 作为Kubernetes CRD定义的云资源
├── Compositions —— 将简单声明扩展为完整AWS基础设施的模板
├── ProviderConfigs —— 每个云提供商的凭证和区域配置
└── 使用跟踪 —— 每个已分配资源都标记团队和成本中心

关键架构原则:Backstage 从不直接与 Kubernetes 或云 API 通信。当开发者在 Backstage 提交 Software Template 时,输出是一个 Git 提交——一个表示 Crossplane 声明或 ArgoCD 应用程序清单的 YAML 文件。ArgoCD 会获取该提交并将其应用到集群。Crossplane 将集群资源转换为实际的云基础设施。

这种间接路径并非为了复杂而复杂。这意味着每次基础设施变更都对应一个 Git 提交,包含作者、时间戳、拉取请求和审查记录。审计追踪是自动化的。回滚机制是 git revert。

code
开发者 → Backstage 模板 → Git 提交 → ArgoCD → Crossplane → AWS
                                     ↑
                          单一真实来源
                          完整审计追踪
                          回滚 = git revert

错误的替代方案如下所示——Backstage 直接调用云 API:

code
// 错误:Backstage 模板直接调用 AWS SDK
// 没有审计追踪,没有回滚,没有 reconciliation 循环
// 如果调用中途失败,您将拥有无记录的部分基础设施
import { S3Client, CreateBucketCommand } from "@aws-sdk/client-s3";

const client = new S3Client({ region: "us-east-1" });
await client.send(new CreateBucketCommand({ Bucket: bucketName }));

正确的方法——Backstage 向 Git 输出 Crossplane 声明:

code
# 正确:Backstage 模板输出 —— 提交到 Git 的 Crossplane 声明
# ArgoCD 应用它,Crossplane 进行 reconciliation,AWS 创建存储桶
# 每个步骤都可追踪、可审计、可逆
apiVersion: platform.cloudfrugal.com/v1alpha1
kind: S3Bucket
metadata:
  name: ${{ values.bucket_name }}
  namespace: ${{ values.team_namespace }}
  labels:
    team: ${{ values.team_name }}
    cost-centre: ${{ values.cost_centre }}
    environment: ${{ values.environment }}
spec:
  versioning: true
  encryption: AES256
  region: us-east-1

1.3 实施顺序

按此顺序构建。偏离它会导致难以调试的集成问题:

code
步骤1:ArgoCD —— 所有其他内容依赖的交付基础
步骤2:Crossplane —— 基础设施控制平面,由 ArgoCD 提供
步骤3:Backstage —— 门户,指向 ArgoCD 和 Crossplane 作为后端
步骤4:连接集成 —— 生成 GitOps 清单的软件模板
步骤5:FinOps 层 —— 每个已配置资源中的成本归属元数据

第2部分:ArgoCD —— GitOps 基础

ArgoCD 是一个用于 Kubernetes 的声明式持续交付工具,它实现了 GitOps 模式。如果您之前没有使用过 GitOps 工具,核心思想很简单:您的 Git 仓库是集群中应运行内容的单一真实来源,ArgoCD 持续将实际集群状态与之对齐。

如果开发人员手动更改集群中的资源,ArgoCD 会检测到偏差并从 Git 重新同步。如果 Git 发生更改,ArgoCD 会将更改应用到集群。不需要人工干预,并且主动不鼓励人工干预——目标是集群状态始终完全由 Git 中的内容解释。

ArgoCD 是 CNCF 毕业项目,这意味着它已准备好生产使用且被广泛采用。它作为一组 Pod 运行在您的集群中,具有 Web UI、CLI 和 REST API。管理跨多个环境的部署所需的一切都位于一个地方。

2.1 安装 ArgoCD

code
# 创建 ArgoCD 命名空间
kubectl create namespace argocd

# 使用官方清单文件安装 ArgoCD
kubectl apply -n argocd \
  -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# 在继续之前等待所有 Pod 运行就绪
kubectl wait --for=condition=Ready pods \
  --all -n argocd --timeout=300s

# 获取初始管理员密码
argocd_password=$(kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d)

echo "ArgoCD 初始密码: $argocd_password"
echo "在继续之前请将此密码保存在安全的位置"

# 通过端口转发本地访问 ArgoCD UI
kubectl port-forward svc/argocd-server -n argocd 8080:443 &

# 通过 CLI 登录
argocd login localhost:8080 \
  --username admin \
  --password "$argocd_password" \
  --insecure

# 立即修改密码
argocd account update-password \
  --current-password "$argocd_password" \
  --new-password "your-secure-password"

2.2 GitOps 的仓库结构

ArgoCD 监控的仓库结构决定了你如何管理多个环境。最可扩展的模式是按目录划分环境,使用 Kustomize 管理覆盖配置。

Kustomize 是一个原生 Kubernetes 的配置管理工具,允许你定义一次基础配置,并在其上叠加环境特定的覆盖配置。这意味着你的预发布环境和生产环境配置共享相同的 YAML 结构,但会在副本数量、镜像标签和资源限制等方面存在差异。

code
gitops-repo/
├── apps/
│   ├── base/                    # 所有环境共享的配置
│   │   ├── payment-api/
│   │   │   ├── deployment.yaml
│   │   │   ├── service.yaml
│   │   │   └── kustomization.yaml
│   │   └── user-api/
│   │       ├── deployment.yaml
│   │       ├── service.yaml
│   │       └── kustomization.yaml
│   └── overlays/
│       ├── staging/             # 预发布环境的覆盖配置
│       │   ├── payment-api/
│       │   │   └── kustomization.yaml   # 覆盖配置:1 个副本,预发布镜像标签
│       │   └── kustomization.yaml
│       └── production/          # 生产环境的覆盖配置
│           ├── payment-api/
│           │   └── kustomization.yaml   # 覆盖配置:3 个副本,固定镜像标签
│           └── kustomization.yaml
└── infrastructure/
    ├── crossplane/              # Crossplane 安装和提供者
    ├── monitoring/              # Prometheus, Grafana
    └── ingress/                 # NGINX 或 ALB 入口控制器

2.3 ApplicationSets — 管理多个环境

ApplicationSet 是 ArgoCD 的一种资源,可以从单个模板生成多个 Application 对象。而不是为每个环境的每个服务创建单独的 Application 清单(这在规模扩大时会变得难以管理),你只需定义一个覆盖所有环境所有服务的 ApplicationSet。矩阵生成器会将环境列表与 Git 目录扫描相结合,自动生成所有可能的组合:

`

applicationset-apps.yaml

该资源为每个环境和应用程序目录的组合生成一个 ArgoCD 应用程序

apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: platform-apps namespace: argocd spec: generators:

  • matrix:

generators:

生成器 1:环境

  • list:

elements:

  • environment: staging

cluster: https://staging.eks.cluster.local

  • environment: production

cluster: https://production.eks.cluster.local

生成器 2:overlay 中的应用程序目录

  • git:

repoURL: https://github.com/your-org/gitops-repo revision: HEAD directories:

  • path: apps/overlays/{{environment}}/*

template: metadata: name: "{{environment}}-{{path.basename}}" labels: environment: "{{environment}}" app: "{{path.basename}}" spec: project: default source: repoURL: https://github.com/your-org/gitops-repo targetRevision: HEAD path: "apps/overlays/{{environment}}/{{path.basename}}" destination: server: "{{cluster}}" namespace: "{{path.basename}}" syncPolicy: automated: prune: true # 从 Git 中删除资源时进行清理 selfHeal: true # 撤销对集群的手动更改 syncOptions:

  • CreateNamespace=true
  • PrunePropagationPolicy=foreground
code

验证 ApplicationSet 是否生成了预期的应用程序:

列出所有生成的应用程序

kubectl get applications -n argocd

预期输出:每个环境和应用程序各一个

staging-payment-api Synced Healthy

staging-user-api Synced Healthy

production-payment-api Synced Healthy

production-user-api Synced Healthy

检查特定应用程序的同步状态

argocd app get staging-payment-api

code

### 2.4 平台团队的 ArgoCD RBAC

在多团队 IDP 中,不同团队需要对 ArgoCD 有不同级别的访问权限。应用程序团队应能查看和同步自己的应用程序。平台团队应拥有更广泛的访问权限。没有人应能通过 ArgoCD 获得无限制的集群管理员权限。

默认策略是只读 —— 每个经过身份验证的用户可以看到所有内容但无法进行任何更改:

argocd-rbac-configmap.yaml

apiVersion: v1 kind: ConfigMap metadata: name: argocd-rbac-cm namespace: argocd data: policy.default: role:readonly policy.csv: |

平台团队:对所有应用程序有完全访问权限

p, role:platform-team, applications, *, */*, allow p, role:platform-team, clusters, get, *, allow p, role:platform-team, repositories, *, *, allow

应用程序团队:仅能获取和同步自己的命名空间

p, role:app-team, applications, get, */staging-*, allow p, role:app-team, applications, sync, */staging-*, allow

将角色绑定到 GitHub 团队

g, your-org:platform-engineers, role:platform-team g, your-org:developers, role:app-team

scopes: '[groups]'

code

## 第 3 部分:Crossplane — 将基础设施作为 Kubernetes 资源

Crossplane 是一个 CNCF 毕业的开源框架,它将 Kubernetes 扩展为一个通用的基础设施控制平面。

核心思想:与其使用 Terraform 或 CloudFormation 等独立于集群的工具来管理云资源,不如将云资源(如 RDS 数据库、S3 存储桶、VPC 和 IAM 角色)定义为 Kubernetes 自定义资源定义。

当将 Crossplane 资源应用到集群后,Crossplane 的控制器会接管并协调期望状态与实际 AWS 状态的一致性,这一过程与 Kubernetes 协调 Deployment 到运行中的 Pod 集合的方式完全一致。

Crossplane 在此基础上增加的关键抽象是组合资源(Composite Resource)。平台团队可以定义一个高级的 PostgreSQLDatabase 类型,该类型封装了实际 RDS 实例所需的三十多个配置字段。

开发者只需与简化后的类型进行交互。Crossplane 会在后台将其扩展为完整的 AWS 资源配置,自动应用平台团队的安全和运维标准——这些标准开发者无法绕过,因为他们根本看不到底层字段。

### 3.1 安装 Crossplane

Crossplane 通过 ArgoCD 部署到你的集群——这是两个工具的首次集成。通过 ArgoCD Application 安装 Crossplane 而不是直接运行 helm install,可以将 Crossplane 本身纳入 GitOps 管理的基础设施中。任何对 Crossplane 配置的更改都需要经过 Git 提交和代码审查:

infrastructure/crossplane/application.yaml

通过 Helm 安装 Crossplane 的 ArgoCD Application

apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: crossplane namespace: argocd spec: project: default source: repoURL: https://charts.crossplane.io/stable chart: crossplane targetRevision: 1.15.0 helm: values: | provider: packages:

AWS 提供商 — 管理所有 AWS 资源

  • xpkg.upbound.io/upbound/provider-aws-s3:v1.2.0
  • xpkg.upbound.io/upbound/provider-aws-rds:v1.2.0
  • xpkg.upbound.io/upbound/provider-aws-iam:v1.2.0

destination: server: https://kubernetes.default.svc namespace: crossplane-system syncPolicy: automated: prune: true selfHeal: true syncOptions:

  • CreateNamespace=true
code

应用 ArgoCD Application — ArgoCD 安装 Crossplane

kubectl apply -f infrastructure/crossplane/application.yaml

观察 Crossplane Pod 启动

kubectl get pods -n crossplane-system -w

验证提供商是否已安装并处于健康状态

kubectl get providers

预期输出:

NAME INSTALLED HEALTHY PACKAGE

upbound-provider-aws-s3 True True xpkg.upbound.io/...

upbound-provider-aws-rds True True xpkg.upbound.io/...

code

### 3.2 提供商凭证

Crossplane 需要 AWS 凭证来创建资源。对于 EKS,推荐使用 IAM Roles for Service Accounts(IRSA)——这是一种允许 Kubernetes Pod 直接担任 IAM 角色而无需在集群中存储任何凭证的机制。

Pod 的 Kubernetes 服务账户会标注 IAM 角色 ARN,当 Pod 发起 API 调用时,AWS 会自动提供短期凭证。无需访问密钥,无需轮换密钥,也无需暴露凭证:

为Crossplane创建带有必要AWS权限的IAM角色

aws iam create-role \ --role-name CrossplaneProviderRole \ --assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::YOUR_ACCOUNT_ID:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/YOUR_OIDC_ID" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "oidc.eks.us-east-1.amazonaws.com/id/YOUR_OIDC_ID:sub": "system:serviceaccount:crossplane-system:provider-aws" } } }] }'

附加权限策略(生产环境应限制为最小必要权限)

aws iam attach-role-policy \ --role-name CrossplaneProviderRole \ --policy-arn arn:aws:iam::aws:policy/AdministratorAccess

code

provider-config.yaml

使用IRSA配置AWS提供者 —— 不使用静态凭证

apiVersion: aws.upbound.io/v1beta1 kind: ProviderConfig metadata: name: default spec: credentials: source: IRSA # 使用附加到提供者服务账户的IAM角色

code

### 3.3 定义复合资源 —— PostgreSQL数据库

这是IDP抽象的实现位置。平台团队定义两个YAML文件:定义开发者可请求资源形状的CompositeResourceDefinition(XRD),以及定义请求如何扩展为实际AWS资源并应用平台标准的Composition。

XRD是与开发者之间的API契约。保持简洁 —— 仅包含开发者真正需要控制的字段:

xrd-postgresql.yaml

定义开发者可请求的PostgreSQLDatabase类型

开发者永远看不到下方特定于RDS的配置

apiVersion: apiextensions.crossplane.io/v1 kind: CompositeResourceDefinition metadata: name: xpostgresqldatabases.platform.cloudfrugal.com spec: group: platform.cloudfrugal.com names: kind: XPostgreSQLDatabase plural: xpostgresqldatabases claimNames: kind: PostgreSQLDatabase # 这是开发者创建的资源类型 plural: postgresqldatabases versions:

  • name: v1alpha1

served: true referenceable: true schema: openAPIV3Schema: type: object properties: spec: type: object properties:

仅包含面向开发者的字段 —— 简单且有限

storageGB: type: integer minimum: 20 maximum: 1000 description: "存储空间(GB)。最小20,最大1000。" instanceClass: type: string enum: ["small", "medium", "large"] description: "small=db.t4g.medium, medium=db.r7g.large, large=db.r7g.2xlarge" environment: type: string enum: ["staging", "production"]

code

Composition是平台团队的实现。它将简单的开发者字段映射到完整的RDS配置,并强制执行开发者无法覆盖的平台标准:

composition-postgresql.yaml

定义 PostgreSQLDatabase 声明扩展后的资源内容

此处应用平台标准(加密、备份、删除保护)

开发者无法覆盖这些标准 — 平台强制实施这些标准

apiVersion: apiextensions.crossplane.io/v1 kind: Composition metadata: name: postgresql-aws-composition labels: provider: aws spec: compositeTypeRef: apiVersion: platform.cloudfrugal.com/v1alpha1 kind: XPostgreSQLDatabase

resources:

实际的 RDS 实例 — 从简单的开发者声明扩展而来

  • name: rds-instance

base: apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: forProvider: region: us-east-1 engine: postgres engineVersion: "15.4"

平台标准 — 始终应用,不可由开发者配置

storageEncrypted: true # 始终启用加密 backupRetentionPeriod: 7 # 始终启用7天备份 deletionProtection: true # 始终启用删除保护 multiAZ: false # 生产环境会覆盖为true(见补丁部分) dbSubnetGroupNameSelector: matchLabels: platform.cloudfrugal.com/subnet-group: private patches:

将开发者声明的简单实例类映射到实际的RDS实例类型

  • type: CombineFromComposite

combine: variables:

  • fromFieldPath: spec.instanceClass

strategy: string string: fmt: | %s toFieldPath: spec.forProvider.dbInstanceClass transforms:

  • type: map

map: small: db.t4g.medium medium: db.r7g.large large: db.r7g.2xlarge

自动为生产环境启用Multi-AZ

  • type: FromCompositeFieldPath

fromFieldPath: spec.environment toFieldPath: spec.forProvider.multiAZ transforms:

  • type: map

map: staging: "false" production: "true"

将声明中的团队标签复制到RDS实例用于成本归因

  • type: FromCompositeFieldPath

fromFieldPath: metadata.labels toFieldPath: spec.forProvider.tags

code

现在申请 PostgreSQL 数据库的开发者只需编写以下内容即可:

开发者在其团队的命名空间中创建此资源

不需要RDS知识。无需配置IAM。无需查找子网组。

apiVersion: platform.cloudfrugal.com/v1alpha1 kind: PostgreSQLDatabase metadata: name: payment-service-db namespace: payments-team labels: team: payments cost-centre: payments-engineering environment: staging spec: storageGB: 100 instanceClass: medium environment: staging

code

Crossplane 会在几分钟内将此声明转换为完整的RDS实例,自动应用加密、备份和所有平台标准。

### 3.4 验证 Crossplane 资源供应

查看声明状态 — 应该转变为Ready=True

kubectl get postgresqldatabases -n payments-team -w

检查组合资源以查看详细状态

kubectl describe xpostgresqldatabases.platform.cloudfrugal.com

code

# 验证实际 AWS 资源是否已创建
aws rds describe-db-instances \
  --query 'DBInstances[?TagList[?Key==`team` && Value==`payments`]].[DBInstanceIdentifier,DBInstanceStatus]' \
  --output table

第4部分:Backstage — 开发者门户

Backstage 是一个由 Spotify 最初构建的 CNCF 孵化中的开源框架。它作为 IDP 的开发者面向接口 —— 开发者在此单一场所可以发现服务、申请基础设施并查找文档,而无需了解任何底层系统提供这些内容。

Backstage 提供三项核心功能:

  • 一个软件目录,用于记录组织中每个服务、API、库和资源
  • 软件模板,为开发者提供自助服务表单以配置基础设施和搭建新服务
  • TechDocs,将文档与它所描述的目录实体集中存放,确保文档始终可以从覆盖该服务的相同位置找到。

Backstage 使用 TypeScript 构建,前端为 React,后端为 Node.js。它是通过配置而非安装来部署的:您创建一个 Backstage 应用,用组织的具体信息进行配置,然后将其部署到您的集群中。

4.1 创建和配置 Backstage

code
# 创建一个新的 Backstage 应用
npx @backstage/create-app@latest

# 当被提示时:
# 应用名称:platform-portal
# 本地开发选择 SQLite,生产环境选择 PostgreSQL

cd platform-portal

配置 Backstage 以连接到您的 ArgoCD 实例和 GitHub:

code
# app-config.production.yaml
app:

  baseUrl: https://platform.your-company.com

backend:
  baseUrl: https://platform.your-company.com
  database:
    client: pg
    connection:
      host: ${POSTGRES_HOST}
      port: 5432
      user: ${POSTGRES_USER}
      password: ${POSTGRES_PASSWORD}
      database: backstage

# GitHub 集成用于目录发现和模板脚手架
integrations:
  github:
    - host: github.com
      apps:
        - appId: ${GITHUB_APP_ID}
          webhookSecret: ${GITHUB_WEBHOOK_SECRET}
          clientId: ${GITHUB_CLIENT_ID}
          clientSecret: ${GITHUB_CLIENT_SECRET}
          privateKey: ${GITHUB_PRIVATE_KEY}

# ArgoCD 插件配置
argocd:
  username: ${ARGOCD_USERNAME}
  password: ${ARGOCD_PASSWORD}
  appLocatorMethods:
    - type: 'config'
      instances:
        - name: main
          url: https://argocd.your-company.com

# 目录自动发现 —— 在您的 GitHub 组织中查找 catalog-info.yaml 文件
catalog:
  providers:
    github:
      your-org:
        organization: 'your-github-org'
        catalogPath: '/catalog-info.yaml'
        filters:
          branch: 'main'

4.2 软件目录 —— 注册服务

平台上的每个服务、API、库和资源都应在 Backstage 目录中通过提交到服务仓库的 catalog-info.yaml 文件进行注册。Backstage 通过 GitHub 集成自动发现这些文件 —— 一旦文件存在,无需手动注册即可自动识别:

code
# catalog-info.yaml — 提交到每个服务仓库的根目录
# Backstage 通过 GitHub 集成会自动发现此文件
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payment-api

  description: "核心支付处理服务。负责交易发起、授权和结算。"
  annotations:
    # 将 ArgoCD 链接到 Backstage UI 显示部署状态
    argocd/app-name: production-payment-api
    # 链接 GitHub Actions 工作流状态
    github.com/project-slug: your-org/payment-api
    # 链接该服务的 Grafana 仪表盘
    grafana/dashboard-selector: "title=Payment API"
    # 链接 PagerDuty 值班时间表
    pagerduty.com/service-id: P123456
  tags:
    - 支付
    - typescript
    - 关键服务
  links:
    - url: https://payment-api.docs.your-company.com

    - url: https://grafana.your-company.com/d/payment-api

spec:
  type: 服务
  lifecycle: 生产环境
  owner: group:payments-team
  system: 支付平台
  dependsOn:
    - component:user-api
    - resource:payment-service-db
  providesApis:
    - payment-api-v2

4.3 软件模板 — 自助服务基础设施

软件模板是 Backstage 的一个表单,提交后会生成一个 Git 提交。该提交包含模板定义的任何 YAML、代码或配置。

对于基础设施配置,输出是一个 Crossplane 声明。对于新服务搭建,输出是一个完整的服务框架,提交到新仓库中。

关键设计决策:模板应创建拉取请求而非直接合并。PR 为平台团队提供可见性,为开发者提供审核机会,并为所有人提供审计轨迹。一旦在模板输出中建立信任,自动合并策略可以消除低风险配置的审核步骤:

code
# templates/postgresql-database/template.yaml
# 该模板为开发者提供一个表单以申请 PostgreSQL 数据库
# 输出是一个提交到 GitOps 仓库的 Crossplane PostgreSQLDatabase 声明
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: postgresql-database

  description: 在 AWS RDS 上配置托管的 PostgreSQL 数据库。平台会自动配置加密、备份和删除保护。
  tags:
    - 数据库
    - postgresql
    - aws
spec:
  owner: group:platform-team
  type: 基础设施

  # 开发者在 Backstage UI 中填写的表单
  parameters:
    - title: 数据库配置
      required: [name, team, environment, storageGB, instanceClass]
      properties:
        name:

          type: string
          description: "仅小写字母和连字符。例如 payment-service-db"
          pattern: '^[a-z][a-z0-9-]*$'

        team:

          type: string
          description: "您的团队名称。用于成本归属和所有权。"
          ui:field: OwnerPicker
          ui:options:
            catalogFilter:
              kind: Group

        environment:

          type: string
          enum: [staging, production]
          default: staging

        storageGB:

          type: integer
          minimum: 20
          maximum: 1000
          default: 50
yaml
        instanceClass:

          type: string
          enum: [small, medium, large]
          enumNames:
            - "小型 (db.t4g.medium) — 开发/预发布工作负载"
            - "中型 (db.r7g.large) — 中等生产流量"
            - "大型 (db.r7g.2xlarge) — 高吞吐量生产环境"
          default: small

  # 提交模板后执行的操作
  steps:
    - id: generate-claim
      name: 生成 Crossplane 声明
      action: fetch:template
      input:
        url: ./skeleton    # 包含 Crossplane 声明 YAML 模板
        values:
          name: ${{ parameters.name }}
          team: ${{ parameters.team | parseEntityRef | pick('name') }}
          environment: ${{ parameters.environment }}
          storageGB: ${{ parameters.storageGB }}
          instanceClass: ${{ parameters.instanceClass }}

    - id: create-pr
      name: 向 GitOps 仓库创建 Pull Request
      action: publish:github:pull-request
      input:
        repoUrl: github.com?repo=gitops-repo&owner=your-org

        branchName: "provision-db-${{ parameters.name }}-${{ '' | now }}"
        description: |
          请求通过 Crossplane 部署 PostgreSQL 数据库。

          - **名称:** ${{ parameters.name }}
          - **团队:** ${{ parameters.team }}
          - **环境:** ${{ parameters.environment }}
          - **存储:** ${{ parameters.storageGB }}GB
          - **实例规格:** ${{ parameters.instanceClass }}

          批准此 PR 以触发部署。ArgoCD 将检测到变更,Crossplane 在合并后约 5 分钟内创建 RDS 实例。
        sourcePath: ./skeleton

  output:
    links:
      - title: 查看 Pull Request
        url: ${{ steps['create-pr'].output.remoteUrl }}
      - title: 在 ArgoCD 中跟踪部署
        url: https://argocd.your-company.com/applications

模板骨架目录包含带有模板变量占位符的 Crossplane 声明:

code
# templates/postgresql-database/skeleton/databases/${{ values.name }}.yaml
apiVersion: platform.cloudfrugal.com/v1alpha1
kind: PostgreSQLDatabase
metadata:
  name: ${{ values.name }}
  namespace: ${{ values.team }}-platform
  labels:
    team: ${{ values.team }}
    cost-centre: ${{ values.team }}-engineering
    environment: ${{ values.environment }}
    managed-by: backstage-scaffolder
spec:
  storageGB: ${{ values.storageGB }}
  instanceClass: ${{ values.instanceClass }}
  environment: ${{ values.environment }}

第5部分:整合流程 —— 黄金路径

黄金路径是完整的端到端工作流:开发人员使用 Backstage 请求基础设施,该请求转化为 Git 提交,ArgoCD 将提交应用到集群,Crossplane 在 AWS 上实际创建资源,最终结果会同时显示在 Backstage 目录和 ArgoCD 控制面板中。

5.1 完整流程

/think

code
开发者在 Backstage 填写表单
    ↓
Backstage 软件模板渲染 Crossplane 声明 YAML
    ↓
Backstage 在 GitOps 仓库中创建 Pull Request
    ↓
平台工程师(或自动合并策略)批准并合并 PR
    ↓
ArgoCD 检测到 GitOps 仓库中的新文件
    ↓
ArgoCD 将 Crossplane 声明应用到集群
    ↓
Crossplane 将声明同步为实际的 AWS RDS 实例
    ↓
开发者通过 Kubernetes Secret 接收数据库端点
    ↓
Backstage 目录显示新资源,由请求团队拥有

5.2 在 Backstage 中展示资源状态

Backstage 的 Kubernetes 插件从您的集群中拉取实时 Pod 和资源状态,并在每个目录实体页面上显示。开发者无需离开 Backstage 或学习 kubectl 即可查看服务是否运行、健康副本数量以及上次部署是否同步:

code
# 安装 Kubernetes 插件包
cd platform-portal
yarn --cwd packages/app add @backstage/plugin-kubernetes
yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend
code
# app-config.production.yaml — 添加 Kubernetes 集群配置
kubernetes:
  serviceLocatorMethod:
    type: 'multiTenant'
  clusterLocatorMethods:
    - type: 'config'
      clusters:
        - name: production-eks
          url: ${PRODUCTION_CLUSTER_URL}
          authProvider: serviceAccount
          serviceAccountToken: ${PRODUCTION_SA_TOKEN}
          caData: ${PRODUCTION_CA_DATA}
        - name: staging-eks
          url: ${STAGING_CLUSTER_URL}
          authProvider: serviceAccount
          serviceAccountToken: ${STAGING_SA_TOKEN}
          caData: ${STAGING_CA_DATA}

为每个目录实体添加注解以链接到其 Kubernetes 资源:

code
# 在每个服务的 catalog-info.yaml 中
annotations:
  backstage.io/kubernetes-label-selector: 'app=payment-api'
  backstage.io/kubernetes-namespace: payments-team

5.3 安装 ArgoCD 插件

ArgoCD 插件直接在 Backstage 实体页面中显示部署历史和同步状态。当开发者在目录中打开 payment-api 页面时,他们可以看到最近 10 次部署、当前同步状态以及应用是否健康 —— 无需打开 ArgoCD UI:

code
yarn --cwd packages/app add @roadiehq/backstage-plugin-argo-cd
code
// packages/app/src/components/catalog/EntityPage.tsx
import { EntityArgoCDOverviewCard } from '@roadiehq/backstage-plugin-argo-cd';

// 添加到服务实体页面布局
const serviceEntityPage = (
  <EntityLayout>
    <EntityLayout.Route path="/" title="Overview">
      <Grid container spacing={3}>
        <Grid item md={6}>
          <EntityAboutCard variant="gridItem" />
        </Grid>
        <Grid item md={6}>
          {/* ArgoCD 部署状态 —— 显示同步状态和最近部署 */}
          <EntityArgoCDOverviewCard />
        </Grid>
      </Grid>
    </EntityLayout.Route>
  </EntityLayout>
);

第6部分:FinOps 集成 —— 在 IDP 上的成本归属

没有成本归属的 IDP 资源供应会带来新问题:现在您拥有自动化基础设施供应,但无法明确账单归属。通过 IDP 创建的每个资源必须从供应时起携带团队和成本中心元数据。

6.1 每个 Crossplane 组合的强制标签

code

Crossplane Compositions 是强制执行成本归属的关键位置 —— 不是在面向开发者的声明中,而是在开发者无法绕过的平台层。这些标签会传递到实际的 AWS 资源作为标签,这意味着它们会出现在 AWS Cost Explorer 中,并可用于生成团队级别的成本报告:

在每个 Composition 中添加强制性的成本归属补丁

patches:

这些标签会传递到实际的 AWS 资源作为标签

开发者声明无法省略或覆盖这些标签

  • type: FromCompositeFieldPath

fromFieldPath: metadata.labels[team] toFieldPath: spec.forProvider.tags[team]

  • type: FromCompositeFieldPath

fromFieldPath: metadata.labels[cost-centre] toFieldPath: spec.forProvider.tags[cost-centre]

  • type: FromCompositeFieldPath

fromFieldPath: metadata.labels[environment] toFieldPath: spec.forProvider.tags[environment]

添加 managed-by 标签以标识所有通过 IDP 部署的资源

  • type: FromCompositeFieldPath

fromFieldPath: metadata.name toFieldPath: spec.forProvider.tags[managed-by] transforms:

  • type: string

string: fmt: "idp-crossplane"

code

### 6.2 成本归属查询

通过在每个资源上添加强制性标签,你可以直接从 AWS Cost Explorer 查询团队的实际成本:

按团队划分的月度成本 —— 所有通过 IDP 部署的资源

aws ce get-cost-and-usage \ --time-period Start=$(date -d 'last month' +%Y-%m-01),End=$(date +%Y-%m-01) \ --granularity MONTHLY \ --filter '{ "Tags": { "Key": "managed-by", "Values": ["idp-crossplane"] } }' \ --group-by Type=TAG,Key=team \ --metrics UnblendedCost \ --query 'ResultsByTime[0].Groups[*].{Team:Keys[0],Cost:Metrics.UnblendedCost.Amount}' \ --output table

code

现在每个通过 IDP 部署资源的团队都会在成本报告中拥有自己的条目并显示团队名称。这种自动化的成本归属模式使 FinOps 在平台规模上得以持续运行 —— 归属是自动完成的,而非人工操作。

## 第7部分:平台成熟度模型 —— 评估你已构建的内容

CNCF 平台工程成熟度模型定义了五个平台成熟度级别。了解你所处的阶段有助于决定下一步要构建什么,并向工程领导层传达进展。

| 等级 | 名称       | 特征描述                                       |
|------|------------|------------------------------------------------|
| 1    | 临时性     | 临时脚本、手动部署、没有标准工具               |
| 2    | 运营性     | 标准化工具、部分自动化、正在使用 Kubernetes    |
| 3    | 可扩展性   | 自助服务门户、GitOps 部署、文档化的黄金路径    |
| 4    | 优化中     | 成本归属、平台自身的 SLO、用户反馈循环         |
| 5    | 优化完成   | AI 辅助部署、预测性扩展、完整的 FinOps 集成    |

完整的 Backstage + ArgoCD + Crossplane 实现,包含成本归属和覆盖常见开发者需求的软件模板,将使你达到等级 3。要升级到等级 4,需要在平台自身健康状况上添加 SLO 告警、每季度进行开发者体验调查,并从归属标签生成月度按团队划分的成本报告。

等级 3 最常见的错误:构建更多功能而非衡量采用率。一个拥有 12 个软件模板但只有 2 个被频繁使用的平台并未达到等级 3 —— 它实际上处于等级 2,只是拥有更多 YAML。要衡量哪些黄金路径被使用,采访未使用门户的开发者,并在添加新功能前解决摩擦点。

## 最佳实践总结

✅ 做法:按顺序构建 —— 先构建 ArgoCD,然后是 Crossplane,最后是 Backstage。每一层都依赖于前一层。

✅ 做法:将 Backstage 用作 Git 提交生成器,而非基础设施调用工具。所有基础设施变更必须通过可审计的 Git 提交实现。

✅ 做法:在 Crossplane 的 Composition 层应用成本归属标签,而非在开发者声明中设置。开发者可绕过的归属机制最终会被绕过。

✅ 做法:从两到三个软件模板起步,并在构建更多模板前确保它们的卓越性。模板采用率是早期最重要的衡量指标。

✅ 做法:从第一天起就将每个服务注册到 Backstage 目录中。目录的价值与其覆盖范围成正比。

✅ 做法:通过 ArgoCD 而非 helm install 将 Crossplane 部署到集群。IDP 管理的所有内容都应由 IDP 自身管理。

❌ 不要:直接将 Backstage 连接到云 API。这会导致无审计追踪、无法回滚、无一致性保障。

❌ 不要:直接向开发者提供 Crossplane XRD。Composition 抽象层的存在是为了隐藏 RDS 特定配置并强制平台标准。绕过它将违背其初衷。

❌ 不要:孤立构建 IDP 并宣布完成。平台工程即产品工程。在前两个模板上线后安排用户访谈。

❌ 不要:跳过 ArgoCD 的 RBAC 配置。通过交付层向所有开发者授予 cluster-admin 权限的 IDP 会制造比它解决的问题更大的安全风险。

## 资源

- Backstage 文档 —— 插件开发、软件模板和目录配置的官方参考

- Crossplane 文档 —— CompositeResourceDefinition 和 Composition 参考,提供者安装指南

- ArgoCD 文档 —— ApplicationSet 生成器参考、RBAC 配置和同步策略选项

- CNCF 平台工程成熟度模型 —— 第 7 部分引用的成熟度框架

- Crossplane 的 AWS 提供者 —— Crossplane 可用所有 AWS 资源类型的完整参考

- Backstage Kubernetes 插件 —— 第 5 部分 Kubernetes 资源可视化的设置指南

- FinOps 基金会 —— 面向平台工程的 FinOps —— 第 6 部分成本归属模型的框架参考

- 附加工具库 —— 本指南中所有清单、Composition、ApplicationSet 和 Backstage 模板

- 2025 年 DORA 人工智能辅助软件开发现状报告

Ayobami Adejumo 是专注于种子轮和 A 轮公司基础设施转型的高级平台工程师。他曾就职于埃克森美孚并参与多个 B2B SaaS 团队的工作。

如果本文对你有帮助,请分享它。

免费学习编程。freeCodeCamp 的开源课程已帮助超过 40,000 人成为开发者。立即开始

ADVERTISEMENT