Backstage
소프트웨어 카탈로그 및 개발자 경험을 관리하기 위한 Spotify의 오픈소스 내부 개발자 포털 플랫폼.
새 애플리케이션 생성
섹션 제목: “새 애플리케이션 생성”| 명령어 | 설명 |
|---|---|
npx @backstage/create-app@latest | 새 Backstage 애플리케이션 생성 |
npx @backstage/create-app@latest --skip-install | 종속성 설치 없이 앱 생성 |
cd my-backstage-app && yarn install | 종속성 설치 |
yarn dev | 개발 모드로 프론트엔드 및 백엔드 시작 |
yarn start | 프론트엔드만 시작 |
yarn start-backend | 백엔드만 시작 |
yarn build:backend | 프로덕션용 백엔드 빌드 |
yarn build | 모든 패키지 빌드 |
node_modules/.bin/backstage-cli --version | Backstage CLI 버전 확인 |
Docker 배포
섹션 제목: “Docker 배포”| 명령어 | 설명 |
|---|---|
yarn build:backend --config ../../app-config.yaml | 구성과 함께 백엔드 빌드 |
docker build -t backstage -f packages/backend/Dockerfile . | Docker 이미지 빌드 |
docker run -p 7007:7007 backstage | Backstage 컨테이너 실행 |
CLI 명령어
섹션 제목: “CLI 명령어”패키지 관리
섹션 제목: “패키지 관리”| 명령어 | 설명 |
|---|---|
yarn backstage-cli package start | 패키지를 개발 모드로 시작 |
yarn backstage-cli package build | 패키지 빌드 |
yarn backstage-cli package lint | 패키지 소스 코드 린트 |
yarn backstage-cli package test | 패키지 테스트 실행 |
yarn backstage-cli repo build --all | 모노레포의 모든 패키지 빌드 |
yarn backstage-cli repo lint --all | 모든 패키지 린트 |
yarn backstage-cli versions:bump | Backstage 종속성을 최신으로 업데이트 |
yarn backstage-cli versions:bump --release next | 다음 프리릴리스로 업데이트 |
yarn backstage-cli migrate package-roles | 패키지 역할 사용으로 마이그레이션 |
플러그인 생성
섹션 제목: “플러그인 생성”| 명령어 | 설명 |
|---|---|
yarn backstage-cli create-plugin | 새 프론트엔드 플러그인 생성 |
yarn backstage-cli create-plugin --backend | 새 백엔드 플러그인 생성 |
yarn backstage-cli create-plugin --id my-plugin | 특정 ID로 플러그인 생성 |
yarn new | 템플릿에서 대화형으로 컴포넌트 생성 |
소프트웨어 카탈로그
섹션 제목: “소프트웨어 카탈로그”엔티티 종류
섹션 제목: “엔티티 종류”| 명령어 | 설명 |
|---|---|
저장소 루트에 catalog-info.yaml 추가 | 카탈로그에 컴포넌트 등록 |
catalog-info.yaml에서 kind: Component | 소프트웨어 컴포넌트 정의 |
catalog-info.yaml에서 kind: API | API 엔티티 정의 |
catalog-info.yaml에서 kind: System | 시스템 그룹 정의 |
catalog-info.yaml에서 kind: Domain | 비즈니스 도메인 정의 |
catalog-info.yaml에서 kind: Resource | 인프라 리소스 정의 |
catalog-info.yaml에서 kind: Group | 팀/그룹 정의 |
catalog-info.yaml에서 kind: User | 사용자 정의 |
catalog-info.yaml에서 kind: Location | 다른 카탈로그 파일 참조 |
엔티티 속성
섹션 제목: “엔티티 속성”| 명령어 | 설명 |
|---|---|
spec.owner: team-name 설정 | 엔티티 소유권 설정 |
spec.lifecycle: production 설정 | 엔티티 라이프사이클 단계 설정 |
spec.type: service 설정 | 컴포넌트 유형 설정 (service, website, library) |
spec.dependsOn: ['component:other'] 설정 | 종속성 정의 |
spec.providesApis: ['api-name'] 설정 | 제공하는 API 선언 |
spec.consumesApis: ['api-name'] 설정 | 사용하는 API 선언 |
spec.system: system-name 설정 | 시스템에 할당 |
통합을 위해 metadata.annotations 설정 | CI/CD, 모니터링 등에 연결 |
catalog-info.yaml 예제
섹션 제목: “catalog-info.yaml 예제”apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payment-service
description: Handles payment processing and billing
annotations:
github.com/project-slug: org/payment-service
backstage.io/techdocs-ref: dir:.
jenkins.io/job-full-name: payment-service/main
pagerduty.com/service-id: PABC123
sonarqube.org/project-key: org_payment-service
tags:
- java
- payments
links:
- url: https://dashboard.example.com/payments
title: Monitoring Dashboard
icon: dashboard
- url: https://wiki.example.com/payment-service
title: Wiki
spec:
type: service
lifecycle: production
owner: team-payments
system: billing
providesApis:
- payment-api
consumesApis:
- user-api
- notification-api
dependsOn:
- resource:payments-db
- component:auth-service
API 엔티티 예제
섹션 제목: “API 엔티티 예제”apiVersion: backstage.io/v1alpha1
kind: API
metadata:
name: payment-api
description: Payment processing REST API
tags:
- rest
- payments
spec:
type: openapi
lifecycle: production
owner: team-payments
system: billing
definition: |
openapi: "3.0.0"
info:
title: Payment API
version: 1.0.0
paths:
/payments:
post:
summary: Create a payment
responses:
"201":
description: Payment created
시스템 및 도메인 예제
섹션 제목: “시스템 및 도메인 예제”apiVersion: backstage.io/v1alpha1
kind: System
metadata:
name: billing
description: Billing and payment processing system
spec:
owner: team-payments
domain: commerce
---
apiVersion: backstage.io/v1alpha1
kind: Domain
metadata:
name: commerce
description: E-commerce domain covering orders, payments, and fulfillment
spec:
owner: group:engineering-leadership
템플릿
섹션 제목: “템플릿”템플릿 속성
섹션 제목: “템플릿 속성”| 명령어 | 설명 |
|---|---|
template.yaml에서 kind: Template | 소프트웨어 템플릿 정의 |
spec.type: service 설정 | 템플릿이 서비스를 생성 |
템플릿 액션에 spec.steps[] 설정 | 스캐폴딩 단계 정의 |
action: fetch:template | 템플릿 파일 가져오기 및 렌더링 |
action: publish:github | 스캐폴딩된 저장소를 GitHub에 게시 |
action: publish:github:pull-request | 새 저장소 대신 PR 생성 |
action: catalog:register | 생성된 엔티티를 카탈로그에 등록 |
action: github:actions:dispatch | GitHub Actions 워크플로우 트리거 |
템플릿에서 ${{ parameters.name }} 사용 | 사용자 입력 매개변수 참조 |
폼 필드에 spec.parameters[] 설정 | 템플릿 입력 폼 정의 |
매개변수에서 ui:widget: textarea 사용 | 폼 필드 위젯 커스터마이즈 |
템플릿 정의 예제
섹션 제목: “템플릿 정의 예제”apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: microservice-template
title: Create a Microservice
description: Scaffold a new microservice with CI/CD, monitoring, and docs
tags:
- recommended
- microservice
spec:
owner: team-platform
type: service
parameters:
- title: Service Details
required:
- name
- description
- owner
properties:
name:
title: Service Name
type: string
description: Unique name of the service
ui:autofocus: true
ui:options:
rows: 5
description:
title: Description
type: string
owner:
title: Owner
type: string
description: Team that owns this service
ui:field: OwnerPicker
ui:options:
catalogFilter:
kind: Group
- title: Infrastructure
properties:
language:
title: Language
type: string
enum: ["go", "java", "python", "typescript"]
default: go
database:
title: Database
type: string
enum: ["postgres", "mysql", "none"]
default: postgres
enableMonitoring:
title: Enable Monitoring
type: boolean
default: true
steps:
- id: fetch-base
name: Fetch Base Template
action: fetch:template
input:
url: ./skeleton
values:
name: ${{ parameters.name }}
description: ${{ parameters.description }}
owner: ${{ parameters.owner }}
language: ${{ parameters.language }}
- id: publish
name: Publish to GitHub
action: publish:github
input:
allowedHosts: ["github.com"]
repoUrl: github.com?owner=my-org&repo=${{ parameters.name }}
description: ${{ parameters.description }}
defaultBranch: main
repoVisibility: internal
- id: register
name: Register in Catalog
action: catalog:register
input:
repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
catalogInfoPath: /catalog-info.yaml
output:
links:
- title: Repository
url: ${{ steps['publish'].output.remoteUrl }}
- title: Open in Catalog
icon: catalog
entityRef: ${{ steps['register'].output.entityRef }}
플러그인
섹션 제목: “플러그인”플러그인 설치
섹션 제목: “플러그인 설치”| 명령어 | 설명 |
|---|---|
yarn add @backstage/plugin-catalog | 카탈로그 플러그인 설치 |
yarn add @backstage/plugin-techdocs | TechDocs 플러그인 설치 |
yarn add @backstage/plugin-kubernetes | Kubernetes 플러그인 설치 |
yarn add @backstage/plugin-github-actions | GitHub Actions 플러그인 설치 |
yarn add @backstage/plugin-search | 검색 플러그인 설치 |
yarn add @backstage/plugin-scaffolder | 스캐폴더 플러그인 설치 |
yarn add @backstage/plugin-api-docs | API 문서 플러그인 설치 |
yarn add @backstage/plugin-cost-insights | 비용 인사이트 플러그인 설치 |
플러그인 등록
섹션 제목: “플러그인 등록”packages/app/src/App.tsx에서 프론트엔드 플러그인 등록:
import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';
import { TechDocsReaderPage } from '@backstage/plugin-techdocs';
import { SearchPage } from '@backstage/plugin-search';
const routes = (
<FlatRoutes>
<Route path="/catalog" element={<CatalogIndexPage />} />
<Route path="/catalog/:namespace/:kind/:name" element={<CatalogEntityPage />}>
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<EntityOverviewContent />
</EntityLayout.Route>
<EntityLayout.Route path="/api" title="API">
<EntityApiContent />
</EntityLayout.Route>
<EntityLayout.Route path="/docs" title="Docs">
<EntityTechDocsContent />
</EntityLayout.Route>
</EntityLayout>
</Route>
<Route path="/docs" element={<TechDocsReaderPage />} />
<Route path="/search" element={<SearchPage />} />
<Route path="/catalog-graph" element={<CatalogGraphPage />} />
</FlatRoutes>
);
packages/backend/src/index.ts에서 백엔드 플러그인 등록:
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
backend.add(import('@backstage/plugin-app-backend'));
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-github-org'));
backend.add(import('@backstage/plugin-scaffolder-backend'));
backend.add(import('@backstage/plugin-techdocs-backend'));
backend.add(import('@backstage/plugin-search-backend'));
backend.add(import('@backstage/plugin-search-backend-module-catalog'));
backend.add(import('@backstage/plugin-search-backend-module-techdocs'));
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-github-provider'));
backend.add(import('@backstage/plugin-kubernetes-backend'));
backend.start();
TechDocs
섹션 제목: “TechDocs”TechDocs 작업
섹션 제목: “TechDocs 작업”| 명령어 | 설명 |
|---|---|
npx @techdocs/cli serve | 로컬에서 TechDocs 미리보기 |
npx @techdocs/cli serve --docker-image techdocs-container | 사용자 정의 Docker 이미지로 서비스 |
npx @techdocs/cli generate | TechDocs 정적 사이트 생성 |
npx @techdocs/cli generate --source-dir . | 특정 디렉터리에서 생성 |
npx @techdocs/cli publish --publisher-type googleGcs | GCS에 TechDocs 게시 |
npx @techdocs/cli publish --publisher-type awsS3 | S3에 TechDocs 게시 |
backstage.io/techdocs-ref 어노테이션 추가 | 엔티티에 TechDocs 활성화 |
mkdocs.yml과 함께 docs/ 디렉터리 생성 | TechDocs 소스 설정 |
구성에서 techdocs.builder: 'local' 설정 | 로컬 TechDocs 빌더 사용 |
구성에서 techdocs.builder: 'external' 설정 | 외부 CI/CD 빌더 사용 |
TechDocs mkdocs.yml 예제
섹션 제목: “TechDocs mkdocs.yml 예제”site_name: Payment Service
nav:
- Home: index.md
- Architecture: architecture.md
- API Reference: api-reference.md
- Runbooks:
- Deployment: runbooks/deployment.md
- Incident Response: runbooks/incident-response.md
plugins:
- techdocs-core
markdown_extensions:
- admonition
- pymdownx.highlight
- pymdownx.superfences
검색 설정
섹션 제목: “검색 설정”| 명령어 | 설명 |
|---|---|
yarn add @backstage/plugin-search | 검색 프론트엔드 설치 |
yarn add @backstage/plugin-search-backend | 검색 백엔드 설치 |
yarn add @backstage/plugin-search-backend-module-catalog | 카탈로그 검색 콜레이터 추가 |
yarn add @backstage/plugin-search-backend-module-techdocs | TechDocs 검색 콜레이터 추가 |
app-config.yaml에서 검색 엔진 구성 | Lunr, Elasticsearch 또는 PgStore 설정 |
구성에서 search.pg 설정 | 검색에 PostgreSQL 사용 |
구성에서 search.elasticsearch 설정 | 검색에 Elasticsearch 사용 |
핵심 설정
섹션 제목: “핵심 설정”| 명령어 | 설명 |
|---|---|
app-config.yaml 편집 | 메인 구성 파일 |
app-config.local.yaml 편집 | 로컬 개발 오버라이드 |
app-config.production.yaml 편집 | 프로덕션 구성 |
구성에서 app.baseUrl 설정 | 프론트엔드 URL 구성 |
구성에서 backend.baseUrl 설정 | 백엔드 URL 구성 |
구성에서 backend.database 설정 | 데이터베이스 연결 구성 |
구성에서 backend.cors.origin 설정 | CORS 출처 구성 |
구성에서 auth.providers 설정 | 인증 프로바이더 구성 |
구성에서 catalog.locations[] 설정 | 카탈로그 엔티티 소스 추가 |
구성에서 catalog.rules[] 설정 | 엔티티 검증 규칙 정의 |
구성에서 integrations.github[] 설정 | GitHub 통합 토큰 구성 |
app-config.yaml 예제
섹션 제목: “app-config.yaml 예제”app:
title: My Company Developer Portal
baseUrl: http://localhost:3000
organization:
name: My Company
backend:
baseUrl: http://localhost:7007
listen:
port: 7007
cors:
origin: http://localhost:3000
methods: [GET, HEAD, PATCH, POST, PUT, DELETE]
database:
client: pg
connection:
host: ${POSTGRES_HOST}
port: ${POSTGRES_PORT}
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
integrations:
github:
- host: github.com
token: ${GITHUB_TOKEN}
auth:
environment: development
providers:
github:
development:
clientId: ${GITHUB_CLIENT_ID}
clientSecret: ${GITHUB_CLIENT_SECRET}
catalog:
import:
entityFilename: catalog-info.yaml
pullRequestBranchName: backstage-integration
rules:
- allow: [Component, System, API, Resource, Location, Domain, Group, User]
locations:
- type: url
target: https://github.com/my-org/backstage-catalog/blob/main/all-components.yaml
- type: github-discovery
target: https://github.com/my-org
- type: github-org
target: https://github.com/my-org
techdocs:
builder: local
generator:
runIn: local
publisher:
type: local
kubernetes:
serviceLocatorMethod:
type: multiTenant
clusterLocatorMethods:
- type: config
clusters:
- url: ${K8S_CLUSTER_URL}
name: production
authProvider: serviceAccount
serviceAccountToken: ${K8S_TOKEN}
엔티티 관계 다이어그램
섹션 제목: “엔티티 관계 다이어그램”| 관계 | 출발 | 도착 | 설명 |
|---|---|---|---|
ownerOf | Group/User | Component/API | 그룹이 엔티티를 소유 |
ownedBy | Component/API | Group/User | 엔티티가 그룹에 의해 소유됨 |
consumesApi | Component | API | 컴포넌트가 API를 사용 |
providesApi | Component | API | 컴포넌트가 API를 노출 |
dependsOn | Component | Component/Resource | 컴포넌트가 다른 것에 의존 |
dependencyOf | Component/Resource | Component | 다른 것의 종속성임 |
partOf | Component | System | 컴포넌트가 시스템에 속함 |
hasPart | System | Component | 시스템이 컴포넌트를 포함 |
parentOf | Group | Group | 상위 팀 관계 |
childOf | Group | Group | 하위 팀 관계 |
memberOf | User | Group | 사용자가 그룹에 속함 |
hasMember | Group | User | 그룹이 사용자를 포함 |
모범 사례
섹션 제목: “모범 사례”-
모든 저장소에 catalog-info.yaml을 사용하세요 — 모든 서비스, 라이브러리, API를 카탈로그에 등록하여 완전한 가시성을 확보하세요.
-
의미 있는 소유권을 설정하세요 — 모든 엔티티에 유효한 Group을 가리키는
owner필드를 설정하여 책임과 라우팅을 가능하게 하세요. -
엔티티에 풍부한 어노테이션을 추가하세요 — CI/CD, 모니터링, PagerDuty, 문서에 대한 어노테이션을 추가하여 카탈로그를 진정한 허브로 만드세요.
-
골든 패스를 위한 템플릿을 만드세요 — 스캐폴더를 사용하여 새 서비스를 생성하는 표준화된 방법을 정의하여 일관성을 보장하세요.
-
TechDocs를 구현하세요 — TechDocs 플러그인과
mkdocs.yml을 사용하여 코드와 함께 문서를 유지하여 살아있는 문서를 만드세요. -
GitHub 디스커버리를 사용하세요 — 카탈로그 위치에
github-discovery를 구성하여 저장소를 자동으로 발견하고 등록하세요. -
조직을 모델링하세요 — 팀 구조를 반영하는 Group과 User를 정의하여 소유권 추적과 팀 페이지를 가능하게 하세요.
-
app-config.yaml을 환경 인식으로 유지하세요 — 개발에는
app-config.local.yaml을, 프로덕션 시크릿에는 환경 변수를 사용하세요. -
플러그인 생태계를 구축하세요 — 조직의 특정 도구로 Backstage를 확장하기 위해 사용자 정의 프론트엔드 및 백엔드 플러그인을 만드세요.
-
시스템과 도메인을 정의하세요 — 시스템과 도메인을 사용하여 상위 수준 아키텍처를 모델링하여 개발자에게 플랫폼의 지도를 제공하세요.