ITSkillsCenter
Développement Web

Software Catalog Backstage : référencer ses services avec catalog-info.yaml

15 min de lecture

Article principal : Plateforme de développeur interne avec Backstage : architecture pour startup
Ce tutoriel s’inscrit dans la série Platform Engineering et Backstage. Pour la vue d’ensemble du sujet, lire d’abord le guide principal.

Pourquoi modéliser son catalogue avant d’écrire la moindre ligne de YAML

Le Software Catalog est ce qui transforme Backstage d’une coquille jolie en un véritable centre de gravité technique. Mais une erreur fréquente consiste à écrire des dizaines de fichiers catalog-info.yaml sans avoir réfléchi à la modélisation, et on se retrouve six mois plus tard avec un catalogue qui dit tous mes services existent mais qui est incapable de répondre aux questions utiles : qui est responsable de quoi, quelle équipe possède quel domaine métier, quelle API consomme quel service. Ce tutoriel construit pas-à-pas un catalogue cohérent, depuis la première entité jusqu’à l’auto-discovery sur une organisation GitHub.

L’objectif final est concret. À la fin du parcours, vous aurez un Backstage qui découvre automatiquement les fichiers catalog-info.yaml dans tous les dépôts d’une organisation GitHub, qui modélise une équipe, deux services qui exposent des APIs, une base de données partagée, un système métier et un domaine. Vous saurez écrire chaque kind d’entité, gérer les relations entre elles, et retrouver dans le portail toutes les informations utiles à un onboarding ou à un incident. Le tutoriel suppose une instance Backstage v1.50.0 fonctionnelle en local — si ce n’est pas encore le cas, suivre d’abord le tutoriel d’installation.

Étape 1 — Comprendre les huit kinds officiels

La spec descriptor format définit huit kinds d’entités, chacun avec un rôle précis. Connaître la sémantique de chaque kind avant de modéliser évite la confusion qui mène à mettre n’importe quoi en Component. La règle est simple : chaque kind répond à une question différente et on choisit celui qui colle au sens.

Component représente un morceau de logiciel qui se déploie : un service backend, une application frontend, un job batch, un worker. API est une interface exposée par un Component : OpenAPI, GraphQL, AsyncAPI, gRPC. Resource est une infrastructure consommée : base de données, bucket S3, queue Kafka, CDN. System regroupe Components et Resources qui forment une fonction métier cohérente — par exemple « système de paiement » qui inclut l’API de paiement, le worker de réconciliation et la base PostgreSQL des transactions. Domain regroupe des Systems autour d’un même langage métier — typiquement un domaine DDD comme Finance ou Logistique. Group est une équipe ou une business unit. User est une personne. Location est un pointeur vers d’autres descripteurs, qui sert à charger plusieurs entités d’un coup.

Les relations entre kinds sont elles aussi codifiées. Un Component appartient à un Group (champ spec.owner), expose des APIs (spec.providesApis), consomme des APIs (spec.consumesApis), dépend de Resources (spec.dependsOn) et appartient à un System (spec.system). Un System appartient à un Domain (spec.domain). Un User est membre de Groups (spec.memberOf). Ces relations sont la valeur du catalogue : c’est ce qui permet la navigation transversale dans le portail.

Étape 2 — Modéliser une équipe et ses membres

Avant de référencer le moindre service, on commence toujours par les équipes. Sans Group existant, un Component qui pointe sur owner: platform-team apparaîtra avec un avertissement orphan owner. La modélisation d’un Group est simple et tient en quelques lignes de YAML, qu’on place idéalement dans un dépôt infra ou company-config dédié à la configuration d’organisation.

# groups.yaml
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
  name: platform-team
  description: Equipe plateforme et IDP
spec:
  type: team
  profile:
    displayName: Platform Team
    email: platform@example.com
  parent: engineering
  children: []
---
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
  name: engineering
spec:
  type: business-unit
  children:
    - platform-team
    - product-team

Le séparateur --- permet de placer plusieurs entités dans un même fichier. Le champ spec.type est libre mais conventionnel — les valeurs courantes sont team, business-unit, department. Le champ parent et son inverse children construisent l’arbre hiérarchique de l’organisation. Si engineering n’existe pas dans le catalogue au moment du chargement de platform-team, Backstage stocke quand même la relation et la résoudra dès que l’entité parente apparaîtra.

On ajoute ensuite quelques utilisateurs. Le pattern recommandé est de générer ces entités automatiquement depuis l’annuaire de l’entreprise (LDAP, Microsoft Entra ID, Google Workspace) via un processeur dédié, mais en démarrage on peut les écrire à la main pour quelques personnes clés.

apiVersion: backstage.io/v1alpha1
kind: User
metadata:
  name: alice.diop
spec:
  profile:
    displayName: Alice Diop
    email: alice.diop@example.com
  memberOf:
    - platform-team

Le champ memberOf liste les Groups dont l’utilisateur fait partie. Une personne peut appartenir à plusieurs équipes, ce qui est utile pour les guilds transverses. Après chargement, la page User dans Backstage affiche son profil, ses équipes et la liste des entités qu’il possède directement ou via un Group dont il est membre.

Étape 3 — Référencer son premier Component

On entre maintenant dans le vif du sujet. Pour chaque service, on place un fichier catalog-info.yaml à la racine du dépôt — c’est la convention Backstage et le pattern le plus simple à industrialiser. Imaginons un service backend de paiement écrit en Node.js, propriété de la platform-team, qui s’appelle payment-api.

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payment-api
  description: API de paiement et de remboursement
  annotations:
    github.com/project-slug: example-org/payment-api
    backstage.io/techdocs-ref: dir:.
  tags:
    - nodejs
    - fastify
  links:
    - url: https://grafana.example.com/d/payment-api
      title: Dashboard Grafana
      icon: dashboard
spec:
  type: service
  lifecycle: production
  owner: platform-team
  system: payment-system
  providesApis:
    - payment-api-rest

Plusieurs champs méritent attention. Le name doit être unique dans le namespace (par défaut default) et respecter le format kebab-case, soit lowercase alphanumeric avec tirets, longueur 1 à 63. Les annotations sont la zone d’extension : github.com/project-slug permet aux plugins GitHub de retrouver le repo, backstage.io/techdocs-ref indique où sont les sources MkDocs. Le spec.type est libre, mais la spec officielle ne documente comme valeurs well-known que service, website et library — les valeurs worker, job ou frontend sont des conventions courantes adoptées en interne par de nombreuses organisations sans figurer dans la doc officielle. Le spec.lifecycle est aussi libre, conventionnellement experimental, production, deprecated.

Le champ spec.providesApis liste les APIs exposées par référence à leur nom. Ces APIs doivent exister comme entités séparées de kind API — c’est l’objet de l’étape suivante. Si on écrit providesApis: [payment-api-rest] sans avoir créé l’entité payment-api-rest, le catalogue affichera un avertissement de relation cassée jusqu’à correction.

Étape 4 — Déclarer les APIs exposées

Une API est une entité de première classe dans Backstage. La définir séparément du Component qui l’expose présente deux avantages : on peut la consommer depuis plusieurs Components (mêmes API, plusieurs clients) et on peut documenter sa spec OpenAPI ou AsyncAPI directement dans le catalogue.

apiVersion: backstage.io/v1alpha1
kind: API
metadata:
  name: payment-api-rest
  description: API REST de paiement
spec:
  type: openapi
  lifecycle: production
  owner: platform-team
  system: payment-system
  definition: |
    openapi: 3.0.0
    info:
      title: Payment API
      version: 1.0.0
    paths:
      /payments:
        post:
          summary: Cree un paiement
          responses:
            '200':
              description: OK

Le champ spec.type peut prendre les valeurs officielles documentées openapi, asyncapi, graphql et grpc — la valeur est techniquement libre, donc une organisation peut introduire un type custom (par exemple trpc ou websocket), mais seules les quatre valeurs précédentes bénéficient du rendu enrichi natif dans le portail. La definition peut être inline dans le YAML (pratique pour démarrer mais peu maintenable) ou pointer vers un fichier externe via $text ou $json. La pratique recommandée pour une API qui dépasse cinquante lignes est de stocker le fichier openapi.yaml à côté du catalog-info.yaml et d’utiliser definition: $text: ./openapi.yaml.

Une fois cette entité chargée, la page de l’API dans le portail rend la spec OpenAPI avec une interface Swagger UI native. Les développeurs qui doivent consommer l’API trouvent la documentation, les schémas et même un client de test directement dans Backstage. Côté Component qui consomme l’API, on déclare la relation symétrique avec spec.consumesApis: [payment-api-rest].

Étape 5 — Modéliser les Resources d’infrastructure

Une Resource représente une infrastructure dont dépend un service mais qui n’est pas elle-même un déployable. Typiquement : une base de données partagée, un bucket S3, une queue de messages, un cluster ElasticSearch. Modéliser ces ressources dans le catalogue donne deux bénéfices immédiats. D’abord, on voit qui dépend de la base PostgreSQL des transactions et on peut mesurer l’impact d’une opération de maintenance. Ensuite, l’équipe qui possède la ressource est identifiée et reçoit les questions plutôt que de les voir circuler dans Slack.

apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
  name: payments-postgres
  description: Base PostgreSQL des transactions de paiement
  annotations:
    aws.com/rds-instance-arn: arn:aws:rds:eu-west-1:123:db:payments-prod
spec:
  type: database
  owner: platform-team
  system: payment-system

Les valeurs courantes pour spec.type sont database, s3-bucket, kafka-topic, redis-cluster. Le champ annotations stocke les références techniques — l’ARN d’une instance RDS, l’identifiant d’un cluster GKE, le nom d’un projet GCP. Ces annotations sont consommées par les plugins d’observabilité pour retrouver les métriques de la ressource. Côté Component qui dépend de la base, on déclare la relation avec spec.dependsOn: [resource:payments-postgres]. Le préfixe resource: est important — sans lui, Backstage cherche un Component du même nom et échoue.

Étape 6 — Regrouper en System et Domain

System et Domain sont les deux niveaux d’abstraction supérieurs qui transforment une liste plate d’entités en une carte navigable. Sans Systems, on a deux mille services qui se ressemblent. Avec Systems, on a cinquante regroupements logiques qu’un humain peut tenir en tête. La règle pratique est qu’un System contient en général entre trois et quinze entités — moins, c’est superficiel ; plus, c’est qu’il faut le scinder.

apiVersion: backstage.io/v1alpha1
kind: System
metadata:
  name: payment-system
  description: Tout ce qui concerne les paiements et remboursements
spec:
  owner: platform-team
  domain: finance
---
apiVersion: backstage.io/v1alpha1
kind: Domain
metadata:
  name: finance
  description: Domaine metier finance et comptabilite
spec:
  owner: cfo-org

Le System payment-system est référencé par les trois entités précédentes via leur champ spec.system. Le Domain finance est référencé par le System via spec.domain. Le portail affiche maintenant une page System qui liste les Components, APIs et Resources du système, et une page Domain qui liste les Systems du domaine. Cette navigation hiérarchique est ce qui rend le catalogue utile à un nouvel arrivant.

Étape 7 — Activer l’auto-discovery sur GitHub

Charger les entités à la main devient vite intenable. Le mécanisme officiel pour échelonner est l’auto-discovery GitHub, qui parcourt automatiquement les dépôts d’une organisation et charge les fichiers catalog-info.yaml qu’il y trouve. La configuration tient en quelques lignes dans app-config.yaml et un token GitHub avec les bons scopes.

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

catalog:
  providers:
    github:
      providerId:
        organization: 'example-org'
        catalogPath: '/catalog-info.yaml'
        filters:
          branch: 'main'
          repository: '.*'
        schedule:
          frequency: { minutes: 30 }
          timeout: { minutes: 3 }

Le token GitHub doit avoir les scopes repo (pour lire les fichiers dans les dépôts privés) et read:org (pour lister les dépôts de l’organisation). Sur une organisation de moins de cent dépôts, un Personal Access Token suffit. Au-delà, on bascule sur une GitHub App avec authentification par installation, qui scale mieux et offre un contrôle plus fin des permissions.

Au backend, il faut activer le provider dans packages/backend/src/index.ts en ajoutant backend.add(import('@backstage/plugin-catalog-backend-module-github')). Au prochain redémarrage de yarn dev, Backstage commence à scanner l’organisation. La première passe est longue — comptez plusieurs minutes pour cent dépôts. Les passes suivantes prennent en charge incrémentalement les nouveautés.

L’effet est immédiat : tous les dépôts qui possèdent un fichier catalog-info.yaml apparaissent dans le catalogue, et tout nouveau dépôt créé avec ce fichier est découvert automatiquement à la prochaine itération du scheduler. Cette automatisation est ce qui permet au catalogue de rester en phase avec la réalité du code sans intervention humaine.

Étape 8 — Vérifier les relations dans le portail

Maintenant que le catalogue est peuplé, on valide visuellement que tout se tient. Dans Backstage, ouvrir la page de payment-api. La barre latérale doit montrer Owner platform-team, System payment-system, l’onglet Dependencies doit lister la base payments-postgres, l’onglet API doit afficher payment-api-rest. Cliquer sur l’API ouvre la spec Swagger, cliquer sur le System ouvre une vue d’ensemble du système.

curl -s "http://localhost:7007/api/catalog/entities?filter=kind=component,spec.system=payment-system"

L’API REST de Backstage permet aussi des requêtes ciblées. La commande ci-dessus retourne tous les Components du système payment-system. C’est l’API qu’on utilise pour construire des dashboards externes ou des intégrations CI qui vérifient qu’un dépôt à toujours un catalog-info.yaml à jour. Si la requête retourne un tableau vide alors qu’on s’attend à des résultats, c’est généralement que le filtre est mal écrit — la syntaxe est kind=component,spec.system=payment-system sans espaces.

Erreurs fréquentes

Erreur Cause Solution
Orphan owner: platform-team Group référencé avant d’être déclaré Charger les Groups avant les Components, ou laisser converger
Invalid entity name (uppercase) Nom avec majuscules Utiliser kebab-case lowercase uniquement
EntityRefValidationError on dependsOn Préfixe kind manquant Écrire resource:payments-postgres et pas payments-postgres
API definition unparseable OpenAPI inline mal indenté Externaliser dans un fichier et utiliser $text
GitHub discovery 0 résultats Token sans scope repo ou mauvais nom d’org Vérifier les scopes du PAT et le slug de l’organisation
System sans Components affichés spec.system absent côté Components Ajouter spec.system: payment-system dans chaque Component
Documentation TechDocs introuvable Annotation manquante Ajouter backstage.io/techdocs-ref: dir:.

Tutoriels associés

Pour aller plus loin

Retour au guide principal : Plateforme de développeur interne avec Backstage : architecture pour startup.

FAQ

Faut-il un fichier catalog-info.yaml par service ou un fichier centralisé ?
Un fichier par service, à la racine du dépôt du service. Cette convention garde la vérité au plus près du code et permet à chaque équipe de mettre à jour son entité sans intervention de la plateforme. Les fichiers centralisés sont réservés aux Groups, Users et entités d’organisation qui n’ont pas de dépôt naturel.

Comment référencer une entité dans un namespace différent ?
La syntaxe complète est kind:namespace/name, par exemple component:finance/payment-api. Si le namespace n’est pas précisé, Backstage utilise default. Pour les organisations très grandes avec des silos métier, créer plusieurs namespaces aide à éviter les collisions de noms — pour les startups, rester sur le namespace par défaut suffit largement.

Que se passe-t-il si je supprime un fichier catalog-info.yaml ?
Le processeur de discovery détecte la disparition à la prochaine itération et marque l’entité comme orpheline. Backstage la conserve quelques heures par défaut puis la supprime. Pour forcer la suppression immédiate, utiliser l’API DELETE /api/catalog/entities/by-uid/<uid>.

Peut-on enrichir le catalogue avec des données calculées (par exemple coût AWS) ?
Oui, via les processors ou entity providers custom. Un processor s’exécute après le chargement et peut ajouter des annotations à une entité. Un provider peut générer des entités complètes depuis une source externe. C’est le mécanisme par lequel les plugins Cost Insights, Kubernetes ou ArgoCD enrichissent les fiches.

Comment valider un catalog-info.yaml avant de le commiter ?
L’utilitaire officiel backstage-cli validate existe mais reste limité. La pratique pragmatique est un job CI qui pousse le fichier sur une instance Backstage de staging et vérifie qu’il est accepté sans erreur. Pour les organisations matures, un linter custom basé sur le JSON Schema de la spec donne une validation locale immédiate.

Une entité peut-elle appartenir à plusieurs Systems ?
Non, le champ spec.system est singulier. Une entité ne peut appartenir qu’à un seul System à la fois. Si le besoin est légitime — par exemple une bibliothèque utilisée par plusieurs systèmes — on modélise plutôt la bibliothèque comme un Component indépendant qui n’est dans aucun System, et chaque System qui en dépend la déclare via spec.dependsOn.

Comment gérer les services internes versus externes (third-party) ?
Les services tiers se modélisent comme des APIs avec un type adapté (openapi ou external) et un Component placeholder. Cette modélisation est utile car les Components internes peuvent ainsi déclarer consumesApis: [stripe-api] et le catalogue trace correctement la dépendance externe.

Sponsoriser ce contenu

Cet emplacement est à vous

Position premium en fin d'article — c'est l'instant où les lecteurs sont le plus engagés. Réservez cet espace pour votre marque, votre formation ou votre offre.

Recevoir nos tarifs
Publicité