Avec l'arrivée des micro-services, du DevOps et la multiplication des projets dans les départements IT, il devient de plus en plus compliqué de recenser et retrouver les différents projets d'une équipe ou d'un département dans son ensemble.
Pour permettre une meilleure visibilité de ces composants, des outils, qu'on appelle « Portail Développeur », commencent à voir le jour.
Qu'est-ce qu'un Portail Développeur ?
Un Portail Développeur (ou « Developer Portal » en anglais) est en général un site web qui répertorie les différents composants des projets internes d'une société ou d'un département. Il recense les documentations ainsi que les API et les relie tous entre eux. Souvent, l'équipe responsable de la CI/CD propose également des modèles de « Scaffolding » (modèle de projet + ou - complexe qui permet de démarrer rapidement avec les bonnes pratiques) pour créer de nouveaux projets/composants directement sur le portail.
La navigation, la découverte et la création de services sont donc simplifiées dans un outil unique, pivot central pour le développeur, l'architecte ou même le directeur SI. Le « onboarding », en particulier, et l'expérience développeur, en général, s'en voient améliorés.
Le plus connu de ces outils est certainement « Backstage » de Spotify que je vais vous présenter ici.
Let's go !
Présentation de Backstage
Backstage est un outil qui permet de créer ce fameux Portail Développeur. Ce n'est pas un produit « clé-en-main » et il demande quelques compétences techniques, notamment sur NodeJS pour être déployé et en Typescript/React pour être totalement personnalisé.
D'où vient-il ?
Backstage est la solution développée en R&D chez Spotify dès 2016 alors que la société est en pleine croissance, avec de nouveaux ingénieurs et de nouveaux composants créés. Le projet est rapidement devenu un besoin critique pour remédier au chaos ambiant.
En 2020, Spotify ouvre le code de Backstage puis, plus tard dans l'année, l'offre à la CNCF. Le projet entre alors dans une phase d'expansion.
De grandes sociétés fournissent également des services d'hébergement ou de conseil (comme Roadie ou ThoughtWorks) ou des plugins pour intégrer différentes technologies (comme Zalando, RedHat, etc.).
Que peut-on faire avec ?
Collecter les composants logiciels
Par défaut, après avoir créé une instance de Backstage en local avec npm
, il permet de collecter manuellement les informations de composants ou projets dans un « catalogue » centralisé et de les afficher dans un portail web simple et élégant.
Pour activer la collecte automatique, il suffit de modifier la configuration dans le fichier app-config.yaml
. Dans la liste des providers
, il faut ajouter le nom du fournisseur du DVCS avec les informations nécessaires à la collecte comme le chemin et le nom du fichier qui contient les métadonnées, des filtres optionnels pour ne pas scanner des dépôts spécifiques et un ordonnancement.
Exemple de configuration pour GitHub :
[...]
catalog:
providers:
github:
providerId:
organization: IpponTechnologies
catalogPath: '/**/catalog-info.yaml'
filters:
repository: '.*'
schedule:
frequency: { hours: 4 }
timeout: { minutes: 2 }
[...]
Tous les composants logiciels peuvent être collectés : applications, bibliothèques, services, pipelines, etc. Il est nécessaire d'ajouter un fichier de métadonnées au format YAML, en général nommé catalog-info.yaml
, dans le dépôt de code source et dans un répertoire qui correspond à la configuration définie précédemment. Dans l'exemple ci-dessus, Backstage va parser les fichiers toutes les 4 heures.
Exemple de fichier de composants :
---
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: my-web-api
title: My Web API
description: My Web API which provide some useful Rest endpoints
annotations:
backstage.io/techdocs-ref: dir:.
spec:
type: backend-service
lifecycle: production
owner: my_dev_squad
providesApis: [my-web-api-openapi]
---
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
name: my-web-api-openapi
description: The provided REST API for My Web API
spec:
type: openapi
lifecycle: production
owner: my_dev_squad
definition:
$yaml: ./openapi.yml # embed a local file
---
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
name: my-web-api-db
description: A database for My Web API
spec:
type: database
owner: my_dev_squad
dependsOn: [resource:mongo-instance] # reference an external resource
Ce fichier exemple contient 3 « documents » (séparés par ---
) décrivant chacun une partie du composant : l'exécutable (composant principal), son API et sa base de données (ressource).
Chaque document est constitué de 3 parties :
- La première partie est l'entête (
apiVersion
etkind
) du document et permet de définir ce que contient le document. - La seconde partie contient les métadonnées (
metadata
) qui sont affichées sur le portail (nom, description, etc.). - La dernière partie (
spec
) fournit les spécificités du composant ; chaque composant pouvant avoir des attributs complètement différents.
Créer un nouveau composant
Avant de pouvoir créer un composant depuis le portail, il faut ajouter un modèle. Celui-ci est constitué d'un fichier template.yaml
(à l'image du fichier précédent) et d'un ensemble de fichiers spécifiques à l'environnement du projet (langage, infra, etc.) dans lesquels des variables pourront être substituées.
Exemple de fichier de modèle :
---
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: my-template
title: My Custom Template
description: Description of the template
tags: [recommended, other-tag]
spec:
type: template
owner: group:devex_squad
parameters:
- title: Default information
required: [name]
properties:
name:
title: Name
type: string
description: Unique name of the component
pattern: "^([a-zA-Z][a-zA-Z0-9]*)(-[a-zA-Z0-9]+)*$"
ui:autofocus: true
ui:options:
rows: 5
owner:
title: Owner
type: string
description: Owner of the component
ui:field: OwnerPicker
ui:options:
catalogFilter:
kind: [User, Group]
- title: Choose a location
required:
- repoUrl
properties:
repoUrl:
title: Repository Location
type: string
ui:field: RepoUrlPicker
ui:options:
allowedHosts: ["github.com"]
allowedOwners: ["IpponTechnologies"]
steps:
- id: fetch-base
name: Fetch Base
action: fetch:template
input:
url: ./skeleton
destinationPath: .
copyWithoutTemplating:
- .github/workflows/*
values:
name: ${{ parameters.name }}
owner: ${{ parameters.owner }}
- id: publish
name: Publish
action: publish:github
input:
allowedHosts: ["github.com"]
allowedOwners: [IpponTechnologies]
allowMergeCommit: false
description: This is ${{ parameters.name }}
repoUrl: ${{ parameters.repoUrl }}
repoVisibility: "public"
requireCodeOwnerReviews: true
defaultBranch: main
protectDefaultBranch: true
gitAuthorName: ${{ user.entity.metadata.name }}
gitAuthorEmail: ${{ user.entity.spec.profile.email }}
- id: register
name: Register
action: catalog:register
input:
repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
catalogInfoPath: "/catalog-info.yaml"
output:
links:
- title: Open GitHub Repository
url: ${{ steps['publish'].output.remoteUrl }}
- title: Open in catalog
icon: catalog
entityRef: ${{ steps['register'].output.entityRef }}
Ce fichier exemple contient les 3 parties détaillées plus haut. La dernière partie est elle-même divisée en 3 parties :
- La première partie contient des groupes de paramètres, chacun représentant une étape dans l'assistant visuel du portail et contenant les paramètres. Chaque paramètre fournit un titre, une description, un type de données (
string
,number
, etc.) des validations optionnelles pour les valeurs saisies par l'utilisateur, et des options d'affichage. - La seconde partie contient les différentes étapes exécutées après l'assistant pour générer la structure de base du composant. Chaque étape est responsable d'une partie de la génération :
- Première étape : récupérer les fichiers « squelette » du modèle en remplaçant les valeurs des paramètres par ceux fournis par l'utilisateur dans l'assistant,
- Seconde étape : publier le code dans un dépôt sur GitHub
- Dernière étape : enregistrer le composant dans le portail
- La dernière partie permet d'afficher un résumé de la génération avec des liens permettant de naviguer vers le dépôt nouvellement créé ou vers le composant enregistré dans le portail.
Dès lors, les développeurs peuvent créer un nouveau composant directement depuis Backstage en fournissant les informations demandées. Ils peuvent également créer leurs propres modèles si les modèles proposés ne correspondent pas à leurs attentes ou besoins.
Afficher la documentation
Backstage utilise MkDocs pour générer et afficher la documentation d'un composant depuis le code source. Il est donc assez facile de fournir une documentation au sein du portail.
La documentation peut être enregistrée dans le portail via l'utilisation de l'annotation backstage.io/techdocs-ref
dans le fichier catalog-info.yaml
et en la fournissant au format MkDocs.
Par défaut, la documentation est convertie en HTML statique par Backstage lors du premier accès ou si la documentation a changé dans le dépôt de code source.
Cette approche n'étant pas recommandée, il est préférable de générer la documentation lors de la CI/CD, en déposant les fichiers HTML statiques dans un espace de stockage externe (type S3 ou GCS).
Il faudra pour cela modifier la configuration techdocs.builder
dans le fichier app-config.yaml
en la changeant de local à external. Après ce changement, la documentation ne sera plus générée par Backstage.
Comment peut-on l'étendre et le personnaliser ?
Avec un peu de Typescript + React + CSS, il est possible de personnaliser l'interface simplement. On peut également créer des plugins personnalisés et utiliser les plugins existants. Il en existe quelques-uns sur la « marketplace » de Backstage.
Quelques exemples :
- Navigateurs d'APIs : AsyncAPI, OpenAPI, GraphQL, gRPC, etc.
- Intégration de linters : Spectral et Zalando
- Intégration des principaux providers Cloud : AWS, Azure, GCP
- Intégration des DVCS : Gerrit, GitHub, GitLab, etc.
- Intégration des outils d'analyse de code : SonarQube, SonarCloud, etc.
- Intégration des outils d'analyse de sécurité : BetterScan, Snyk, etc.
- Intégration des outils de CI/CD : ArgoCD, AzureDevOps, CircleCI, GitHub Actions, GitLab, Travis CU, etc.
- Intégration des outils de monitoring : DataDog, DynaTrace, Grafana, OpenDORA, OpenCost
Pour chaque plugin, il faudra tout de même mettre un peu « la main à la pâte » : ce n'est pas automatisé et quelques modifications du code seront nécessaires. Heureusement, les explications de chacun des modules sont relativement simples à mettre en place et bien expliquées. Je n'ai eu aucun souci pour intégrer qui m'intéressait !
Actuellement, je suis en train de créer un plugin pour exécuter n'importe quelle commande shell lors de l'exécution des modèles de scaffolding. Après avoir exécuté l'outil de Backstage en CLI qui prépare la structure des fichiers, l'ajout des fonctionnalités est relativement simple pour un développeur même débutant. Je vous en parlerai plus longuement dans un prochain article.
Comment le déployer ?
Le déploiement de l'outil est relativement simple et conforme à ce qu'il se fait ailleurs.
Il faudra quand même rajouter une base de données, par exemple PostgreSQL plutôt que d'utiliser SQLite qui est activé par défaut, afin d'assurer une reprise de service rapide après une mise à jour ou autre. Mais ce n'est pas plus compliqué que le reste : il faut remplacer la configuration backend.database
dans le fichier app-config.yaml
; comme ceci :
[...]
backend:
database:
client: pg
connection:
host: ${POSTGRES_HOST}
port: ${POSTGRES_PORT}
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
[...]
L'utilisation de variables d'environnement plutôt que d'avoir des valeurs en dur est recommandée. Il faudra simplement les passer lors de l'exécution du serveur Node.
Il est également possible d'intégrer des services d'authentification comme Azure Entra, GitHub, GitLab ou LDAP. Un service d'authentification permettra aux utilisateurs de s'identifier après que Backstage ait collecter les informations sur les groupes et les utilisateurs. Chaque fournisseur demandant une configuration particulière, je détaillerais ceci dans un article ultérieur.
Conclusion
Backstage n'est pas un outil « sur étagère » mais déployer une application sur un socle open-source permet de gagner du temps au démarrage du projet. L'extensibilité est également une des forces de l'outil, en intégrant des plugins existants rapidement et en en développant d'autres au besoin. Il faudra donc le gérer comme une application interne, avec des tests et un cycle de développement à définir. Le déploiement et/ou la maintenance de l'outil devrait être assuré par l'équipe chargée de la CI/CD ou de la "Developer Experience".