Nâng cao
9 phút đọc2 tháng 6, 20261

Backstage: Xây dựng Developer Portal cho team của bạn từ A đến Z

Backstage là framework mã nguồn mở từ Spotify giúp bạn xây dựng developer portal, gom tất cả service, docs, tooling về một nơi. Mình sẽ hướng dẫn setup chi tiết.

N

Nguyễn Nhật Long

@nguyennhatlong1303

Backstage: Xây dựng Developer Portal cho team của bạn từ A đến Z

Backstage: Xây dựng Developer Portal cho team của bạn từ A đến Z

Mình nhớ cái thời mới vào một công ty có hơn 50 microservices. Muốn tìm owner của một service thì phải hỏi trên Slack. Muốn biết service đó dùng database gì thì phải đọc README (nếu có). Muốn tạo một service mới thì copy-paste từ service cũ rồi sửa tay. Documentation thì nằm rải rác từ Confluence, Google Docs đến Notion. Nghe quen không?

Đó chính xác là vấn đề mà Backstage giải quyết. Và hôm nay mình sẽ đi sâu vào nó không phải kiểu overview chung chung, mà là thực sự setup, config, và hiểu cách nó hoạt động.

Backstage giải quyết cái gì mà Spotify phải tự build?

Backstage được Spotify tạo ra từ nội bộ, ban đầu chỉ để dùng cho chính họ. Bạn thử tưởng tượng Spotify có hơn 2000 microservices, hàng trăm team dev nếu không có một nơi trung tâm để quản lý tất cả thì sẽ loạn. Sau đó họ open-source nó và donate cho CNCF (Cloud Native Computing Foundation), hiện tại đang ở mức Incubation tức là đã đủ mature để production dùng được.

Core idea của Backstage xoay quanh 3 trụ cột chính:

Ngoài 3 cái core đó, Backstage còn có một hệ sinh thái plugin cực kỳ phong phú Kubernetes, CI/CD, monitoring, cost management... gần như cái gì cũng có plugin.

Tính năngMô tảGiải quyết vấn đề gì
**Software Catalog**Quản lý tất cả service, library, website, data pipeline, ML model trong một nơiKhông còn phải hỏi "ai own cái service này?"
**Software Templates**Template để tạo project mới theo chuẩn của tổ chứcKhông còn copy-paste rồi sửa tay
**TechDocs**Documentation as code, viết bằng Markdown, render trực tiếp trong portalDocs không còn nằm rải rác khắp nơi

Bắt tay vào setup Backstage

Điều kiện tiên quyết: bạn cần Node.js (phiên bản 18 hoặc 20), Yarn, và Git. Backstage dùng Yarn Berry (v3+) nên anh em đừng dùng npm nhé.

Terminal
1# Tạo Backstage app mới
2npx @backstage/create-app@latest

Command này sẽ hỏi bạn tên app, rồi nó scaffold ra một monorepo hoàn chỉnh. Cấu trúc folder trông sẽ như thế này:

TEXT
1my-backstage-app/
2├── app-config.yaml # Config chính
3├── catalog-info.yaml # Catalog entity của chính app này
4├── packages/
5│ ├── app/ # Frontend (React)
6│ └── backend/ # Backend (Node.js/Express)
7├── plugins/ # Custom plugins của bạn
8└── package.json

Sau khi scaffold xong, chạy:

Terminal
1cd my-backstage-app
2yarn install
3yarn dev

yarn dev sẽ start cả frontend (port 3000) và backend (port 7007) cùng lúc. Mở browser lên http://localhost:3000 là bạn sẽ thấy giao diện Backstage.

Theo kinh nghiệm của mình, bước setup ban đầu này rất smooth. Cái khó nằm ở phần config và customize sau đó.

Hiểu Software Catalog trái tim của Backstage

Software Catalog là thứ quan trọng nhất trong Backstage. Nó là nơi bạn đăng ký tất cả "entities" trong hệ thống service, API, website, library, team, user... Mỗi entity được mô tả bằng một file YAML gọi là catalog-info.yaml, đặt ngay trong repo của service đó.

Đây là một ví dụ thực tế cho một service:

YAML
1apiVersion: backstage.io/v1alpha1
2kind: Component
3metadata:
4 name: payment-service
5 description: Handles payment processing via Stripe and VNPay
6 annotations:
7 github.com/project-slug: my-org/payment-service
8 backstage.io/techdocs-ref: dir:.
9 tags:
10 - java
11 - spring-boot
12 - payments
13 links:
14 - url: https://grafana.internal.com/d/payment
15 title: Grafana Dashboard
16 icon: dashboard
17spec:
18 type: service
19 lifecycle: production
20 owner: team-payment
21 system: checkout-system
22 providesApis:
23 - payment-api
24 consumesApis:
25 - user-api
26 - notification-api
27 dependsOn:
28 - resource:payment-db

Bạn thấy không? Chỉ với file YAML này, Backstage đã biết: service này do team nào own, đang ở lifecycle nào (production/experimental/deprecated), cung cấp API gì, consume API gì, phụ thuộc vào resource nào. Khi bạn mở Backstage lên, nó sẽ render thành một trang chi tiết với tất cả thông tin này, kèm theo link đến Grafana, GitHub, và docs.

Để Backstage biết tìm file catalog-info.yaml ở đâu, bạn config trong app-config.yaml:

YAML
1catalog:
2 import:
3 entityFilename: catalog-info.yaml
4 pullRequestBranchName: backstage-integration
5 rules:
6 - allow: [Component, System, API, Resource, Location, Template, Group, User]
7 locations:
8 # Scan toàn bộ org trên GitHub
9 - type: github-discovery
10 target: https://github.com/my-org/*/blob/main/catalog-info.yaml
11
12 # Hoặc add từng repo cụ thể
13 - type: url
14 target: https://github.com/my-org/payment-service/blob/main/catalog-info.yaml

Mình thấy cái hay ở chỗ là bạn dùng github-discovery thì Backstage sẽ tự động scan tất cả repo trong org, tìm file catalog-info.yaml và import vào. Team chỉ cần thêm file YAML vào repo của mình là xong không cần vào Backstage UI để đăng ký gì cả.

Anh em lưu ý: lần đầu scan một org lớn (vài trăm repo) có thể mất khá lâu và hit rate limit của GitHub API. Config thêm schedule để nó scan định kỳ thay vì liên tục:

YAML
1catalog:
2 providers:
3 github:
4 myOrg:
5 organization: 'my-org'
6 catalogPath: '/catalog-info.yaml'
7 filters:
8 branch: 'main'
9 schedule:
10 frequency: { minutes: 30 }
11 timeout: { minutes: 3 }

Software Templates Standardize cách tạo project mới

Đây là feature mà theo mình, nó mang lại giá trị ngay lập tức cho team. Thay vì mỗi dev tự tạo project theo cách riêng, bạn define template trong Backstage. Dev chỉ cần vào portal, chọn template, điền vài thông tin, rồi Backstage sẽ tự tạo repo trên GitHub, setup CI/CD pipeline, đăng ký service vào catalog tất cả tự động.

Một template trông như thế này:

YAML
1apiVersion: scaffolder.backstage.io/v1beta3
2kind: Template
3metadata:
4 name: spring-boot-service
5 title: Spring Boot Microservice
6 description: Tạo một Spring Boot service mới với đầy đủ CI/CD, Docker, và monitoring
7 tags:
8 - java
9 - spring-boot
10spec:
11 owner: team-platform
12 type: service
13
14 parameters:
15 - title: Thông tin service
16 required:
17 - name
18 - owner
19 properties:
20 name:
21 title: Tên service
22 type: string
23 description: Unique name cho service (kebab-case)
24 pattern: '^[a-z0-9-]+$'
25 owner:
26 title: Team owner
27 type: string
28 ui:field: OwnerPicker
29 ui:options:
30 catalogFilter:
31 kind: Group
32 description:
33 title: Mô tả
34 type: string
35
36 - title: Infrastructure
37 properties:
38 database:
39 title: Database
40 type: string
41 enum: ['postgresql', 'mysql', 'none']
42 default: 'postgresql'
43 javaVersion:
44 title: Java Version
45 type: string
46 enum: ['17', '21']
47 default: '21'
48
49 steps:
50 - id: fetch-template
51 name: Fetch skeleton code
52 action: fetch:template
53 input:
54 url: ./skeleton
55 values:
56 name: ${{ parameters.name }}
57 owner: ${{ parameters.owner }}
58 database: ${{ parameters.database }}
59 javaVersion: ${{ parameters.javaVersion }}
60
61 - id: publish
62 name: Tạo GitHub repository
63 action: publish:github
64 input:
65 allowedHosts: ['github.com']
66 repoUrl: github.com?owner=my-org&repo=${{ parameters.name }}
67 defaultBranch: main
68 repoVisibility: internal
69
70 - id: register
71 name: Đăng ký vào Backstage Catalog
72 action: catalog:register
73 input:
74 repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
75 catalogInfoPath: '/catalog-info.yaml'
76
77 output:
78 links:
79 - title: Repository
80 url: ${{ steps['publish'].output.remoteUrl }}
81 - title: Mở trong Backstage
82 icon: catalog
83 entityRef: ${{ steps['register'].output.entityRef }}

Folder skeleton bên cạnh file template này chứa source code mẫu với các placeholder dùng Nunjucks syntax (${{ values.name }}). Khi dev chọn template và điền form, Backstage sẽ render skeleton code, tạo repo, push code, rồi đăng ký vào catalog. Cực kỳ mượt.

TechDocs Docs nằm cạnh code

Backstage dùng approach "docs like code": bạn viết docs bằng Markdown, config MkDocs, và Backstage render nó thành một trang docs đẹp ngay trong portal.

Thêm file mkdocs.yml vào root repo:

YAML
1site_name: Payment Service
2nav:
3 - Home: index.md
4 - Architecture: architecture.md
5 - API Reference: api.md
6 - Runbook: runbook.md
7 - Troubleshooting: troubleshooting.md
8
9plugins:
10 - techdocs-core

Rồi tạo folder docs/ với các file Markdown tương ứng. Trong catalog-info.yaml, thêm annotation:

YAML
1metadata:
2 annotations:
3 backstage.io/techdocs-ref: dir:.

Khi dev mở service trong Backstage, tab "Docs" sẽ hiện toàn bộ documentation. Docs luôn cập nhật theo code vì nó nằm cùng repo. Mình thấy approach này giải quyết triệt để vấn đề docs outdated vì khi dev sửa code, họ sửa docs luôn trong cùng một PR.

Config authentication và database cho production

Default Backstage dùng SQLite in-memory, tức là restart là mất hết data. Cho production, bạn cần PostgreSQL:

YAML
1# app-config.production.yaml
2backend:
3 database:
4 client: pg
5 connection:
6 host: ${POSTGRES_HOST}
7 port: ${POSTGRES_PORT}
8 user: ${POSTGRES_USER}
9 password: ${POSTGRES_PASSWORD}

Về authentication, Backstage hỗ trợ nhiều provider. Phổ biến nhất là GitHub OAuth:

YAML
1auth:
2 environment: production
3 providers:
4 github:
5 production:
6 clientId: ${AUTH_GITHUB_CLIENT_ID}
7 clientSecret: ${AUTH_GITHUB_CLIENT_SECRET}

Rồi trong code backend, bạn cần config sign-in resolver để map GitHub user với Backstage entity:

TypeScript
1// packages/backend/src/index.ts
2import { createBackend } from '@backstage/backend-defaults';
3
4const backend = createBackend();
5
6backend.add(import('@backstage/plugin-auth-backend'));
7backend.add(import('@backstage/plugin-auth-backend-module-github-provider'));
8backend.add(import('@backstage/plugin-catalog-backend'));
9backend.add(import('@backstage/plugin-techdocs-backend'));
10backend.add(import('@backstage/plugin-scaffolder-backend'));
11
12backend.start();

Backstage mới chuyển sang "new backend system" dùng declarative approach như trên, thay vì phải wire mọi thứ manually. Nếu anh em đọc tutorial cũ thấy code backend dài dằng dặc thì đừng hoảng version mới clean hơn nhiều.

Plugin ecosystem sức mạnh thực sự

Backstage có hơn 200+ plugin từ community. Đây là một số plugin mình thấy hữu ích nhất:

Cài plugin thường chỉ cần:

PluginChức năngKhi nào cần
`@backstage/plugin-kubernetes`Xem pods, deployments, logs trực tiếp trong BackstageTeam dùng K8s
`@backstage/plugin-github-actions`Xem CI/CD status từ GitHub ActionsDùng GitHub Actions
`@roadiehq/backstage-plugin-argo-cd`Quản lý ArgoCD deploymentsDùng ArgoCD
`@backstage/plugin-cost-insights`Theo dõi cloud cost theo serviceMuốn control chi phí
`@backstage/plugin-api-docs`Render OpenAPI/AsyncAPI specsCó API documentation
`@pagerduty/backstage-plugin`Xem on-call schedule, incidentsDùng PagerDuty
Terminal
1# Cài plugin frontend
2yarn --cwd packages/app add @backstage/plugin-kubernetes
3
4# Cài plugin backend
5yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend

Rồi thêm vào backend và frontend config. Mỗi plugin có docs riêng khá chi tiết.

Deploy Backstage lên production

Backstage cung cấp sẵn Dockerfile. Build image:

Terminal
1yarn install --frozen-lockfile
2yarn tsc
3yarn build:backend
4
5docker build -t backstage:latest -f packages/backend/Dockerfile .

Một docker-compose.yml đơn giản cho staging:

YAML
1version: '3.8'
2services:
3 backstage:
4 image: backstage:latest
5 ports:
6 - '7007:7007'
7 environment:
8 POSTGRES_HOST: db
9 POSTGRES_PORT: 5432
10 POSTGRES_USER: backstage
11 POSTGRES_PASSWORD: backstage
12 depends_on:
13 - db
14
15 db:
16 image: postgres:15
17 environment:
18 POSTGRES_USER: backstage
19 POSTGRES_PASSWORD: backstage
20 POSTGRES_DB: backstage
21 volumes:
22 - pgdata:/var/lib/postgresql/data
23
24volumes:
25 pgdata:

Cho production thực sự, mình recommend deploy lên Kubernetes với Helm chart. Community có maintain chart tại backstage/charts trên GitHub.

Những bài học mình rút ra sau khi triển khai

Sau khi đưa Backstage vào dùng cho team ~40 dev, mình có vài điều muốn chia sẻ:

Bắt đầu nhỏ. Đừng cố enable tất cả plugin cùng lúc. Bắt đầu với Software Catalog trước chỉ cần mọi người thêm catalog-info.yaml vào repo là đã có giá trị rồi. Sau đó mới thêm Templates, rồi TechDocs.

Ownership là key. Cái giá trị lớn nhất mình thấy không phải là technology, mà là việc mọi service đều có owner rõ ràng. Khi có incident, bạn mở Backstage lên, tìm service, biết ngay team nào own, ai đang on-call.

Đừng underestimate effort maintain. Backstage release rất thường xuyên (gần như hàng tuần). Upgrade version đôi khi có breaking changes. Team platform cần allocate time để maintain. Backstage có tool backstage-cli versions:bump để giúp việc upgrade, nhưng vẫn cần test kỹ.

Custom plugin không khó như bạn nghĩ. Backstage dùng React cho frontend và Express cho backend. Nếu team bạn cần integrate với internal tool, viết custom plugin khá straightforward. Scaffold plugin bằng:

Terminal
1yarn new --select plugin

Nó sẽ tạo sẵn structure cho bạn, chỉ cần fill logic vào.

Nếu team bạn đang ở giai đoạn 20+ services trở lên và bắt đầu thấy chaos trong việc quản lý Backstage là một investment rất đáng. Nó không phải silver bullet, vẫn cần effort để setup và maintain, nhưng cái giá trị nó mang lại cho developer experience là rất rõ ràng. Mỗi lần onboard dev mới, thay vì phải giải thích "service A gọi service B qua Kafka, rồi service C poll từ S3..." thì chỉ cần nói: "mở Backstage lên, tất cả ở đó".

NN

Nguyễn Nhật Long

@nguyennhatlong1303

Nguyễn Nhật Long is a Senior Frontend Engineer and Frontend Team Leader with 7 years of experience building real-time fintech platforms. Specializing in React, Next.js, TypeScript, and React Native, shipping 10+ products across Web, Mobile, Telegram Mini-Apps, and Web3.

Thấy hay? Chia sẻ cho bạn bè!