Module 31 Platform 5 labs

Backstage Developer Portal

Backstage (CNCF Graduated) là open-source developer portal do Spotify tạo ra, giúp tổ chức xây dựng Internal Developer Platform với service catalog, software templates, TechDocs và plugin ecosystem — nền tảng cho Platform Engineering hiện đại.

Công cụ thực hành Node.js, npx, Backstage CLI, Git, VS Code, curl
Nền tảng Linux (WSL2) / macOS / Windows, GitHub, Node.js 18+
Thời điểm phát hành 23/05/2026
Ngày biên soạn 23/05/2026
Người biên soạn Trần Văn Hòa — Microsoft Certified Trainer (MCT)

Mục tiêu học tập

1. Lý thuyết cốt lõi

1.1. Backstage là gì và tại sao cần?

Backstage là open-source developer portal do Spotify phát hành năm 2020, nay là CNCF Graduated project. Vấn đề nó giải quyết: khi tổ chức lớn lên, developer mất thời gian tìm kiếm service nằm ở đâu, ai owns, doc ở chỗ nào, cách tạo service mới như thế nào. Backstage tập trung tất cả vào một nơi: single pane of glass cho developer experience.

Ba trụ cột của Backstage

  • Software Catalog — inventory tập trung của tất cả services, APIs, libraries, websites, resources. Mỗi thực thể được mô tả bằng catalog-info.yaml lưu trong repo.
  • Software Templates (Scaffolder) — wizard self-service để developer tạo repo mới, pipeline CI/CD, Helm chart theo chuẩn nội bộ; không cần mở ticket cho Platform team.
  • TechDocs — docs-as-code: Markdown trong repo → rendered HTML trong Backstage portal, luôn cập nhật, version-controlled.

1.2. Kiến trúc kỹ thuật

Backstage là một monorepo TypeScript (Yarn workspaces) gồm: backend (Node.js/Express), frontend (React), và plugin packages. Mỗi tính năng là một plugin độc lập — kể cả catalog, scaffolder, TechDocs đều là plugin. Điều này cho phép tổ chức thêm/bớt tính năng mà không ảnh hưởng core.

Thành phầnVai tròPackage
CorePlugin API, routing, auth, theming@backstage/core-*
CatalogĐọc catalog-info.yaml từ SCM, lưu DB@backstage/plugin-catalog
ScaffolderChạy software templates, gọi actions@backstage/plugin-scaffolder
TechDocsBuild & serve MkDocs từ repo@backstage/plugin-techdocs
KubernetesHiển thị workload K8s theo service owner@backstage/plugin-kubernetes

1.3. Catalog Entities và Ownership Model

Mỗi entity trong catalog có kind (Component, API, Resource, System, Domain, Group, User, Location, Template) và các annotation quan trọng:

1.4. Software Templates và Scaffolder Actions

Template là YAML với kind: Template, chứa: parameters (JSON Schema form cho developer điền), steps (dãy actions: fetch:template, publish:github, catalog:register...). Khi developer submit form, Scaffolder backend thực thi steps tuần tự — tạo repo, push code, đăng ký catalog — tất cả trong vài giây.

1.5. Backstage trong Platform Engineering

Backstage là UI layer của Internal Developer Platform (IDP). Phía sau portal là các hệ thống thật: Kubernetes clusters, CI/CD pipelines, secret managers, cloud accounts. Backstage không thay thế chúng mà tích hợp qua plugin, cung cấp developer experience nhất quán. Mô hình triển khai phổ biến: Backstage chạy trên Kubernetes, dùng PostgreSQL làm catalog database, kết nối GitHub/GitLab qua OAuth.

2. Thực hành (Labs)

LAB-151

Cài Backstage local và khởi động dev server

Node.js · npx · Backstage CLI

🎯 Mục tiêu: Tạo ứng dụng Backstage mới từ đầu, chạy trên localhost:3000 với catalog, scaffolder, TechDocs mặc định.

🧰 Công cụ / nền tảng: Node.js 18+ (LTS), npm 9+, Git, curl, terminal Linux/WSL2 (khuyến nghị).

📦 Chuẩn bị:

# Kiểm tra phiên bản Node.js (cần >= 18)
node --version    # phải trả về v18.x.x hoặc cao hơn
npm --version     # phải >= 9.x

# Nếu chưa có Node 18, cài qua nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install 18
nvm use 18

▶️ Các bước:

# 1. Tạo ứng dụng Backstage mới (sẽ hỏi tên app)
npx @backstage/create-app@latest
# Nhập tên app khi được hỏi, ví dụ: my-portal
# Quá trình sẽ mất 3-5 phút (yarn install các dependencies)

# 2. Vào thư mục vừa tạo
cd my-portal

# 3. Khởi động dev server (chạy cả frontend + backend cùng lúc)
yarn dev
# Frontend: http://localhost:3000
# Backend:  http://localhost:7007

🖥️ Đối chiếu GUI: Mở VS Code trong thư mục my-portal, dùng Split Terminal để quan sát log frontend (3000) và backend (7007) riêng biệt.

✅ Kết quả mong đợi:

# Terminal sẽ hiển thị:
# webpack compiled successfully
# Backend listening on :7007

# Mở trình duyệt tại http://localhost:3000
# Thấy Backstage UI với sidebar: Catalog, Create, TechDocs, Settings
# Catalog đã có ~10 example components được seed sẵn

🧹 Cleanup: Ctrl+C dừng dev server. Giữ thư mục my-portal cho các lab tiếp theo.

LAB-152

Viết catalog-info.yaml và đăng ký Service Catalog

Backstage · Git · GitHub

🎯 Mục tiêu: Viết entity descriptor cho một microservice thực tế, đăng ký vào Catalog, xem dependency graph.

🧰 Công cụ / nền tảng: VS Code, Git, GitHub public repo, Backstage local từ LAB-151.

📦 Chuẩn bị: Tạo một repo GitHub public (ví dụ demo-payment-service).

▶️ Các bước:

# 1. Tạo file catalog-info.yaml trong root của repo GitHub
# (Tạo trực tiếp trên GitHub UI hoặc clone rồi tạo)

cat > catalog-info.yaml << 'EOF'
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payment-service
  description: Xử lý giao dịch thanh toán và refund
  annotations:
    github.com/project-slug: YOUR_GITHUB_USERNAME/demo-payment-service
    backstage.io/techdocs-ref: dir:.
  tags:
    - java
    - payments
    - critical
spec:
  type: service
  lifecycle: production
  owner: group:team-payments
  system: banking-platform
  dependsOn:
    - component:user-service
    - resource:postgres-payments
EOF

git add catalog-info.yaml
git commit -m "chore: add backstage catalog descriptor"
git push origin main
# 2. Đăng ký vào Backstage local
# Mở http://localhost:3000/catalog-import
# Nhập URL raw GitHub:
# https://github.com/YOUR_USERNAME/demo-payment-service/blob/main/catalog-info.yaml
# Click "Analyze" rồi "Import"

# Hoặc dùng Backstage CLI (nếu đã cài):
backstage-cli catalog-import \
  https://github.com/YOUR_USERNAME/demo-payment-service/blob/main/catalog-info.yaml
# 3. Kiểm tra qua API backend
curl http://localhost:7007/api/catalog/entities?filter=kind=Component | \
  python3 -m json.tool | grep -E '"name"|"kind"'

✅ Kết quả mong đợi: Component payment-service xuất hiện trong Catalog UI. Tab "Relations" hiển thị dependsOn links. API trả về entity JSON với spec.ownerspec.system.

🧹 Cleanup: Giữ catalog entry cho LAB-153/155. Nếu muốn xóa: vào Catalog UI → entity → "Unregister entity".

LAB-153

Tích hợp TechDocs — docs-as-code

TechDocs · MkDocs · Backstage

🎯 Mục tiêu: Thêm documentation vào service đã catalog ở LAB-152, render trong Backstage TechDocs.

🧰 Công cụ / nền tảng: Python 3.8+ (cho mkdocs), Git, Backstage local.

📦 Chuẩn bị: pip install mkdocs mkdocs-techdocs-core

▶️ Các bước:

# 1. Trong repo demo-payment-service, tạo cấu trúc docs
mkdir -p docs

cat > mkdocs.yml << 'EOF'
site_name: Payment Service
site_description: Documentation for payment-service
docs_dir: docs
plugins:
  - techdocs-core
EOF

cat > docs/index.md << 'EOF'
# Payment Service

Service xử lý giao dịch thanh toán real-time.

## Architecture
- REST API trên port 8080
- PostgreSQL cho persistent storage
- Kafka consumer cho event-driven processing

## SLOs
| Metric    | Target  |
|-----------|---------|
| Latency   | p99 < 200ms |
| Error Rate| < 0.1% |
| Uptime    | 99.95% |

## Runbook
Xem [Runbook](runbook.md) để biết cách xử lý sự cố.
EOF

cat > docs/runbook.md << 'EOF'
# Runbook: Payment Service

## High Error Rate
1. Check logs: `kubectl logs -l app=payment-service -n payments --tail=100`
2. Check DB connections: `kubectl exec -it postgres-pod -- psql -c "SELECT count(*) FROM pg_stat_activity"`
3. Nếu DB quá tải: scale down traffic, alert on-call DBA
EOF

git add mkdocs.yml docs/
git commit -m "docs: add TechDocs for payment-service"
git push origin main
# 2. Verify annotation trong catalog-info.yaml đã có:
# backstage.io/techdocs-ref: dir:.

# 3. Mở Backstage UI → Catalog → payment-service → tab "Docs"
# Backstage sẽ build MkDocs và render inline

# 4. Test build locally trước:
cd demo-payment-service
mkdocs serve   # http://localhost:8000 để preview

✅ Kết quả mong đợi: Tab "Docs" trong entity page hiển thị styled HTML từ Markdown. Navigation sidebar hiển thị cả "Home" và "Runbook". Tìm kiếm full-text trong TechDocs hoạt động.

🧹 Cleanup: Ctrl+C dừng mkdocs serve. Commit docs vào repo là xong.

LAB-154

Tạo Software Template — scaffolder tự động hóa khởi tạo service

Backstage Scaffolder · GitHub Actions

🎯 Mục tiêu: Viết Template YAML để developer tạo microservice mới (với catalog-info.yaml và CI pipeline) chỉ bằng form UI.

🧰 Công cụ / nền tảng: Backstage Scaffolder, GitHub token, VS Code.

📦 Chuẩn bị: Tạo GitHub Personal Access Token (PAT) với quyền repo scope. Cài vào Backstage backend config.

▶️ Các bước:

# 1. Trong my-portal, tạo thư mục templates
mkdir -p packages/backend/templates/microservice

# 2. Tạo template skeleton (nội dung repo mới sẽ được tạo từ đây)
mkdir -p packages/backend/templates/microservice/skeleton
cat > packages/backend/templates/microservice/skeleton/catalog-info.yaml << 'EOF'
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: ${{ values.name }}
  description: ${{ values.description }}
  tags: ${{ values.tags | dump }}
spec:
  type: service
  lifecycle: development
  owner: ${{ values.owner }}
  system: ${{ values.system }}
EOF

cat > packages/backend/templates/microservice/skeleton/README.md << 'EOF'
# ${{ values.name }}

${{ values.description }}

## Owner
${{ values.owner }}
EOF
# 3. Tạo Template descriptor
cat > packages/backend/templates/microservice/template.yaml << 'EOF'
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: microservice-template
  title: Microservice Starter
  description: Tạo microservice mới với catalog-info.yaml và README
  tags:
    - recommended
    - microservice
spec:
  owner: group:platform-team
  type: service
  parameters:
    - title: Service Information
      required:
        - name
        - description
        - owner
      properties:
        name:
          title: Service Name
          type: string
          description: Tên service (kebab-case, vd: order-service)
          pattern: '^[a-z][a-z0-9-]*$'
        description:
          title: Description
          type: string
        owner:
          title: Owner (Team)
          type: string
          description: Ví dụ group:team-backend
        system:
          title: System
          type: string
          default: default
    - title: Repository
      required:
        - repoUrl
      properties:
        repoUrl:
          title: GitHub Repository
          type: string
          ui:field: RepoUrlPicker
          ui:options:
            allowedHosts:
              - github.com
  steps:
    - id: fetch
      name: Fetch Template
      action: fetch:template
      input:
        url: ./skeleton
        values:
          name: ${{ parameters.name }}
          description: ${{ parameters.description }}
          owner: ${{ parameters.owner }}
          system: ${{ parameters.system }}
    - id: publish
      name: Publish to GitHub
      action: publish:github
      input:
        allowedHosts: ['github.com']
        description: ${{ parameters.description }}
        repoUrl: ${{ parameters.repoUrl }}
        defaultBranch: main
    - 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 }}
EOF
# 4. Đăng ký template vào Backstage
# Mở http://localhost:3000/catalog-import
# Import file template.yaml từ đường dẫn local hoặc push lên GitHub rồi import URL

# 5. Test: mở http://localhost:3000/create
# Chọn "Microservice Starter" → điền form → Click "Create"
# Backstage sẽ tạo GitHub repo mới và register vào catalog tự động

✅ Kết quả mong đợi: Form "Create" xuất hiện với đúng các field. Sau khi submit, Backstage chạy 3 steps (fetch → publish → register) và hiển thị link tới repo GitHub mới và entity trong catalog.

🧹 Cleanup: Xóa repo test trên GitHub nếu không cần. Template có thể giữ lại làm chuẩn nội bộ.

LAB-155

Service Ownership Model — Group, User, System entity

Backstage Catalog · YAML · API

🎯 Mục tiêu: Xây dựng organizational model hoàn chỉnh: Group (team), User, System, và Component liên kết nhau — phản ánh cấu trúc tổ chức thực tế.

🧰 Công cụ / nền tảng: VS Code, Backstage local, curl.

📦 Chuẩn bị: Backstage dev server đang chạy từ LAB-151.

▶️ Các bước:

# 1. Tạo file org.yaml chứa Group, User, System entities
cat > org.yaml << 'EOF'
---
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
  name: team-payments
  description: Team phụ trách payment domain
spec:
  type: team
  profile:
    displayName: Payment Team
    email: [email protected]
  children: []
  members:
    - nguyen-van-a
    - tran-thi-b
---
apiVersion: backstage.io/v1alpha1
kind: User
metadata:
  name: nguyen-van-a
  annotations:
    github.com/user-login: nguyenvana
spec:
  profile:
    displayName: Nguyễn Văn A
    email: [email protected]
  memberOf:
    - team-payments
---
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
  name: banking-platform
  description: Hệ thống core banking và payment gateway
spec:
  owner: group:team-payments
  domain: finance
---
apiVersion: backstage.io/v1alpha1
kind: Domain
metadata:
  name: finance
  description: Finance và payments domain
spec:
  owner: group:team-payments
EOF
# 2. Đăng ký qua Catalog Import UI
# http://localhost:3000/catalog-import
# Upload hoặc paste nội dung org.yaml

# 3. Verify qua Backstage API
# Kiểm tra Group entities
curl -s http://localhost:7007/api/catalog/entities?filter=kind=Group \
  | python3 -m json.tool | grep -E '"name":|"kind":'

# Kiểm tra System entities
curl -s http://localhost:7007/api/catalog/entities?filter=kind=System \
  | python3 -m json.tool | grep -E '"name":|"description":'

# 4. Xem trong UI
# http://localhost:3000/catalog?filters[kind]=Group  --> Team list
# http://localhost:3000/catalog?filters[kind]=System --> System list
# Click vào banking-platform System → Relations tab
# Sẽ thấy payment-service (từ LAB-152) nằm trong System này
# 5. Kiểm tra ownership chain:
# payment-service → owner: group:team-payments → members: nguyen-van-a
# Vào entity payment-service, sidebar hiển thị Team owner với avatar

✅ Kết quả mong đợi: Catalog có đủ 4 entity kinds: Component, Group, User, System. Trang System "banking-platform" hiển thị danh sách components thuộc system. Group "team-payments" hiển thị đúng members. Dependency graph vẽ được toàn bộ chuỗi ownership.

🧹 Cleanup: yarn backstage-cli clean để clear build cache. Xóa org.yaml local nếu không cần.

3. Tình huống doanh nghiệp thực tế

Bối cảnh

Một công ty fintech với 15 team, 200+ microservices. Developer mới mất 2 tuần mới biết cần đọc docs ở đâu, service X do ai owns, cách tạo service mới đúng chuẩn. Mỗi team dùng cấu trúc repo khác nhau → không consistent.

Giải pháp với Backstage

  • Service Catalog: Mỗi repo thêm catalog-info.yaml. Backstage auto-discover qua GitHub org scanner. Tất cả 200+ services lên catalog trong 1 ngày.
  • TechDocs: Yêu cầu mỗi service có mkdocs.yml + docs/. CI pipeline check: PR fail nếu thiếu docs cập nhật. Developer onboarding từ 2 tuần xuống 2 ngày.
  • Software Templates: Tạo 3 template chuẩn (REST API, event consumer, batch job). Developer mới tạo service đúng chuẩn trong 10 phút thay vì copy-paste từ repo cũ không biết có outdated không.
  • Ownership: Backstage trở thành nguồn truth cho on-call routing (PagerDuty plugin đọc owner từ catalog).
  • ROI đo được: MTTR giảm 40% vì engineer tìm đúng on-call và runbook ngay trong Backstage thay vì hỏi Slack.

📚 Nguồn tham khảo

Module 30: Platform Engineering Module 32: NoOps & Self-healing
Zalo