Installation
graal s’installe chez vous, par un chart Helm, dans deux namespaces d’un cluster Kubernetes que vous administrez. Cette page dit ce qu’il faut avoir avant de commencer, ce que la plateforme attend de votre équipe, et quels objets l’installation crée — ou ne crée pas — dans votre cluster.
Ce qu’il faut
Section intitulée « Ce qu’il faut »| Élément | Précisions |
|---|---|
| Kubernetes | Une distribution qui fournit un contrôleur d’entrée, la gestion des certificats, la synchronisation des secrets et un opérateur PostgreSQL (voir plus bas). Deux namespaces, fournis par votre équipe plateforme. Les pods respectent le profil PSA restricted |
| PostgreSQL | Une base applicative dédiée. Le déploiement de référence utilise un opérateur qui génère lui-même les mots de passe des rôles |
| Keycloak 26 | Un realm par tenant, plus un realm d’administration. Fédération de votre annuaire possible |
| Un stockage objet S3 | MinIO, Ceph RGW ou équivalent : buckets de données et tables Apache Iceberg |
| Un relais SMTP | Invitations, notifications, réinitialisation de mot de passe. Sans lui, ces fonctions se taisent — la plateforme démarre quand même |
| Un contrôleur d’entrée et un certificat | Un seul nom d’hôte suffit pour la console et l’API, qui le partagent |
| Un registre d’images | Celui que vous contrôlez : images de la plateforme et images de runtime (Python, Spark, Jupyter, VS Code) |
Le moteur SQL (Trino) et la façade de suivi d’expériences (compatible avec le client MLflow, dont elle implémente un sous-ensemble) sont déployés par la plateforme, pas à fournir.
Une contrainte à connaître sur le registre : le dépôt des images de runtime doit être accessible
en tirage anonyme, parce que le namespace d’exécution ne porte aucun Secret — donc aucun secret
de tirage. Les images de la plateforme, elles, peuvent venir d’un dépôt privé. Chaque image de runtime
est une valeur du chart : changer de registre, c’est changer ces valeurs, sans toucher au code.
Les deux namespaces
Section intitulée « Les deux namespaces »L’installation occupe deux namespaces, et la séparation est structurante :
| Namespace | Ce qui y vit | Pourquoi |
|---|---|---|
| namespace applicatif | console, API, moteur low-code, services de plateforme | c’est la zone de confiance de la plateforme |
| namespace d’exécution | les jobs et les espaces de travail des utilisateurs | un espace de travail exécute du code d’utilisateur : rien de confidentiel n’y est monté, et il n’y a aucun secret matérialisé |
Dans le déploiement de référence ils s’appellent graal et graal-run. C’est cette séparation qui
permet de dire qu’un utilisateur qui exécute du code arbitraire ne lit pas les identifiants de la
plateforme : ils ne sont pas dans son namespace.
Les images de runtime sont tirées anonymement depuis un dépôt public de votre registre, sans secret de tirage, pour la même raison. Corollaire : rien de confidentiel, et jamais l’image d’un client, dans ce dépôt-là.
Ce que l’installation ne crée pas
Section intitulée « Ce que l’installation ne crée pas »C’est volontaire et c’est vérifié au rendu du chart : l’installation ne crée ni ne demande le droit de créer :
- aucun namespace — un namespace est une frontière réseau et un plafond de ressources : il ne s’auto-déclare pas ;
- aucun
Secret; - aucun objet cluster-scoped : pas de
ClusterRole, pas deClusterRoleBinding, pas deStorageClass, pas deClusterIssuer; - aucune CRD installée par graal — le déploiement de référence consomme en revanche les ressources personnalisées de quatre familles fournies par votre plateforme : entrée, certificats, secrets et base de données ;
- aucun
Role,RoleBinding,NetworkPolicy,ResourceQuotaniLimitRange: vos garde-fous restent les vôtres, et un locataire qui écrit son propre RBAC n’a pas de plafond visible.
Ce que votre équipe plateforme fournit donc une fois, avant l’installation : les deux
namespaces avec leur quota et leur politique réseau, un ServiceAccount d’exécution dans le
namespace d’exécution, et une Role namespacée autorisant le planificateur à y créer des pods.
Le planificateur ne crée plus ni ServiceAccount, ni RoleBinding, ni secret de tirage par job.
Conséquence pratique : graal ne peut pas s’installer avec un compte cluster-admin par accident, et un audit de vos habilitations n’a pas à faire confiance à notre documentation — le rendu du chart se relit.
Les variables qui comptent
Section intitulée « Les variables qui comptent »Le chart en expose beaucoup ; six familles décident si l’installation fonctionne.
GRAAL_SECRETS_SALT_KEY — obligatoire, à générer une seule fois
Section intitulée « GRAAL_SECRETS_SALT_KEY — obligatoire, à générer une seule fois »C’est la clé racine du coffre des locataires : les secrets d’un projet (vos identifiants vers vos systèmes) sont chiffrés au repos en base, en AES-256-GCM, avec une clé dérivée de ce sel par HKDF-SHA256 — une clé distincte par locataire, de sorte qu’une valeur copiée d’un locataire à un autre ne s’ouvre pas.
- Obligatoire : absente, trop courte ou vide, l’API refuse de démarrer en nommant la variable. C’est le seul réglage dont l’absence arrête le démarrage, et c’est délibéré — un repli silencieux sur une clé par défaut serait pire qu’un refus.
- Définitive : la changer rend illisible tout ce qui a déjà été scellé. La valeur posée à la première installation est celle de toute la vie de l’installation.
- À générer une seule fois, avant la première installation, par exemple
openssl rand -hex 16(au moins 32 caractères), puis à sauvegarder comme vous sauvegardez une clé de chiffrement de base de données. La perdre équivaut à perdre les secrets des projets.
Stockage objet
Section intitulée « Stockage objet »| Variable | Rôle |
|---|---|
GRAAL_S3_ENDPOINT_INTERNAL |
Requis pour créer un locataire : l’adresse par laquelle l’API et les pods joignent le stockage. Absente, la création d’un locataire échoue en TENANT_CONFIGURATION_ERROR |
GRAAL_S3_ENDPOINT |
L’URL publique du stockage, celle qui est donnée aux utilisateurs. À défaut, l’adresse interne est réutilisée |
GRAAL_S3_TYPE, GRAAL_S3_REGION, GRAAL_S3_PATH_STYLE_ACCESS, GRAAL_S3_SSL |
Les défauts posés sur chaque locataire |
Les deux adresses sont distinctes parce qu’elles le sont presque toujours en vrai : un pod joint le stockage par un nom de service interne, un utilisateur par un nom public.
GRAAL_API_INTERNAL_URL
Section intitulée « GRAAL_API_INTERNAL_URL »L’origine par laquelle un pod du namespace d’exécution rejoint l’API. Elle n’a pas de valeur utile par défaut dans un cluster réel, et son absence ne se voit pas à l’écran : les runs échouent, sans message explicite côté interface. À poser explicitement, et à vérifier par un premier run réel plutôt que par la lecture des sondes de santé.
Keycloak
Section intitulée « Keycloak »| Variable | Rôle |
|---|---|
GRAAL_KEYCLOAK_URL |
L’origine publique de Keycloak. L’émetteur attendu des jetons est <origine>/realms/<realm> |
GRAAL_KEYCLOAK_AUTH_ENDPOINT_INTERNAL |
La même, vue depuis un pod |
GRAAL_KEYCLOAK_JWKS_ENDPOINT |
Où télécharger les clés publiques. Ne change pas l’émetteur accepté |
GRAAL_KEYCLOAK_ISSUERS |
La liste des origines acceptées comme émetteur |
GRAAL_KEYCLOAK_ADMIN_REALM, GRAAL_KEYCLOAK_ADMIN_CLIENT_ID, GRAAL_KEYCLOAK_ADMIN_USERNAME, GRAAL_KEYCLOAK_ADMIN_PASSWORD |
Le compte qui provisionne les realms des locataires |
GRAAL_KEYCLOAK_UI_CLIENT_ID, GRAAL_KEYCLOAK_UI_REDIRECT_URIS |
Le client public de la console et ses URI de redirection autorisées |
GRAAL_KEYCLOAK_AGENT_CLIENT_ID |
L’« Application graal » d’un locataire : l’identité des agents MCP |
Le point qui coûte le plus de temps : l’origine publique fige l’émetteur (iss) des jetons. Un
jeton pris depuis l’extérieur est rejeté à l’intérieur — et inversement — si les deux vues ne
désignent pas le même nom d’hôte. C’est pour cela que l’origine publique et l’origine interne sont
deux réglages séparés, et que seule la première décide de ce qui est accepté.
Base de données
Section intitulée « Base de données »L’hôte, le port et la base ne sont pas trois réglages indépendants mais une seule URL. Le mot de passe, dans le déploiement de référence, vient de l’opérateur PostgreSQL et jamais d’un coffre : deux sources de vérité pour le même secret, dont une ignorée en silence, c’est une rotation qui casse l’application sans que rien ne la relie à sa cause.
Attention au mode TLS : certaines distributions PostgreSQL managées refusent toute connexion en clair. Là où la base est gérée, exiger TLS explicitement.
Courrier
Section intitulée « Courrier »GRAAL_SMTP_HOST, GRAAL_SMTP_PORT, GRAAL_SMTP_USERNAME, GRAAL_SMTP_PASSWORD,
GRAAL_SMTP_AUTH, GRAAL_SMTP_STARTTLS.
Le courrier est optionnel : tant qu’il n’est pas configuré, la plateforme fonctionne et l’indicateur de santé du courrier reste éteint plutôt que de déclarer la plateforme malade. Une fonction non configurée ne doit pas rendre l’ensemble rouge.
Vérifier que l’installation tient
Section intitulée « Vérifier que l’installation tient »Dans cet ordre, parce que chaque étape suppose la précédente :
- L’API démarre. Si elle refuse, elle nomme la variable qui manque — lire le journal du pod avant de chercher ailleurs.
- Un locataire se crée. C’est ce qui met à l’épreuve le stockage objet et le provisionnement Keycloak.
- Un utilisateur se connecte à la console par Keycloak. C’est ce qui met à l’épreuve l’origine publique et les URI de redirection.
- Un job s’exécute vraiment, et son journal revient. C’est le seul contrôle qui met à l’épreuve
le namespace d’exécution, le
ServiceAccountfourni, le tirage d’images etGRAAL_API_INTERNAL_URL. Une plateforme dont les sondes sont vertes peut très bien ne jamais exécuter un run : ne pas s’arrêter avant cette étape.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Souveraineté et architecture — ce qui reste chez vous, et pourquoi
- Sécurité et gouvernance
- Architecture — composants et réseau
- Agents IA (MCP)