Aller au contenu

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 :

  1. scripts/bootstrap-climascore-catalog.sh (one-shot, hors chart) :

    • crée un rôle PG trino_readonly avec GRANT 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 .properties du catalog Trino avec readOnly=true
  2. helm install akko-tenant-climascore helm/akko/charts/akko-tenant -f helm/examples/values-tenant-climascore.yaml installe 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=climascore dans le token
    • OPA data scope restreint au catalog climascore-pg uniquement
    • quotas 2 CPU / 4 Gio RAM / 50 Gio stockage / 20 pods

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 akko-tenant-<id> -n akko

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.