Backstage:Internal Developer Platform 框架
支持版本: Backstage 1.35+, Kubernetes 1.31+, EKS 最后更新: June 22, 2026
目录
- 概述
- 学习目标
- Backstage 架构
- EKS 部署
- Software Catalog
- Software Templates (Golden Paths)
- TechDocs
- 面向 EKS 的 Plugin 生态系统
- RBAC 与治理
- 生产运维
- 最佳实践
- 参考资料
概述
什么是 Internal Developer Platform (IDP)?
Internal Developer Platform (IDP) 是位于开发者与底层基础设施之间的自助服务层,为部署应用、预置资源和管理服务提供标准化工作流。正如 Platform Engineering Overview 中所述,IDP 通过抽象运维复杂性,让开发者能够专注于编写代码。
IDP 通常提供:
- Service Catalog:所有服务、API 和基础设施组件的集中式注册表
- 自助服务工作流:用于创建新服务、数据库和环境的模板化流程
- 文档中心:集中化、可发现的技术文档
- 可见性:跨组织实时查看部署、成本、所有权和健康状况
为什么选择 Backstage?
Backstage 是一个用于构建 Internal Developer Platform 的开源框架,最初由 Spotify 创建,现在是 CNCF Incubating 项目。Spotify 构建 Backstage 是为了管理数百个工程团队中的 2,000 多个 microservices(微服务)。在 2020 年开源后,Backstage 已成为 Kubernetes 生态系统中采用最广泛的 IDP 框架。
选择 Backstage 的主要原因:
- 开源且可扩展:采用 MIT 许可证,并拥有由 200+ 社区 plugins 组成的活跃 plugin 生态系统
- CNCF 支持:Incubating 项目,具备强治理能力,确保长期可持续性
- Plugin 架构:一切皆 plugin,无需 fork 即可高度定制
- Software Catalog:集中式所有权注册表,可建模你的整个技术生态系统
- Software Templates:Golden Path 脚手架,从第一天起强制执行组织标准
- TechDocs:docs-like-code 方法,将文档与其描述的代码放在一起
- 活跃社区:超过 900 位贡献者,并被 American Airlines、Netflix、Zalando 和 HP 等公司采用
IDP 平台对比
| Criteria | Backstage | Port | Cortex | Humanitec | OpsLevel |
|---|---|---|---|---|---|
| 许可证 | 开源 (MIT) | 商业(免费层) | 商业 | 商业 | 商业 |
| 托管方式 | 自托管 | SaaS | SaaS | SaaS + Agent | SaaS |
| 定制能力 | 不受限制(plugin system) | API + Blueprints | 有限 | 中等 | 中等 |
| Kubernetes Native | 强(plugins) | 基于 API | 有限 | 强(Score) | 基于 API |
| 设置复杂度 | 高(DIY) | 低 | 低 | 中 | 低 |
| 社区生态系统 | 200+ plugins | 不断增长的市场 | 有限 | 不断增长 | 有限 |
| 成本 | 仅基础设施 | 每用户/月 | 每用户/月 | 每用户/月 | 每用户/月 |
注意:与 SaaS 替代方案相比,Backstage 需要更多初始投入来完成设置,但它提供无可比拟的灵活性且没有许可成本。对于已经投入 Kubernetes 和 GitOps 的组织,Backstage 可以自然地集成到现有生态系统中。
学习目标
完成本文档后,你将能够:
- 解释 Backstage 作为 IDP 框架在 Kubernetes 平台工程技术栈中的作用
- 使用 Helm 在 Amazon EKS 上部署 Backstage,并配置 RDS PostgreSQL 和 ALB ingress
- 配置 Software Catalog,从 GitHub repositories 自动发现服务
- 创建 Software Templates (Golden Paths),用于生成包含 CI/CD 和基础设施的 microservices 脚手架
- 将 Backstage 与 Kubernetes plugin 集成,以在 EKS 上实现实时 workload 可见性
- 设置 使用 S3 storage 的 TechDocs,用于集中式文档
- 安装并配置 ArgoCD、Kubecost 以及其他 Kubernetes 生态系统工具的 plugins
- 为 多团队环境实现 RBAC 和治理策略
- 以 高可用、备份和升级策略在生产环境中运行 Backstage
Backstage 架构
高层架构
核心概念
Backstage 围绕四个基础支柱构建:
Software Catalog:组织中所有软件的集中式、自动更新的清单。它为每个组件跟踪所有权、依赖关系、API、文档链接和运维元数据。
Software Templates:可复用的脚手架定义(Golden Paths),用于创建包含全部组织标准的新项目、服务或基础设施 -- CI/CD pipelines、监控、安全策略和文档结构。
TechDocs:一种 docs-like-code 解决方案,可将 Markdown 文档(通过 MkDocs)直接渲染到 Backstage portal 中。文档与代码位于同一 repository,确保其保持最新。
Search:统一搜索平台,会索引 catalog、TechDocs 和任何其他数据源,为发现工程组织中的任何内容提供单一搜索栏。
Plugin 架构
Backstage 遵循“一切皆 plugin”的理念。即使 Software Catalog 这样的核心功能也是以 plugins 形式实现的。该架构提供:
- Frontend Plugins:用于渲染 UI 元素(页面、卡片、标签页)的 React components
- Backend Plugins:提供 APIs、处理数据处理并与外部系统集成的 Node.js modules
- Plugin 隔离:每个 plugin 都有自己的数据库 schema、API routes 和配置
- 可组合性:Plugins 可以通过定义良好的 extension points 依赖并扩展其他 plugins
┌─────────────────────────────────────────────────────────────────┐
│ Backstage App Shell │
├─────────────────────────────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Catalog │ │Templates │ │ TechDocs │ │ Search │ │
│ │ Plugin │ │ Plugin │ │ Plugin │ │ Plugin │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ K8s │ │ ArgoCD │ │ Kubecost │ │ Custom │ │
│ │ Plugin │ │ Plugin │ │ Plugin │ │ Plugins │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Plugin API / Extension Points │
├─────────────────────────────────────────────────────────────────┤
│ Backend Services (Node.js) │
├─────────────────────────────────────────────────────────────────┤
│ PostgreSQL │ S3 / Cache │
└─────────────────────────────────────────────────────────────────┘一个 plugin 通常包含:
| Component | Location | Purpose |
|---|---|---|
| Frontend Plugin | plugins/<name>/ | React components、routes、API clients |
| Backend Plugin | plugins/<name>-backend/ | Express routers、database access、external API calls |
| Common | plugins/<name>-common/ | Shared types、constants、API definitions |
| Node | plugins/<name>-node/ | Shared backend utilities、extension points |
EKS 部署
前提条件
在 EKS 上部署 Backstage 之前,请确保以下资源可用:
| Resource | Purpose | Notes |
|---|---|---|
| EKS Cluster | Backstage runtime | Kubernetes 1.31+ |
| Amazon RDS (PostgreSQL) | 持久化存储 | PostgreSQL 15+, db.r6g.large 或更大 |
| Amazon ECR | Container image registry | Backstage images 的私有 repository |
| AWS ALB | Ingress controller | 已安装 AWS Load Balancer Controller |
| Amazon S3 | TechDocs storage | 用于生成文档的 Bucket |
| AWS Certificate Manager | TLS certificate | 用于 ALB 上的 HTTPS |
| Amazon Cognito or Okta | OIDC authentication | 用户登录的 identity provider |
| GitHub App or Token | Source code integration | 用于 catalog discovery 和 template scaffolding |
步骤 1:创建 Backstage Application
首先在本地生成一个新的 Backstage application 脚手架:
# Ensure Node.js 20+ and Yarn are installed
node --version # v20.x or higher
yarn --version # 4.x (Berry)
# Create a new Backstage app
npx @backstage/create-app@latest --skip-install
# When prompted, enter the app name: my-backstage-app
cd my-backstage-app
# Install dependencies
yarn install生成的项目结构:
my-backstage-app/
├── app-config.yaml # Main configuration
├── app-config.production.yaml # Production overrides
├── catalog-info.yaml # Backstage's own catalog entry
├── package.json
├── packages/
│ ├── app/ # Frontend (React)
│ │ ├── src/
│ │ └── package.json
│ └── backend/ # Backend (Node.js)
│ ├── src/
│ └── package.json
├── plugins/ # Custom plugins
└── yarn.lock步骤 2:将 Application 容器化
为生产部署创建一个 multi-stage Dockerfile:
# Stage 1: Build the frontend and backend
FROM node:20-bookworm-slim AS build
WORKDIR /app
# Copy dependency files
COPY package.json yarn.lock .yarnrc.yml ./
COPY .yarn ./.yarn
COPY packages/app/package.json ./packages/app/
COPY packages/backend/package.json ./packages/backend/
COPY plugins/ ./plugins/
# Install all dependencies
RUN yarn install --immutable
# Copy the rest of the source
COPY . .
# Build the app
RUN yarn tsc
RUN yarn build:backend --config ../../app-config.yaml
# Stage 2: Production image
FROM node:20-bookworm-slim
# Install runtime dependencies for TechDocs
RUN apt-get update && \
apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv git curl && \
python3 -m pip install --break-system-packages \
mkdocs-techdocs-core==1.4.* && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Create a non-root user
RUN useradd -m -u 1000 backstage
USER backstage
WORKDIR /app
# Copy the built backend bundle
COPY --from=build --chown=backstage:backstage /app/packages/backend/dist ./packages/backend/dist
COPY --from=build --chown=backstage:backstage /app/node_modules ./node_modules
COPY --from=build --chown=backstage:backstage /app/package.json ./
# Copy configuration files
COPY --chown=backstage:backstage app-config.yaml app-config.production.yaml ./
# Environment variables
ENV NODE_ENV=production
# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
CMD curl -f http://localhost:7007/healthcheck || exit 1
EXPOSE 7007
CMD ["node", "packages/backend/dist", "--config", "app-config.yaml", "--config", "app-config.production.yaml"]构建 image 并推送到 ECR:
# Authenticate to ECR
aws ecr get-login-password --region ap-northeast-2 | \
docker login --username AWS --password-stdin \
111122223333.dkr.ecr.ap-northeast-2.amazonaws.com
# Build and push
docker build -t backstage:latest .
docker tag backstage:latest \
111122223333.dkr.ecr.ap-northeast-2.amazonaws.com/backstage:v1.35.0
docker push \
111122223333.dkr.ecr.ap-northeast-2.amazonaws.com/backstage:v1.35.0步骤 3:使用 Helm 部署
添加 Backstage Helm chart repository 并创建 values 文件:
helm repo add backstage https://backstage.github.io/charts
helm repo update创建一个完整的 values.yaml:
# backstage-values.yaml
backstage:
image:
registry: 111122223333.dkr.ecr.ap-northeast-2.amazonaws.com
repository: backstage
tag: v1.35.0
pullPolicy: IfNotPresent
replicas: 2
resources:
requests:
memory: 512Mi
cpu: 250m
limits:
memory: 1Gi
cpu: 1000m
extraEnvVarsSecrets:
- backstage-secrets
appConfig:
app:
title: "My Company Developer Portal"
baseUrl: https://backstage.example.com
backend:
baseUrl: https://backstage.example.com
listen:
port: 7007
cors:
origin: https://backstage.example.com
methods: [GET, HEAD, PATCH, POST, PUT, DELETE]
credentials: true
database:
client: pg
connection:
host: ${POSTGRES_HOST}
port: ${POSTGRES_PORT}
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
database: backstage
ssl:
require: true
rejectUnauthorized: true
podAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "7007"
prometheus.io/path: "/metrics"
serviceAccount:
create: true
name: backstage
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::111122223333:role/backstage-irsa-role
ingress:
enabled: true
className: alb
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-2:111122223333:certificate/abcd-1234-efgh
alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS13-1-2-2021-06
alb.ingress.kubernetes.io/healthcheck-path: /healthcheck
alb.ingress.kubernetes.io/group.name: backstage
hosts:
- host: backstage.example.com
paths:
- path: /
pathType: Prefix
postgresql:
enabled: false # Using external RDS
serviceAccount:
create: true
name: backstage部署到 EKS:
# Create the namespace
kubectl create namespace backstage
# Create the secrets (referencing values from AWS Secrets Manager or SSM)
kubectl create secret generic backstage-secrets \
--namespace backstage \
--from-literal=POSTGRES_HOST=backstage-db.cluster-xxxxxxx.ap-northeast-2.rds.amazonaws.com \
--from-literal=POSTGRES_PORT=5432 \
--from-literal=POSTGRES_USER=backstage \
--from-literal=POSTGRES_PASSWORD='<secure-password>' \
--from-literal=GITHUB_TOKEN='ghp_xxxxxxxxxxxxxxxxxxxx' \
--from-literal=AUTH_OIDC_CLIENT_ID='<client-id>' \
--from-literal=AUTH_OIDC_CLIENT_SECRET='<client-secret>'
# Install the chart
helm install backstage backstage/backstage \
--namespace backstage \
--values backstage-values.yaml \
--wait --timeout 10m步骤 4:ALB Ingress 配置
如果你希望独立于 Helm chart 管理 Ingress resource,可以显式创建它:
# backstage-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: backstage
namespace: backstage
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-2:111122223333:certificate/abcd-1234-efgh
alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS13-1-2-2021-06
alb.ingress.kubernetes.io/healthcheck-path: /healthcheck
alb.ingress.kubernetes.io/healthcheck-interval-seconds: "15"
alb.ingress.kubernetes.io/healthy-threshold-count: "2"
alb.ingress.kubernetes.io/unhealthy-threshold-count: "3"
alb.ingress.kubernetes.io/group.name: backstage
alb.ingress.kubernetes.io/tags: Environment=production,Team=platform
alb.ingress.kubernetes.io/load-balancer-attributes: >-
idle_timeout.timeout_seconds=120,
routing.http.drop_invalid_header_fields.enabled=true
spec:
ingressClassName: alb
rules:
- host: backstage.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: backstage
port:
number: 7007kubectl apply -f backstage-ingress.yaml步骤 5:通过 RDS 使用 PostgreSQL
数据库连接在 app-config.production.yaml 中配置。以下示例展示了包含连接池和 SSL 的完整数据库部分:
# app-config.production.yaml
backend:
database:
client: pg
connection:
host: ${POSTGRES_HOST}
port: ${POSTGRES_PORT}
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
database: backstage
ssl:
require: true
rejectUnauthorized: true
knexConfig:
pool:
min: 3
max: 12
acquireTimeoutMillis: 60000
idleTimeoutMillis: 30000
plugin:
catalog:
connection:
database: backstage_plugin_catalog
scaffolder:
connection:
database: backstage_plugin_scaffolder
auth:
connection:
database: backstage_plugin_auth
search:
connection:
database: backstage_plugin_search注意:Backstage 支持按 plugin 进行数据库隔离。每个 plugin 都可以在同一个 PostgreSQL instance 中使用独立数据库,从而提升安全性并简化备份和迁移。
步骤 6:OIDC Authentication 设置
Backstage 支持多个 authentication providers。下面是 Amazon Cognito 和 Okta 的配置。
Amazon Cognito 配置
# app-config.production.yaml
auth:
environment: production
providers:
oidc:
production:
metadataUrl: https://cognito-idp.ap-northeast-2.amazonaws.com/<user-pool-id>/.well-known/openid-configuration
clientId: ${AUTH_OIDC_CLIENT_ID}
clientSecret: ${AUTH_OIDC_CLIENT_SECRET}
authorizationUrl: https://<domain>.auth.ap-northeast-2.amazoncognito.com/oauth2/authorize
tokenUrl: https://<domain>.auth.ap-northeast-2.amazoncognito.com/oauth2/token
scope: openid profile email
prompt: auto
session:
secret: ${AUTH_SESSION_SECRET}Okta 配置
# app-config.production.yaml
auth:
environment: production
providers:
okta:
production:
clientId: ${AUTH_OKTA_CLIENT_ID}
clientSecret: ${AUTH_OKTA_CLIENT_SECRET}
audience: https://dev-123456.okta.com
authServerId: default
idp: ${AUTH_OKTA_IDP_ID}
session:
secret: ${AUTH_SESSION_SECRET}Sign-In Resolver 配置
在 backend plugin 代码中,配置 OIDC identities 如何映射到 Backstage users:
// packages/backend/src/plugins/auth.ts
import { createBackendModule } from '@backstage/backend-plugin-api';
import {
authProvidersExtensionPoint,
createOAuthProviderFactory,
} from '@backstage/plugin-auth-node';
import { oidcAuthenticator } from '@backstage/plugin-auth-backend-module-oidc-provider';
export const authModuleOidc = createBackendModule({
pluginId: 'auth',
moduleId: 'oidc',
register(reg) {
reg.registerInit({
deps: { providers: authProvidersExtensionPoint },
async init({ providers }) {
providers.registerProvider({
providerId: 'oidc',
factory: createOAuthProviderFactory({
authenticator: oidcAuthenticator,
async signInResolver({ result }, ctx) {
const email = result.userinfo.email;
if (!email) {
throw new Error('OIDC login did not provide an email');
}
return ctx.signInWithCatalogUser({
filter: { 'spec.profile.email': email },
});
},
}),
});
},
});
},
});Software Catalog
Entity Model
Backstage Software Catalog 使用定义良好的 entity model 来表示组织的软件生态系统。理解该模型对于有效管理 catalog 至关重要。
Entity Types
| Entity Kind | Description | Example |
|---|---|---|
| Component | 软件组件(service、website、library) | order-service, payment-api |
| System | 构成产品的一组 components | order-platform, data-pipeline |
| Domain | 将相关 systems 分组的业务 domain | commerce, fulfillment |
| API | 由 component 暴露的接口 | order-api, payment-grpc |
| Resource | 基础设施依赖 | order-db, events-queue |
| Group | 团队或组织单元 | platform-team, commerce-team |
| User | 单个个人 | john.doe |
catalog-info.yaml 示例
Component Entity
# catalog-info.yaml (in the service repository root)
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: order-service
description: Handles order creation, updates, and lifecycle management
labels:
app.kubernetes.io/name: order-service
annotations:
backstage.io/techdocs-ref: dir:.
github.com/project-slug: my-org/order-service
backstage.io/kubernetes-id: order-service
argocd/app-name: order-service
backstage.io/kubernetes-namespace: commerce
tags:
- java
- spring-boot
- grpc
links:
- url: https://grafana.example.com/d/order-service
title: Grafana Dashboard
icon: dashboard
- url: https://runbook.example.com/order-service
title: Runbook
icon: docs
spec:
type: service
lifecycle: production
owner: group:commerce-team
system: order-platform
providesApis:
- order-api
consumesApis:
- payment-api
- inventory-api
dependsOn:
- resource:order-db
- resource:order-events-queueSystem Entity
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
name: order-platform
description: End-to-end order management platform including order processing, payment, and fulfillment
annotations:
backstage.io/techdocs-ref: dir:.
tags:
- commerce
- critical
spec:
owner: group:commerce-team
domain: commerceDomain Entity
apiVersion: backstage.io/v1alpha1
kind: Domain
metadata:
name: commerce
description: All systems related to the online commerce experience including ordering, payments, and fulfillment
spec:
owner: group:commerce-leadershipAPI Entity
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
name: order-api
description: REST API for order management operations
tags:
- rest
- json
spec:
type: openapi
lifecycle: production
owner: group:commerce-team
system: order-platform
definition: |
openapi: "3.0.0"
info:
title: Order API
version: 1.0.0
paths:
/orders:
get:
summary: List orders
responses:
'200':
description: A list of orders
post:
summary: Create an order
responses:
'201':
description: Order created
/orders/{orderId}:
get:
summary: Get order by ID
parameters:
- name: orderId
in: path
required: true
schema:
type: string
responses:
'200':
description: Order detailsResource Entity
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
name: order-db
description: Aurora PostgreSQL cluster for order data
annotations:
aws.amazon.com/rds-cluster-id: order-db-cluster
tags:
- postgresql
- aurora
spec:
type: database
owner: group:commerce-team
system: order-platformGroup Entity
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
name: commerce-team
description: Commerce engineering team responsible for the order platform
spec:
type: team
profile:
displayName: Commerce Team
email: commerce-team@example.com
picture: https://avatars.example.com/commerce-team.png
parent: engineering
children: []
members:
- john.doe
- jane.smith
- alex.kimUser Entity
apiVersion: backstage.io/v1alpha1
kind: User
metadata:
name: john.doe
description: Senior Backend Engineer
spec:
profile:
displayName: John Doe
email: john.doe@example.com
picture: https://avatars.example.com/john-doe.png
memberOf:
- commerce-teamGitHub 自动发现
GitHub discovery plugin 会在你的 GitHub organization 中自动查找 catalog-info.yaml 文件:
# app-config.yaml
catalog:
providers:
github:
myOrgProvider:
organization: my-org
catalogPath: /catalog-info.yaml
filters:
branch: main
repository: '.*' # All repositories
schedule:
frequency:
minutes: 30
timeout:
minutes: 3
rules:
- allow:
- Component
- System
- Domain
- API
- Resource
- Group
- User
- Template
- Location安装所需的 backend plugin:
# From the Backstage app root
yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-github在 backend 中注册 module:
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
// Core plugins
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-github'));
// ... other plugins
backend.start();Kubernetes Cluster 集成
Kubernetes plugin 会在每个 component 的 Backstage catalog 中直接显示实时 Pod、Deployment 和 Service 状态。
安装 Kubernetes Plugin
# Frontend plugin
yarn --cwd packages/app add @backstage/plugin-kubernetes
# Backend plugin
yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend配置 Backend
注册 Kubernetes backend plugin:
// packages/backend/src/index.ts
backend.add(import('@backstage/plugin-kubernetes-backend'));配置 EKS Cluster 访问
# app-config.yaml
kubernetes:
serviceLocatorMethod:
type: multiTenant
clusterLocatorMethods:
- type: config
clusters:
- name: production-eks
url: https://ABCDEF1234567890.gr7.ap-northeast-2.eks.amazonaws.com
authProvider: serviceAccount
serviceAccountToken: ${K8S_PROD_SA_TOKEN}
caData: ${K8S_PROD_CA_DATA}
skipTLSVerify: false
skipMetricsLookup: false
dashboardUrl: https://console.aws.amazon.com/eks/home?region=ap-northeast-2#/clusters/production-eks
dashboardApp: aws
- name: staging-eks
url: https://GHIJKL5678901234.gr7.ap-northeast-2.eks.amazonaws.com
authProvider: serviceAccount
serviceAccountToken: ${K8S_STAGING_SA_TOKEN}
caData: ${K8S_STAGING_CA_DATA}
skipTLSVerify: false
skipMetricsLookup: false面向 Backstage 的 ServiceAccount 和 RBAC
在每个 EKS cluster 中创建只读 ServiceAccount,使 Backstage 能够查询 workload 状态:
# backstage-k8s-rbac.yaml
---
apiVersion: v1
kind: Namespace
metadata:
name: backstage-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: backstage-reader
namespace: backstage-system
---
apiVersion: v1
kind: Secret
metadata:
name: backstage-reader-token
namespace: backstage-system
annotations:
kubernetes.io/service-account.name: backstage-reader
type: kubernetes.io/service-account-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: backstage-reader
rules:
- apiGroups: [""]
resources:
- pods
- services
- configmaps
- namespaces
- endpoints
- serviceaccounts
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources:
- deployments
- replicasets
- statefulsets
- daemonsets
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources:
- jobs
- cronjobs
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources:
- ingresses
verbs: ["get", "list", "watch"]
- apiGroups: ["autoscaling"]
resources:
- horizontalpodautoscalers
verbs: ["get", "list", "watch"]
- apiGroups: ["metrics.k8s.io"]
resources:
- pods
- nodes
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: backstage-reader-binding
subjects:
- kind: ServiceAccount
name: backstage-reader
namespace: backstage-system
roleRef:
kind: ClusterRole
name: backstage-reader
apiGroup: rbac.authorization.k8s.io# Apply to each target EKS cluster
kubectl apply -f backstage-k8s-rbac.yaml
# Retrieve the token for Backstage configuration
kubectl get secret backstage-reader-token \
-n backstage-system \
-o jsonpath='{.data.token}' | base64 -d为 Kubernetes Discovery 标注 Components
为了让 Kubernetes plugin 找到正确的 workloads,请为 catalog entities 添加 annotations:
# In catalog-info.yaml
metadata:
annotations:
backstage.io/kubernetes-id: order-service
backstage.io/kubernetes-namespace: commerce
backstage.io/kubernetes-label-selector: app=order-service该 plugin 会通过这些 annotations 匹配 workloads,并直接在 component 页面显示 Pods、Deployments、ReplicaSets 和 HPA 状态。
Software Templates (Golden Paths)
Software Templates 使平台团队能够定义创建新项目的标准化路径。这些 Golden Paths 确保每个新服务从一开始就具备正确的结构、CI/CD pipeline、监控和安全配置。有关 Golden Path 概念的背景信息,请参阅 Platform Engineering Overview。
Template 结构
Backstage template 包含:
- Parameters:呈现给开发者的输入字段(forms)
- Steps:按顺序执行以生成项目脚手架的 actions
- Output:完成后显示的链接和信息
template.yaml
├── parameters: # What the developer fills in
│ ├── service name
│ ├── team/owner
│ ├── language
│ └── features (database, queue, etc.)
├── steps: # What happens automatically
│ ├── fetch:template (scaffold files)
│ ├── publish:github (create repository)
│ ├── catalog:register (add to Backstage)
│ └── argocd:create-resources (set up CD)
└── output: # What the developer sees
├── repository URL
├── catalog entity link
└── ArgoCD app linkMicroservice Golden Path Template
该 template 会生成一个完整 microservice 的脚手架,其中包含 Dockerfile、Helm chart、ArgoCD Application 和 GitHub Actions CI pipeline:
# templates/microservice/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: microservice-golden-path
title: Microservice Golden Path
description: |
Create a production-ready microservice with CI/CD pipeline,
Kubernetes deployment, monitoring, and documentation scaffolding.
tags:
- recommended
- microservice
- kubernetes
spec:
owner: group:platform-team
type: service
parameters:
- title: Service Information
required:
- serviceName
- owner
- description
properties:
serviceName:
title: Service Name
type: string
description: Unique name for the microservice (lowercase, hyphens only)
pattern: "^[a-z][a-z0-9-]*$"
maxLength: 40
ui:autofocus: true
description:
title: Description
type: string
description: Brief description of what this service does
maxLength: 200
owner:
title: Owner Team
type: string
description: The team that will own this service
ui:field: OwnerPicker
ui:options:
catalogFilter:
kind: Group
system:
title: System
type: string
description: The system this service belongs to
ui:field: EntityPicker
ui:options:
catalogFilter:
kind: System
- title: Technical Configuration
required:
- language
- port
properties:
language:
title: Programming Language
type: string
enum:
- java-spring
- go
- node-express
- python-fastapi
enumNames:
- Java (Spring Boot 3)
- Go (Gin)
- Node.js (Express)
- Python (FastAPI)
port:
title: Service Port
type: integer
default: 8080
description: Port the service listens on
enableDatabase:
title: Enable Database
type: boolean
default: false
description: Provision an Aurora PostgreSQL database via ACK
enableQueue:
title: Enable Message Queue
type: boolean
default: false
description: Provision an SQS queue via ACK
- title: Deployment Configuration
required:
- namespace
- environment
properties:
namespace:
title: Kubernetes Namespace
type: string
default: default
description: Target namespace for deployment
environment:
title: Environment
type: string
enum:
- dev
- staging
- production
default: dev
- title: Repository Configuration
required:
- repoUrl
properties:
repoUrl:
title: Repository Location
type: string
ui:field: RepoUrlPicker
ui:options:
allowedHosts:
- github.com
allowedOwners:
- my-org
steps:
# Step 1: Scaffold the project from a skeleton template
- id: fetch-skeleton
name: Fetch Project Skeleton
action: fetch:template
input:
url: ./skeleton/${{ parameters.language }}
targetPath: .
values:
serviceName: ${{ parameters.serviceName }}
description: ${{ parameters.description }}
owner: ${{ parameters.owner }}
system: ${{ parameters.system }}
port: ${{ parameters.port }}
namespace: ${{ parameters.namespace }}
environment: ${{ parameters.environment }}
enableDatabase: ${{ parameters.enableDatabase }}
enableQueue: ${{ parameters.enableQueue }}
# Step 2: Generate the Helm chart
- id: fetch-helm
name: Generate Helm Chart
action: fetch:template
input:
url: ./skeleton/helm-chart
targetPath: ./deploy/helm
values:
serviceName: ${{ parameters.serviceName }}
port: ${{ parameters.port }}
namespace: ${{ parameters.namespace }}
enableDatabase: ${{ parameters.enableDatabase }}
enableQueue: ${{ parameters.enableQueue }}
# Step 3: Generate GitHub Actions CI pipeline
- id: fetch-ci
name: Generate CI Pipeline
action: fetch:template
input:
url: ./skeleton/github-actions/${{ parameters.language }}
targetPath: ./.github/workflows
values:
serviceName: ${{ parameters.serviceName }}
language: ${{ parameters.language }}
# Step 4: Generate ArgoCD Application manifest
- id: fetch-argocd
name: Generate ArgoCD Application
action: fetch:template
input:
url: ./skeleton/argocd-app
targetPath: ./deploy/argocd
values:
serviceName: ${{ parameters.serviceName }}
namespace: ${{ parameters.namespace }}
environment: ${{ parameters.environment }}
repoUrl: ${{ (parameters.repoUrl | parseRepoUrl).host }}/${{ (parameters.repoUrl | parseRepoUrl).owner }}/${{ (parameters.repoUrl | parseRepoUrl).repo }}
# Step 5: Publish to GitHub
- id: publish
name: Create GitHub Repository
action: publish:github
input:
repoUrl: ${{ parameters.repoUrl }}
description: ${{ parameters.description }}
defaultBranch: main
protectDefaultBranch: true
repoVisibility: internal
collaborators:
- team: ${{ parameters.owner | replace("group:", "") }}
access: maintain
- team: platform-team
access: admin
# Step 6: Register in Backstage catalog
- id: register
name: Register in Backstage Catalog
action: catalog:register
input:
repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
catalogInfoPath: /catalog-info.yaml
output:
links:
- title: Source Code Repository
url: ${{ steps['publish'].output.remoteUrl }}
- title: Backstage Catalog Entity
icon: catalog
entityRef: ${{ steps['register'].output.entityRef }}
- title: GitHub Actions CI
url: ${{ steps['publish'].output.remoteUrl }}/actionsInfrastructure Provisioning Template
该 template 通过 ACK 和 KRO Claims 创建 AWS 基础设施资源,为开发者提供数据库、缓存和队列的自助服务访问:
# templates/infrastructure/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: aws-infrastructure-provisioning
title: AWS Infrastructure Provisioning
description: |
Self-service provisioning of AWS infrastructure resources (RDS, ElastiCache, SQS)
using ACK controllers and KRO resource graphs on EKS.
tags:
- infrastructure
- aws
- self-service
spec:
owner: group:platform-team
type: resource
parameters:
- title: Resource Information
required:
- resourceName
- owner
- resourceType
properties:
resourceName:
title: Resource Name
type: string
description: Name for the infrastructure resource
pattern: "^[a-z][a-z0-9-]*$"
maxLength: 40
owner:
title: Owner Team
type: string
ui:field: OwnerPicker
ui:options:
catalogFilter:
kind: Group
resourceType:
title: Resource Type
type: string
enum:
- aurora-postgresql
- aurora-mysql
- elasticache-redis
- sqs-queue
- s3-bucket
enumNames:
- Aurora PostgreSQL
- Aurora MySQL
- ElastiCache Redis
- SQS Queue
- S3 Bucket
- title: Resource Configuration
required:
- environment
- size
properties:
environment:
title: Environment
type: string
enum:
- dev
- staging
- production
default: dev
size:
title: Instance Size
type: string
enum:
- small
- medium
- large
enumNames:
- "Small (dev/test: db.t4g.medium, cache.t4g.small)"
- "Medium (staging: db.r6g.large, cache.r6g.large)"
- "Large (production: db.r6g.xlarge, cache.r6g.xlarge)"
default: small
namespace:
title: Target Namespace
type: string
default: default
description: Kubernetes namespace where the resource claim will be created
- title: Database-Specific Options
description: Only applicable for database resource types
properties:
dbName:
title: Database Name
type: string
default: appdb
description: Name of the initial database to create
storageSize:
title: Storage Size (GiB)
type: integer
default: 20
minimum: 20
maximum: 1000
enableMultiAZ:
title: Enable Multi-AZ
type: boolean
default: false
description: Enable Multi-AZ deployment for high availability
steps:
# Step 1: Generate the KRO resource claim
- id: generate-claim
name: Generate Resource Claim
action: fetch:template
input:
url: ./skeleton/infrastructure/${{ parameters.resourceType }}
targetPath: ./infrastructure
values:
resourceName: ${{ parameters.resourceName }}
environment: ${{ parameters.environment }}
size: ${{ parameters.size }}
namespace: ${{ parameters.namespace }}
dbName: ${{ parameters.dbName }}
storageSize: ${{ parameters.storageSize }}
enableMultiAZ: ${{ parameters.enableMultiAZ }}
owner: ${{ parameters.owner }}
# Step 2: Publish to the infrastructure GitOps repository
- id: publish
name: Create Pull Request
action: publish:github:pull-request
input:
repoUrl: github.com?owner=my-org&repo=infrastructure-gitops
branchName: provision/${{ parameters.resourceName }}
title: "Provision ${{ parameters.resourceType }}: ${{ parameters.resourceName }}"
description: |
## Infrastructure Provisioning Request
| Field | Value |
|-------|-------|
| Resource | ${{ parameters.resourceName }} |
| Type | ${{ parameters.resourceType }} |
| Environment | ${{ parameters.environment }} |
| Size | ${{ parameters.size }} |
| Owner | ${{ parameters.owner }} |
Provisioned via Backstage Software Template.
targetPath: claims/${{ parameters.namespace }}
# Step 3: Register as a Resource entity in the catalog
- id: register
name: Register Resource in Catalog
action: catalog:register
input:
repoContentsUrl: https://github.com/my-org/infrastructure-gitops/tree/main
catalogInfoPath: /claims/${{ parameters.namespace }}/${{ parameters.resourceName }}/catalog-info.yaml
output:
links:
- title: Pull Request
url: ${{ steps['publish'].output.remoteUrl }}
- title: Catalog Entity
icon: catalog
entityRef: ${{ steps['register'].output.entityRef }}生成的 Aurora PostgreSQL database KRO claim(由 template skeleton 创建):
# claims/<namespace>/<resource-name>/kro-claim.yaml
apiVersion: kro.run/v1alpha1
kind: DatabaseClaim
metadata:
name: order-db
namespace: commerce
labels:
app.kubernetes.io/managed-by: backstage
backstage.io/owner: group:commerce-team
environment: production
spec:
engine: aurora-postgresql
engineVersion: "15.4"
instanceClass: db.r6g.xlarge
storageSize: 100
multiAZ: true
databaseName: appdb
backupRetentionDays: 30
deletionProtection: trueArgoCD Integration Plugin
要将 Backstage templates 与 ArgoCD 链接起来以实现自动部署,请安装 ArgoCD scaffolder action:
yarn --cwd packages/backend add @roadiehq/scaffolder-backend-argocd// packages/backend/src/index.ts
backend.add(import('@roadiehq/scaffolder-backend-argocd'));添加 ArgoCD 配置:
# app-config.yaml
argocd:
appLocatorMethods:
- type: config
instances:
- name: main
url: https://argocd.example.com
token: ${ARGOCD_AUTH_TOKEN}这使 templates 能够包含 ArgoCD steps:
# In a template step
- id: create-argocd-app
name: Create ArgoCD Application
action: argocd:create-resources
input:
appName: ${{ parameters.serviceName }}-${{ parameters.environment }}
argoInstance: main
namespace: argocd
repoUrl: ${{ steps['publish'].output.remoteUrl }}
path: deploy/helm
projectName: ${{ parameters.namespace }}TechDocs
TechDocs 为 Backstage 带来 “docs-like-code” 体验,可在 developer portal 中直接渲染基于 MkDocs 的 Markdown 文档。
TechDocs 如何工作
- 文档以 Markdown 文件形式与源代码一起编写
mkdocs.yml配置文件定义文档结构- TechDocs 将文档构建为静态 HTML
- 构建后的 docs 存储在 S3(或 GCS/Azure Blob)中以供提供服务
- 开发者直接在 Backstage UI 中阅读与所属 component 关联的 docs
S3 Storage 配置
# app-config.yaml
techdocs:
builder: external
generator:
runIn: local
publisher:
type: awsS3
awsS3:
bucketName: backstage-techdocs
region: ap-northeast-2
bucketRootPath: /
accountId: "111122223333"
credentials:
roleArn: arn:aws:iam::111122223333:role/backstage-techdocs-roleS3 Bucket Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "BackstageTechDocsReadWrite",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/backstage-techdocs-role"
},
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::backstage-techdocs",
"arn:aws:s3:::backstage-techdocs/*"
]
}
]
}Service Repositories 中的 MkDocs 配置
每个发布 TechDocs 的 service 都需要在其根目录包含一个 mkdocs.yml:
# mkdocs.yml (in the service repository)
site_name: Order Service Documentation
site_description: Technical documentation for the Order Service
plugins:
- techdocs-core
nav:
- Home: index.md
- Architecture: architecture.md
- API Reference: api-reference.md
- Runbook: runbook.md
- ADRs:
- ADR-001 Database Choice: adrs/001-database-choice.md
- ADR-002 Event Schema: adrs/002-event-schema.md对应的 catalog-info.yaml annotation:
metadata:
annotations:
backstage.io/techdocs-ref: dir:.External TechDocs Build Pipeline
对于 external builder 模式,设置一个 CI job,在每次 merge 时构建并发布文档:
# .github/workflows/techdocs.yaml
name: Build and Publish TechDocs
on:
push:
branches: [main]
paths:
- 'docs/**'
- 'mkdocs.yml'
jobs:
publish-techdocs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: pip install mkdocs-techdocs-core==1.4.*
- name: Build TechDocs
run: npx @techdocs/cli generate --no-docker
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::111122223333:role/techdocs-publisher
aws-region: ap-northeast-2
- name: Publish TechDocs
run: |
npx @techdocs/cli publish \
--publisher-type awsS3 \
--storage-name backstage-techdocs \
--entity default/Component/order-service \
--awsRoleArn arn:aws:iam::111122223333:role/techdocs-publisher \
--awsRegion ap-northeast-2面向 EKS 的 Plugin 生态系统
Backstage 的 plugin system 是它最大的优势。以下 plugins 对在 Amazon EKS 上运行 workloads 的团队特别有价值。
Kubernetes Plugin
Kubernetes plugin 可直接从 catalog entity 页面提供 Pod 和 Deployment 状态的实时可见性。
它显示的内容:
- Pod 数量和状态(Running、Pending、CrashLoopBackOff)
- Deployment replica 状态(desired vs. available)
- 最近的 Pod events 和错误消息
- HPA 当前指标和目标指标
- Container resource utilization(CPU、memory)
Frontend integration:
// packages/app/src/components/catalog/EntityPage.tsx
import { EntityKubernetesContent } from '@backstage/plugin-kubernetes';
// Add to the service entity page
const serviceEntityPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
{/* overview content */}
</EntityLayout.Route>
<EntityLayout.Route path="/kubernetes" title="Kubernetes">
<EntityKubernetesContent refreshIntervalMs={10000} />
</EntityLayout.Route>
</EntityLayout>
);ArgoCD Plugin
ArgoCD plugin 会显示每个 component 的 GitOps deployment sync 状态、健康状况和历史记录。
安装:
# Frontend
yarn --cwd packages/app add @roadiehq/backstage-plugin-argo-cd
# Backend
yarn --cwd packages/backend add @roadiehq/backstage-plugin-argo-cd-backend配置:
# app-config.yaml
argocd:
appLocatorMethods:
- type: config
instances:
- name: main
url: https://argocd.example.com
token: ${ARGOCD_AUTH_TOKEN}Catalog annotation:
metadata:
annotations:
argocd/app-name: order-serviceFrontend integration:
// packages/app/src/components/catalog/EntityPage.tsx
import {
EntityArgoCDOverviewCard,
EntityArgoCDHistoryCard,
} from '@roadiehq/backstage-plugin-argo-cd';
// Add to the overview page
const overviewContent = (
<Grid container spacing={3}>
<Grid item md={6}>
<EntityArgoCDOverviewCard />
</Grid>
<Grid item md={6}>
<EntityArgoCDHistoryCard />
</Grid>
</Grid>
);Kubecost Plugin
Kubecost plugin 提供按 service 维度的成本可见性,显示每个 component 在 compute、memory 和 storage 方面的成本。
安装:
yarn --cwd packages/app add @kubecost/backstage-plugin
yarn --cwd packages/backend add @kubecost/backstage-plugin-backend配置:
# app-config.yaml
kubecost:
baseUrl: https://kubecost.example.com
# Optional: filter by cluster
clusterFilter: production-eksCatalog annotation:
metadata:
annotations:
kubecost.com/deployment-name: order-service
kubecost.com/namespace: commerceFrontend integration:
// packages/app/src/components/catalog/EntityPage.tsx
import { EntityKubecostCard } from '@kubecost/backstage-plugin';
const overviewContent = (
<Grid container spacing={3}>
<Grid item md={6}>
<EntityKubecostCard />
</Grid>
</Grid>
);KEDA 和 Karpenter 可见性(Custom Plugin 概念)
虽然没有面向 KEDA 或 Karpenter 的官方 Backstage plugin,但你可以构建一个 custom plugin,查询 Kubernetes API 中的 ScaledObject 和 NodePool resources,然后在 component 旁边显示 scaling metrics。
Custom plugin backend 概念:
// plugins/keda-backend/src/router.ts
import { Router } from 'express';
import { Logger } from 'winston';
import { Config } from '@backstage/config';
export async function createRouter(options: {
logger: Logger;
config: Config;
}): Promise<Router> {
const router = Router();
router.get('/scaled-objects/:namespace/:name', async (req, res) => {
const { namespace, name } = req.params;
// Query the Kubernetes API for ScaledObject
const scaledObject = await k8sClient.getNamespacedCustomObject(
'keda.sh',
'v1alpha1',
namespace,
'scaledobjects',
name,
);
res.json({
name: scaledObject.body.metadata.name,
minReplicas: scaledObject.body.spec.minReplicaCount,
maxReplicas: scaledObject.body.spec.maxReplicaCount,
triggers: scaledObject.body.spec.triggers,
currentReplicas: scaledObject.body.status?.currentReplicas,
});
});
return router;
}Custom plugin frontend 概念:
// plugins/keda/src/components/KedaCard.tsx
import React from 'react';
import { InfoCard, Progress } from '@backstage/core-components';
import { useEntity } from '@backstage/plugin-catalog-react';
import useAsync from 'react-use/lib/useAsync';
export const KedaScalingCard = () => {
const { entity } = useEntity();
const namespace =
entity.metadata.annotations?.['backstage.io/kubernetes-namespace'];
const name = entity.metadata.name;
const { value, loading, error } = useAsync(async () => {
const response = await fetch(
`/api/keda/scaled-objects/${namespace}/${name}`,
);
return response.json();
});
if (loading) return <Progress />;
if (error) return <div>Error loading KEDA data: {error.message}</div>;
return (
<InfoCard title="KEDA Autoscaling">
<p>Min Replicas: {value?.minReplicas}</p>
<p>Max Replicas: {value?.maxReplicas}</p>
<p>Current Replicas: {value?.currentReplicas}</p>
<p>Triggers: {value?.triggers?.length}</p>
</InfoCard>
);
};Plugin 摘要
| Plugin | Purpose | Data Source | Installation |
|---|---|---|---|
| @backstage/plugin-kubernetes | Pod/Deployment 状态 | Kubernetes API | 官方 |
| @roadiehq/backstage-plugin-argo-cd | Sync 状态、deploy 历史 | ArgoCD API | 社区(Roadie) |
| @kubecost/backstage-plugin | 按 service 的成本明细 | Kubecost API | 社区(Kubecost) |
| Custom KEDA Plugin | Autoscaler 状态 | Kubernetes API (keda.sh CRDs) | 自定义构建 |
| Custom Karpenter Plugin | Node provisioning 状态 | Kubernetes API (karpenter.sh CRDs) | 自定义构建 |
| @backstage/plugin-techdocs | Documentation viewer | S3 / GCS | 官方 |
| @backstage/plugin-github-actions | CI pipeline 状态 | GitHub API | 官方 |
RBAC 与治理
Permission Framework 概述
Backstage 包含内置 permission framework,用于控制对 catalog entities、templates 和 plugin features 的访问。该 permission system 基于策略,并与 catalog 的所有权模型集成。
启用 Permission Framework
# app-config.yaml
permission:
enabled: true基于团队的访问控制
实现一个强制执行基于所有权访问的 custom permission policy:
// packages/backend/src/plugins/permission.ts
import {
PolicyDecision,
AuthorizeResult,
isPermission,
} from '@backstage/plugin-permission-common';
import {
PermissionPolicy,
PolicyQuery,
} from '@backstage/plugin-permission-node';
import {
catalogEntityDeletePermission,
catalogEntityCreatePermission,
} from '@backstage/plugin-catalog-common/alpha';
import {
createCatalogConditionalDecision,
catalogConditions,
} from '@backstage/plugin-catalog-backend/alpha';
import { BackstageIdentityResponse } from '@backstage/plugin-auth-node';
import { createBackendModule } from '@backstage/backend-plugin-api';
import { policyExtensionPoint } from '@backstage/plugin-permission-node/alpha';
class TeamBasedPermissionPolicy implements PermissionPolicy {
async handle(
request: PolicyQuery,
user?: BackstageIdentityResponse,
): Promise<PolicyDecision> {
// Platform team gets full access
if (
user?.identity.ownershipEntityRefs.includes(
'group:default/platform-team',
)
) {
return { result: AuthorizeResult.ALLOW };
}
// Only owners can delete catalog entities
if (isPermission(request.permission, catalogEntityDeletePermission)) {
if (!user) {
return { result: AuthorizeResult.DENY };
}
return createCatalogConditionalDecision(request.permission, {
anyOf: user.identity.ownershipEntityRefs.map((ref) =>
catalogConditions.isEntityOwner({ claims: [ref] }),
),
});
}
// Everyone can create entities and view the catalog
if (isPermission(request.permission, catalogEntityCreatePermission)) {
return { result: AuthorizeResult.ALLOW };
}
// Default: allow read operations
return { result: AuthorizeResult.ALLOW };
}
}
export const permissionModule = createBackendModule({
pluginId: 'permission',
moduleId: 'team-based-policy',
register(reg) {
reg.registerInit({
deps: { policy: policyExtensionPoint },
async init({ policy }) {
policy.setPolicy(new TeamBasedPermissionPolicy());
},
});
},
});治理规则示例
| Rule | Implementation | Scope |
|---|---|---|
| 只有 owners 可以删除 entities | 对 catalogEntityDeletePermission 使用 Conditional permission | Catalog |
| 只有 platform team 可以创建 templates | 除非在 platform-team group 中,否则 DENY templateCreatePermission | Scaffolder |
| 外部 contractors 只读 | 对 contractor group DENY 所有写权限 | Global |
| Template execution requires approval | Templates 中的 custom approval workflow step | Scaffolder |
| Production deployments restricted | 基于 entity lifecycle 的 Conditional permission | Catalog + ArgoCD |
Audit Logging
Backstage 支持 audit logging,用于跟踪谁做了什么。配置 audit log backend:
# app-config.yaml
backend:
audit:
enabled: true
logger:
type: winston
options:
transports:
- type: console
level: info
- type: file
level: info
filename: /var/log/backstage/audit.log
maxsize: 10485760
maxFiles: 10对于生产环境,将 audit logs 发送到 CloudWatch:
# app-config.yaml
backend:
audit:
enabled: true
logger:
type: winston
options:
transports:
- type: cloudwatch
level: info
logGroupName: /backstage/audit
logStreamName: backstage-production
region: ap-northeast-2Audit log entries 会捕获:
- Who:已认证的 user identity
- What:执行的 action(entity created、template executed、entity deleted)
- When:action 的 timestamp
- Where:目标 entity 或 resource
- Result:成功或失败,并附带错误详情
生产运维
高可用配置
对于生产部署,在 ALB 后运行多个 Backstage replicas,并使用共享的外部 PostgreSQL:
# backstage-values.yaml (HA configuration)
backstage:
replicas: 3
resources:
requests:
memory: 1Gi
cpu: 500m
limits:
memory: 2Gi
cpu: 2000m
podDisruptionBudget:
enabled: true
minAvailable: 2
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- backstage
topologyKey: topology.kubernetes.io/zone
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app.kubernetes.io/name: backstage
livenessProbe:
httpGet:
path: /healthcheck
port: 7007
initialDelaySeconds: 60
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /healthcheck
port: 7007
initialDelaySeconds: 30
periodSeconds: 5
failureThreshold: 3
postgresql:
enabled: false # External RDS with Multi-AZHA deployment 架构:
┌─────────────────────────────────────────────────────────────┐
│ ALB (internet-facing) │
│ backstage.example.com │
└──────────┬──────────────────┬──────────────────┬────────────┘
│ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ Backstage │ │ Backstage │ │ Backstage │
│ Pod (AZ-a) │ │ Pod (AZ-b) │ │ Pod (AZ-c) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└──────────────────┼──────────────────┘
│
┌─────────▼─────────┐
│ RDS PostgreSQL │
│ (Multi-AZ) │
└───────────────────┘备份与恢复策略
| Component | Backup Method | Frequency | Retention |
|---|---|---|---|
| PostgreSQL (RDS) | Automated RDS snapshots | 每日 | 30 天 |
| PostgreSQL (RDS) | Point-in-time recovery | 持续 | 35 天 |
| TechDocs (S3) | S3 versioning + cross-region replication | 持续 | 90 天 |
| Configuration | Git repository (app-config.yaml) | 每次 commit | 无限期 |
| Catalog entities | Git repositories (catalog-info.yaml) | 每次 commit | 无限期 |
| Secrets | AWS Secrets Manager with rotation | 变更时 | 已版本化 |
RDS 备份配置(Terraform 示例):
resource "aws_rds_cluster" "backstage" {
cluster_identifier = "backstage-db"
engine = "aurora-postgresql"
engine_version = "15.4"
master_username = "backstage"
database_name = "backstage"
backup_retention_period = 30
preferred_backup_window = "03:00-04:00"
deletion_protection = true
storage_encrypted = true
kms_key_id = aws_kms_key.backstage.arn
copy_tags_to_snapshot = true
tags = {
Environment = "production"
Service = "backstage"
ManagedBy = "terraform"
}
}灾难恢复流程:
- 数据库损坏或丢失:从最新的 RDS automated snapshot 恢复,或使用 point-in-time recovery 恢复到特定时间戳
- TechDocs 丢失:S3 versioning 允许恢复任何先前版本;cross-region replica 提供区域级故障转移
- 配置漂移:所有配置都在 Git 中;从已知良好的 commit 重新部署
- Catalog 数据丢失:Catalog entities 定义在 Git repositories 中;重新触发 GitHub discovery provider 以重新填充
升级策略
Backstage 会频繁发布新版本(大约每月一次)。遵循以下升级策略:
- 显式固定版本:始终在 Dockerfile 和 package.json 中使用精确 version tags,绝不要使用
latest - 阅读 changelog:升级前查看 Backstage changelog 中的 breaking changes
- 逐步升级:不要跳过 major versions;一次升级一个 minor version
- 在 staging 中测试:先部署到带有生产数据库 schema 副本的 staging environment
- 滚动更新:使用带 readiness probes 的 Kubernetes rolling update strategy,确保零停机
# Deployment strategy in values.yaml
backstage:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1升级工作流:
# 1. Update Backstage packages
yarn backstage-cli versions:bump --release 1.36.0
# 2. Run database migrations
yarn backstage-cli db:migrate
# 3. Build and test locally
yarn build:backend
yarn test
# 4. Build new container image
docker build -t backstage:v1.36.0 .
# 5. Push to ECR
docker tag backstage:v1.36.0 \
111122223333.dkr.ecr.ap-northeast-2.amazonaws.com/backstage:v1.36.0
docker push \
111122223333.dkr.ecr.ap-northeast-2.amazonaws.com/backstage:v1.36.0
# 6. Update Helm values and upgrade
helm upgrade backstage backstage/backstage \
--namespace backstage \
--set backstage.image.tag=v1.36.0 \
--values backstage-values.yaml \
--wait --timeout 10m最佳实践
推荐实践
从 Software Catalog 开始:在构建 templates 或安装 plugins 之前,先投入时间构建完整且准确的 catalog。填充良好的 catalog 是让其他所有功能有价值的基础。首先注册所有现有服务,并确保所有权正确。
将 Backstage 视为内部产品:指定 product owner,收集开发者反馈,迭代功能,并跟踪采用指标。平台团队是一个 product team,其客户是其他工程团队。
定义 Golden Paths,而不是 Golden Cages:Templates 应编码最佳实践和组织标准,但当开发者有合理理由时,始终允许他们偏离。目标是让正确的事情变得容易,而不是让它成为唯一选择。
自动化 Catalog 填充:使用 GitHub discovery plugin 自动查找并注册
catalog-info.yaml文件,而不是要求手动注册。这可以减少摩擦并保持 catalog 最新。将 app-config.yaml 保存在 Version Control 中:切勿在运行中的 cluster 上手动编辑配置。所有 Backstage 配置都应位于 Git repository 中,并通过与 application 本身相同的 CI/CD pipeline 部署。
投资文档(TechDocs):没有文档的 developer portal 只是一个 dashboard。让 TechDocs 成为每个 service 的要求 -- 将它包含在你的 Golden Path templates 中,使新服务从第一天起就随附文档脚手架。
监控 Backstage 本身:从 Backstage backend 暴露 Prometheus metrics,为 API latency、error rates 和 plugin health 设置 dashboards。使用你提供给开发者的同一套 observability stack。
规划 Plugin 维护:Community plugins 可能落后于 Backstage core releases。固定 plugin versions,在 staging 中测试升级,并维护一份对部署至关重要的 plugins 列表。
常见陷阱
构建过多、过快:尝试在第一天就部署包含每个 plugin 的 Backstage 会导致复杂性和延迟。先从 catalog 和一个 template 开始,然后根据开发者反馈逐步添加 plugins。
忽视所有权数据:缺失或不正确的所有权信息会削弱对 catalog 的信任。如果开发者无法找到某个 service 的 owner,他们就会停止使用 catalog。通过对
catalog-info.yaml的 CI checks 强制执行所有权。从一开始就忽略 Authentication:即使在 staging 中,在没有正确 OIDC authentication 的情况下部署 Backstage 也会造成安全缺口,并使之后实施 RBAC 更加困难。在将 Backstage 暴露给用户之前配置 authentication。
将 Backstage 视为只读:Backstage 的真正力量在于 Software Templates 和自助服务工作流,而不仅仅是查看 catalog 数据。如果开发者可以看到自己的 services,但不能通过 portal 创建新的 services,采用率就会停滞。
参考资料
官方文档
- Backstage Official Documentation
- Backstage GitHub Repository
- Backstage Plugin Marketplace
- Backstage Helm Chart
CNCF 和社区
AWS 和 EKS 集成
- AWS EKS Best Practices Guide
- AWS Load Balancer Controller Documentation
- Amazon Cognito Developer Guide
本 Repository 中的相关文档
- Platform Engineering Overview -- IDP 概念、成熟度模型和参考架构
- Helm Package Manager -- 用于 Backstage 部署的 Helm chart packaging
- AWS Controllers for Kubernetes (ACK) -- 通过 Kubernetes API 预置基础设施
- Kubernetes Resource Operator (KRO) -- 面向自助服务 claims 的 resource graph orchestration
- Kubernetes Extension Mechanisms -- 驱动 Backstage plugins 的 CRDs 和 operators
- ExampleCorp Integration Example -- 端到端 ACK + KRO 部署示例
- Crossplane -- 通过 Kubernetes API 实现 Infrastructure as Code;与 Backstage 集成以支持自助服务
- ArgoCD Applications -- 与 Backstage templates 集成的 GitOps 部署