Multi-tenancy¶
AKKO permet de faire tourner plusieurs tenants isolés sur un même cluster. Un tenant peut être un client, un département, un projet interne, ou un pilote tiers (climascore est la simulation de référence — voir plus bas).
Chaque tenant reçoit :
| Couche | Primitive d'isolation |
|---|---|
| Compute | Namespace Kubernetes dédié akko-<id>, PSS restricted imposé, ResourceQuota (CPU/RAM/stockage/pods) |
| Données SQL | Base PostgreSQL + utilisateur dédiés dans akko-postgresql-data (ou instance PG standalone si tenant.database.dedicatedInstance=true) |
| Stockage objet | Bucket object storage dédié <id>-warehouse + policy IAM tenant-<id> + buckets additionnels en lecture seule optionnels |
| Lakehouse | Namespace Iceberg dédié <id>.* sur Polaris |
| Identité | Soit un realm Keycloak complet (tenant.keycloak.mode=realm), soit un client-scope dédié injectant un attribut tenant dans les tokens (tenant.keycloak.mode=client-scope) |
| Autorisation | Fragment de ConfigMap OPA labellisé akko.io/opa-tenant=<id> — chargé à chaud par le sidecar watcher OPA, sans rolling restart |
| Audit | Événement JSON émis à chaque install/upgrade, collecté par logs layer / logs layer |
Provisionner un tenant¶
Les tenants sont provisionnés via le sub-chart akko-tenant
(helm/akko/charts/akko-tenant). Une release Helm par tenant :
helm install akko-tenant-<id> helm/akko/charts/akko-tenant \
--namespace akko \
-f helm/examples/values-tenant-<id>.yaml
Toutes les étapes de bootstrap (base PostgreSQL, bucket object storage, client-scope
Keycloak, fragment de policy OPA, événement d'audit) tournent en Helm hooks
post-install idempotents. Relancer la commande est sûr — les ressources
existantes sont détectées et réutilisées.
Exemple type : climascore (simulation premier client)¶
Climascore est un projet tiers qui possède déjà sa base PostgreSQL. Nous voulons que les utilisateurs AKKO puissent fédérer les données climascore via Trino en lecture seule, sans jamais écrire sur la source. Le provisioning se fait en deux temps :
-
scripts/bootstrap-climascore-catalog.sh(one-shot, hors chart) :- crée un rôle PG
trino_readonlyavecGRANT SELECT - vérifie qu'une tentative d'écriture est refusée
- calcule un checksum MD5 de la base source avant et après, et annule si les données ont changé (défense en profondeur)
- stocke les credentials dans le Secret K8s
climascore-pg - génère le
.propertiesdu catalog Trino avecreadOnly=true
- crée un rôle PG
-
helm install akko-tenant-climascore helm/akko/charts/akko-tenant -f helm/examples/values-tenant-climascore.yamlinstalle la couche d'isolation K8s :- namespace
akko-climascore database.enabled: false(climascore possède sa propre PG)- bucket object storage
climascore-warehouse+ accès lecture seule àakko-shared - Keycloak client-scope : attribut
tenant=climascoredans le token - OPA data scope restreint au catalog
climascore-pguniquement - quotas 2 CPU / 4 Gio RAM / 50 Gio stockage / 20 pods
- namespace
Ajouter un utilisateur à un tenant¶
Mode client-scope (défaut) :
# Depuis l'UI admin Keycloak ou via l'API REST admin :
# positionner l'attribut `tenant = <id>` sur le ou les utilisateurs
# puis attacher le scope optionnel `tenant-<id>` au client cockpit.
Le cockpit, Trino, Superset, OpenMetadata et MLflow voient tous une claim
tenant dans le JWT ; OPA et Catalog Manager Pro appliquent que
l'utilisateur ne voit que les ressources taguées avec ce tenant.
Mode realm : les utilisateurs du tenant vivent dans un realm dédié. Soit on
expose ce realm comme identity provider dans le realm principal akko,
soit on impose la connexion sur le realm dédié.
Interaction avec le RBAC¶
Les rôles Keycloak existants (akko-admin, akko-engineer, akko-analyst,
akko-steward, akko-viewer) restent valables à l'intérieur de chaque
tenant. L'attribut tenant est orthogonal au rôle : un akko-analyst
dans le tenant A voit un accès analyste sur les données de A, et rien sur
les données de B.
L'administration inter-tenants requiert explicitement le rôle akko-admin
sans attribut tenant (administrateur plateforme).
Supprimer un tenant¶
helm uninstall supprime le namespace (avec quotas et fragment OPA) mais
laisse la base PostgreSQL et le bucket object storage en place par défaut, pour
éviter toute perte de données. Pour récupérer ces ressources :
# Sur akko-postgresql-data :
DROP DATABASE "<id>_db";
DROP ROLE "<id>_user";
# Sur mc :
mc rb --force akko/<id>-warehouse
mc admin policy rm akko tenant-<id>
Événements d'audit¶
Chaque install/upgrade émet une ligne JSON sur stdout, collectée par log shipper et interrogeable dans Dashboards/logs layer :
{
"audit_type": "TENANT_LIFECYCLE",
"decision": "PROVISIONED",
"tenant": "climascore",
"display_name": "Climascore (first client simulation)",
"owner_email": "owner@climascore.local",
"namespace": "akko-climascore",
"release": "akko-tenant-climascore",
"revision": "1",
"hook": "install",
"timestamp": "2026-04-19T14:37:02Z"
}
Tester l'isolation¶
# 1. Provisionner un tenant pilote
helm install akko-tenant-pilot helm/akko/charts/akko-tenant \
-n akko -f my-pilot-values.yaml
# 2. Vérifier l'isolation inter-tenants
kubectl -n akko-climascore auth can-i get pods --as=system:serviceaccount:akko-pilot:default
# attendu : no
Les tests d'intégration complets sont dans
tests/integration/test_multi_tenant_isolation.py.