DevOps & Cloud

Backstage Developer Portal

GRATIS

Bangun Internal Developer Platform dengan Backstage — Software Catalog, TechDocs, Scaffolder templates, plugin ecosystem, dan RBAC

1. Apa itu Backstage?

Backstage adalah open-source platform dari Spotify (sekarang CNCF) untuk membangun Internal Developer Portal (IDP). Backstage menyatukan tooling, layanan, dokumentasi, dan infrastruktur dalam satu portal yang meningkatkan developer experience.

Arsitektur Backstage
🖥️
Frontend
React, Plugin pages
→ API →
⚙️
Backend
Node.js, Catalog API
→ →
🔌
Plugins
K8s, CI/CD, Grafana
→ →
📊
External Systems
GitHub, ArgoCD, AWS

2. Setup & Instalasi

Terminal — Create Backstage App
# Prerequisites: Node.js 18+, yarn
npx @backstage/create-app@latest my-portal
cd my-portal
yarn install
yarn dev
# Buka http://localhost:3000

# Project structure:
# my-portal/
# ├── app-config.yaml
# ├── packages/
# │   ├── app/      # Frontend React
# │   └── backend/  # Backend Node.js
# └── plugins/
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
  database:
    client: better-sqlite3
    connection: ':memory:'
    # Production: gunakan PostgreSQL
    # client: pg
    # connection:
    #   host: ${POSTGRES_HOST}
    #   port: 5432
    #   user: ${POSTGRES_USER}
    #   password: ${POSTGRES_PASSWORD}
    #   database: backstage

auth:
  providers:
    github:
      development:
        clientId: ${GITHUB_CLIENT_ID}
        clientSecret: ${GITHUB_CLIENT_SECRET}

catalog:
  rules:
    - allow: [Component, System, API, Resource, Group, User, Template]
  locations:
    - type: file
      target: ../catalog-model/examples/all.yaml
    - type: github-discovery
      target: https://github.com/my-org/blob/main/catalog-info.yaml

integrations:
  github:
    - host: github.com
      token: ${GITHUB_TOKEN}

3. Software Catalog

Software Catalog adalah jantung Backstage — registry terpusat semua service, library, API, dan resource. Setiap repo mendefinisikan catalog-info.yaml.

catalog-info.yaml — User Service
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: user-service
  description: Service autentikasi dan manajemen user
  annotations:
    github.com/project-slug: my-org/user-service
    argocd/app-name: user-service
    jira/project-key: USER
    backstage.io/techdocs-ref: dir:.
    backstage.io/kubernetes-id: user-service
    sonarqube.org/project-key: my-org_user-service
  tags: [go, grpc, production]
  links:
    - url: https://grafana.example.com/d/user-service
      title: Grafana Dashboard
      icon: dashboard
    - url: https://github.com/my-org/user-service
      title: Source Code
      icon: github
  labels:
    tier: critical
    team: platform
spec:
  type: service
  lifecycle: production
  owner: group:team-platform
  system: auth-system
  providesApis:
    - user-api
  dependsOn:
    - component:postgres-database
  consumesApis:
    - notification-api

3.1 Entity Types di Backstage

KindFungsiContoh
ComponentService, library, websiteuser-api, shared-utils
APIAPI specificationREST API, gRPC
ResourceInfrastructureDatabase, S3 bucket
SystemGrup related componentspayment-system
DomainBusiness domainuser-domain
GroupTeam/org unitteam-platform
UserIndividual persondeveloper accounts
API & System definitions
# API Definition
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
  name: user-api
  description: REST API untuk manajemen user
spec:
  type: openapi
  lifecycle: production
  owner: group:team-platform
  system: auth-system
  definition: |
    openapi: "3.0.0"
    info:
      version: "1.0.0"
      title: User API
    paths:
      /users:
        get:
          summary: List all users
          responses:
            '200':
              description: List of users
      /users/{id}:
        get:
          summary: Get user by ID
          parameters:
            - name: id
              in: path
              required: true
              schema:
                type: string
          responses:
            '200':
              description: User details

---
# System Definition
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
  name: auth-system
  description: Autentikasi dan authorisasi
  tags: [auth, security]
spec:
  owner: group:team-platform
  domain: user-domain

---
# Domain Definition
apiVersion: backstage.io/v1alpha1
kind: Domain
metadata:
  name: user-domain
  description: Semua terkait user management
spec:
  owner: group:team-platform

---
# Group Definition
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
  name: team-platform
  description: Platform Engineering Team
spec:
  type: team
  profile:
    displayName: Platform Team
    email: platform@company.com
  children: []
  members: [user:jane, user:john]

---
# User Definition
apiVersion: backstage.io/v1alpha1
kind: User
metadata:
  name: jane
spec:
  profile:
    displayName: Jane Doe
    email: jane@company.com
  memberOf: [group:team-platform]
💡 GitHub Discovery

Gunakan github-discovery untuk otomatis menemukan semua repo yang memiliki catalog-info.yaml. Ini menghilangkan kebutuhan registrasi manual — cukup tambahkan file catalog-info.yaml ke repo dan Backstage mendeteksinya.

4. TechDocs

TechDocs memungkinkan dokumentasi yang di-commit langsung bersama kode menggunakan MkDocs.

mkdocs.yaml — TechDocs configuration
# mkdocs.yml di root repository
site_name: User Service Documentation

nav:
  - Home: index.md
  - Getting Started: getting-started.md
  - API Reference: api-reference.md
  - Architecture: architecture.md
  - Runbook: runbook.md

plugins:
  - techdocs-core

# Tambahkan annotation di catalog-info.yaml:
# metadata:
#   annotations:
#     backstage.io/techdocs-ref: dir:.

# Struktur docs/
# docs/
# ├── index.md
# ├── getting-started.md
# ├── api-reference.md
# ├── architecture.md
# └── runbook.md

# Build locally: mkdocs serve
# Build untuk Backstage: npx @backstage/cli techdocs:build
docs/index.md — TechDocs example
# User Service

## Overview
User Service menangani autentikasi dan manajemen user.
Menyediakan REST API untuk registrasi, login, dan CRUD.

## Architecture
```mermaid
graph LR
  Client --> Gateway[API Gateway]
  Gateway --> UserService[User Service]
  UserService --> Postgres[(PostgreSQL)]
  UserService --> Redis[(Redis Cache)]
  UserService --> Kafka[Event Bus]
```

## Quick Start
### Prerequisites
- Go 1.22+, PostgreSQL 15+, Redis 7+

### Running Locally
```bash
git clone https://github.com/my-org/user-service.git
cd user-service
cp .env.example .env
docker-compose up -d postgres redis
go run cmd/migrate/main.go up
go run cmd/server/main.go
```

## Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| DB_HOST | PostgreSQL host | localhost |
| DB_PORT | PostgreSQL port | 5432 |
| REDIS_URL | Redis URL | redis://localhost:6379 |
| JWT_SECRET | JWT signing secret | (required) |

## Monitoring
- Grafana Dashboard: https://grafana.example.com/d/user-service
- PagerDuty: https://pagerduty.com/user-service
- Logs: https://grafana.example.com/explore?query=user-service

5. Scaffolder Templates

Scaffolder memungkinkan developer membuat service baru dari template. Mendefinisikan steps yang dijalankan untuk meng-generate, register, dan deploy service.

template-node-microservice.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: create-node-microservice
  title: Node.js Microservice
  description: Buat Node.js microservice baru dengan standar perusahaan
  tags: [nodejs, microservice, recommended]
  annotations:
    backstage.io/time: '3 minutes'
spec:
  owner: group:team-platform
  type: service

  parameters:
    - title: Service Information
      required: [name, description, owner]
      properties:
        name:
          title: Service Name
          type: string
          pattern: '^[a-z][a-z0-9-]*[a-z0-9]$'
          examples: [user-service, payment-gateway]
        description:
          title: Description
          type: string
        owner:
          title: Owner Team
          type: string
          ui:field: OwnerPicker
        system:
          title: System
          type: string
          ui:field: EntityPicker
          ui:options:
            catalogFilter:
              - kind: System
    - title: Technology Options
      properties:
        database:
          title: Database
          type: string
          enum: [none, postgres, mysql, mongodb]
          default: postgres
        ci:
          title: CI/CD
          type: string
          enum: [github-actions, gitlab-ci]
          default: github-actions

  steps:
    - id: fetch
      name: Fetch Template
      action: fetch:template
      input:
        url: https://github.com/my-org/templates/node-microservice
        values:
          name: ${{ parameters.name }}
          description: ${{ parameters.description }}
          owner: ${{ parameters.owner }}
          system: ${{ parameters.system }}
          database: ${{ parameters.database }}

    - id: publish
      name: Publish to GitHub
      action: github:repo:create
      input:
        repoUrl: 'github.com?repo=${{ parameters.name }}&owner=my-org'
        description: ${{ parameters.description }}
        visibility: private

    - id: register
      name: Register in Catalog
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps.publish.output.remoteUrl }}
        catalogInfoPath: '/catalog-info.yaml'

    - id: argocd
      name: Create ArgoCD Application
      action: argocd:create-app
      input:
        appName: ${{ parameters.name }}

  output:
    links:
      - title: Repository
        url: ${{ steps.publish.output.remoteUrl }}
      - title: Catalog
        entityRef: ${{ steps.register.output.entityRef }}

6. Plugin Ecosystem

Backstage memiliki ekosistem plugin yang kaya untuk integrasi berbagai tool DevOps.

PluginFungsiPackage
KubernetesStatus pods, deployments@backstage/plugin-kubernetes
GitHub ActionsCI/CD pipeline status@backstage/plugin-github-actions
GrafanaEmbed dashboard@k-phoen/backstage-plugin-grafana
ArgoCDGitOps status@roadiehq/backstage-plugin-argo-cd
SonarQubeCode quality@backstage/plugin-sonarqube
JiraIssue tracking@backstage/plugin-jira
PagerDutyOn-call & incidents@backstage/plugin-pagerduty
Plugin installation & configuration
# Install plugins
cd packages/app
yarn add @backstage/plugin-kubernetes
yarn add @backstage/plugin-github-actions
yarn add @k-phoen/backstage-plugin-grafana

# Register di packages/app/src/App.tsx
import { KubernetesPage } from '@backstage/plugin-kubernetes';
import { EntityGitHubActionsContent } from '@backstage/plugin-github-actions';

# Entity page: tab untuk component
# <EntityLayout>
#   <EntityLayout.Route path="/" title="Overview">
#     <EntityOverviewPage />
#   </EntityLayout.Route>
#   <EntityLayout.Route path="/ci-cd" title="CI/CD">
#     <EntityGitHubActionsContent />
#   </EntityLayout.Route>
#   <EntityLayout.Route path="/kubernetes" title="K8s">
#     <EntityKubernetesContent />
#   </EntityLayout.Route>
# </EntityLayout>

# Backend plugin
# packages/backend/src/index.ts
backend.add(import('@backstage/plugin-kubernetes-backend'));
backend.add(import('@backstage/plugin-catalog-backend'));

7. RBAC & Authentication

RBAC & OAuth Configuration
# Custom Permission Policy
# packages/backend/src/plugins/permission.ts
import { PermissionPolicy } from '@backstage/plugin-permission-node';

class CustomPermissionPolicy implements PermissionPolicy {
  async handle(request: PolicyQuery, user?: BackstageIdentityResponse) {
    const identity = user?.identity;

    // Admin: full access
    if (identity?.ownershipEntityRef.includes('group:team-platform')) {
      return { result: AuthorizeResult.ALLOW };
    }

    // Read access for all authenticated users
    if (request.permission.attributes.action === 'read') {
      return { result: AuthorizeResult.ALLOW };
    }

    // Write: only owner
    return {
      result: AuthorizeResult.CONDITIONAL,
      conditions: {
        rule: 'IS_OWNER',
        params: { claims: identity?.ownershipEntityRef ?? [] },
      },
    };
  }
}

# OAuth2/OIDC config (app-config.yaml)
# auth:
#   providers:
#     github:
#       development:
#         clientId: ${GITHUB_CLIENT_ID}
#         clientSecret: ${GITHUB_CLIENT_SECRET}
#         signIn:
#           resolvers:
#             - resolver: usernameMatchingUserEntityName

# Group ownership mapping
# team-platform di GitHub → group:team-platform di Backstage
# auth.providers.github.development:
#   signIn.resolvers:
#     - resolver: usernameMatchingUserEntityName
#     - resolver: emailLocalPartMatchingUserEntityName
#     - resolver: emailMatchingUserEntityAnnotation

8. Production Deployment

Dockerfile & Kubernetes
# Dockerfile
# Stage 1: Build
FROM node:18-bullseye-slim AS build
WORKDIR /app
COPY yarn.lock package.json ./
COPY packages/backend/package.json packages/backend/
COPY packages/app/package.json packages/app/
RUN yarn install --frozen-lockfile --production=false
COPY . .
RUN yarn tsc
RUN yarn build:backend

# Stage 2: Production
FROM node:18-bullseye-slim
WORKDIR /app
COPY --from=build /app/packages/backend/dist/bundle.tar.gz .
RUN tar xzf bundle.tar.gz && rm bundle.tar.gz
COPY --from=build /app/app-config.yaml .
ENV NODE_ENV=production
EXPOSE 7007
CMD ["node", "packages/backend"]

---
# Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backstage
  namespace: developer-portal
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backstage
  template:
    metadata:
      labels:
        app: backstage
    spec:
      containers:
        - name: backstage
          image: my-registry/backstage:latest
          ports:
            - containerPort: 7007
          env:
            - name: APP_CONFIG_app_baseUrl
              value: "https://developer-portal.example.com"
            - name: GITHUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: backstage-secrets
                  key: github-token
          resources:
            limits:
              cpu: 500m
              memory: 512Mi
            requests:
              cpu: 100m
              memory: 256Mi
          livenessProbe:
            httpGet:
              path: /api/catalog/health
              port: 7007
            initialDelaySeconds: 30
          readinessProbe:
            httpGet:
              path: /api/catalog/health
              port: 7007
            initialDelaySeconds: 10

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: backstage
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
    - hosts: [developer-portal.example.com]
      secretName: backstage-tls
  rules:
    - host: developer-portal.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: backstage
                port:
                  number: 7007
⚠️ PostgreSQL untuk Production

Jangan gunakan SQLite untuk production. Gunakan PostgreSQL 15+ sebagai database backend. Konfigurasikan connection pooling dengan PgBouncer untuk performa optimal. Pastikan backup database dilakukan secara teratur.

9. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut:

Pertanyaan 1: Apa fungsi utama Software Catalog di Backstage?

a) Menyimpan source code
b) Registry terpusat berisi metadata semua service, API, library, dan resource di organisasi
c) Menggantikan Git repository
d) Monitoring performa aplikasi

Pertanyaan 2: Apa fungsi Scaffolder di Backstage?

a) Mendeploy aplikasi ke production
b) Membuat service baru dari template yang sudah didefinisikan, memastikan standarisasi
c) Menghapus service yang tidak digunakan
d) Mengelola secrets

Pertanyaan 3: Mengapa TechDocs menggunakan "docs-as-code"?

a) Karena Markdown lebih cepat dari HTML
b) Karena dokumentasi di-commit bersama kode, selalu up-to-date dan bisa di-review
c) Karena Backstage tidak mendukung HTML
d) Karena Markdown lebih aman

Pertanyaan 4: Apa fungsi annotation backstage.io/kubernetes-id?

a) Mengaktifkan sidecar injection
b) Menghubungkan komponen catalog dengan deployment K8s sehingga status terlihat di portal
c) Mengatur RBAC Kubernetes
d) Menentukan namespace

Pertanyaan 5: Apa keuntungan GitHub discovery di Backstage Catalog?

a) Menggantikan GitHub Actions
b) Otomatis menemukan repo dengan catalog-info.yaml tanpa registrasi manual
c) Menghapus repository tidak aktif
d) Mengelola GitHub permissions
← SebelumnyaOpenTofu: Terraform Alternative Selanjutnya →Grafana Stack: Mimir + Tempo + Loki
🔍 Zoom
100%
🎨 Tema