снизить friction discovery через catalog-first портал

Backstage как портал разработчиков: каталог сервисов, плагины и golden-path шаблоны

12 минут

Когда владение сервисом живёт в Slack, а runbook'и гниют в Confluence, поставка замедляется. В материале — как Backstage объединяет discovery через catalog-info.yaml, плагины, scaffolder-шаблоны и catalog-first rollout.

Почему разрозненные инструменты создают налог на discovery

Когда команд становится больше нескольких, инженеры тратят часы на поиск нужного репозитория, владельца сервиса или runbook'а, зарытого в wiki. Онбординг растягивается с дней до недель. Осиротевшие сервисы копятся, потому что ownership нигде не зафиксирован. Организации отвечают разрозненными wiki, таблицами и культурой «спроси в Slack», которая не масштабируется. Стоимость незаметна, но постоянна: каждый разработчик повторяет один и тот же discovery, а платформенная команда не может enforce'ить стандарты, которых не видит.

Backstage как connective tissue, а не замена CI/CD

Backstage, open source от Spotify и проект CNCF Incubating, — расширяемый internal developer portal. Он не заменяет GitHub Actions, Prometheus, PagerDuty или Kubernetes. Он агрегирует их, чтобы разработчик начинал работу из одной точки. На практике важны четыре опоры. Software Catalog регистрирует сервисы, библиотеки, API и ресурсы из catalog-info.yaml в Git. Software Templates (scaffolder) создают новые репозитории по golden-path blueprint'ам. TechDocs собирает документацию из MkDocs-источников в репозитории сервиса. Широкая экосистема плагинов показывает данные Kubernetes, GitHub, Terraform и observability без дублирования этих систем. В production Backstage обычно работает как Node.js-приложение с PostgreSQL для состояния портала и Elasticsearch, OpenSearch или PostgreSQL search module для поиска по каталогу, с аутентификацией через существующий OIDC-провайдер.

Регистрируйте каждый сервис через catalog-info.yaml

Сущность каталога — контракт между product-командами и platform engineering. Коммитьте catalog-info.yaml в корень репозитория, чтобы Backstage подхватывал ownership, lifecycle, зависимости и deep link'и на dashboard'ы или runbook'и. Ссылайтесь на другие сущности типизированными relation вроде resource:default/postgres-payment-db, а не свободными строками. Один валидированный файл питает поиск, граф зависимостей, точки входа TechDocs и контекст плагинов на странице Component.

YAML · catalog-info.yaml для production-сервиса
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payment-service
  description: Handles payment processing and refunds
  annotations:
    github.com/project-slug: myorg/payment-service
    pagerduty.com/service-id: P123456
    backstage.io/techdocs-ref: dir:.
  tags:
    - java
    - payments
    - tier-1
  links:
    - url: https://grafana.internal/d/payment-service
      title: Grafana Dashboard
spec:
  type: service
  lifecycle: production
  owner: group:payments-team
  system: commerce-platform
  providesApis:
    - payment-api
  dependsOn:
    - resource:default/postgres-payment-db

Расширяйте портал плагинами и scaffolder-шаблонами

Плагины — React-модули front-end с опциональными backend-route. Используйте их для чтения из существующих API — метрик CI, частоты деплоев, cost tag'ов — вместо параллельных хранилищ данных. Software Templates — custom resource Template в каталоге. Они собирают параметры, fetch'ат skeleton-директорию, рендерят файлы вроде catalog-info.yaml и публикуют репозиторий в GitHub или GitLab. Фрагмент skeleton ниже — то, что шаблон подставляет; манифест Template задаёт wizard и actions.

TypeScript · custom deployment frequency panel
import React from 'react';
import { useApi } from '@backstage/core-plugin-api';
import { deploymentMetricsApiRef } from '../api';
import { Progress } from '@backstage/core-components';
import { useAsync } from 'react-use';

export const DeploymentFrequency = ({ entity }) => {
  const api = useApi(deploymentMetricsApiRef);
  const { value, loading } = useAsync(
    () => api.getDeploymentFrequency(entity.metadata.name),
    [entity]
  );

  if (loading) return <Progress />;

  return (
    <div>
      <h3>Deployment Frequency (last 30 days)</h3>
      <p>{value?.count ?? 'N/A'} deployments</p>
      <p>Average interval: {value?.avgInterval ?? 'N/A'}</p>
    </div>
  );
};
YAML · scaffolder Template с catalog skeleton
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: microservice
  title: New microservice
  description: Scaffold a service with catalog registration and CI wiring
spec:
  owner: group:platform-team
  type: service
  parameters:
    - title: Service metadata
      required: [name, owner, system, org]
      properties:
        name:
          title: Name
          type: string
        description:
          title: Description
          type: string
        owner:
          title: Owner group
          type: string
        system:
          title: System
          type: string
        org:
          title: GitHub org
          type: string
          default: myorg
  steps:
    - id: fetch
      name: Fetch skeleton
      action: fetch:template
      input:
        url: ./skeleton
        values:
          name: ${{ parameters.name }}
          description: ${{ parameters.description }}
          owner: ${{ parameters.owner }}
          system: ${{ parameters.system }}
          org: ${{ parameters.org }}
    - id: publish
      name: Publish to GitHub
      action: publish:github
      input:
        repoUrl: github.com?owner=${{ parameters.org }}&repo=${{ parameters.name }}
  output:
    links:
      - title: Repository
        url: ${{ steps['publish'].output.remoteUrl }}
---
# templates/microservice/skeleton/catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: ${{ values.name }}
  description: ${{ values.description }}
  annotations:
    github.com/project-slug: ${{ values.org }}/${{ values.name }}
spec:
  type: service
  lifecycle: experimental
  owner: ${{ values.owner }}
  system: ${{ values.system }}

Практики rollout для платформенных команд

Начинайте с покрытия каталога, а не с кастомного UI. Enforce'ите catalog-info.yaml через CI-валидацию на каждом merge, чтобы портал не расходился с Git. Поддерживайте три–пять scaffolder-шаблонов как golden path; сломанный шаблон подрывает доверие быстрее, чем его отсутствие. Требуйте owner на каждой сущности и линтуйте это правило. Размещайте TechDocs в docs/ с mkdocs.yml, чтобы документация ехала вместе с кодом. Подключите SSO и permission framework, прежде чем открывать регистрацию шаблонов на широкую запись. Запускайте production Backstage в Kubernetes с PostgreSQL, поддерживаемым search backend и минимум двумя репликами. Версионируйте шаблоны в Git с PR-review как код приложения. Измеряйте adoption — долю сервисов в каталоге, использование шаблонов, время онбординга — а не число плагинов. Developer portal — инфраструктура инженерной скорости: начинайте с малого, автоматизируйте ingestion каталога и считайте операционную ясность главным результатом.

Backstage часто становится UI-слоем поверх абстракций деплоя из гайда по internal developer platform.

Записи каталога должны вести к здоровой телеметрии delivery — rollout портала сочетайте с диагностикой узких мест release-pipeline.