Stockage objet¶
AKKO embarque une couche de stockage objet S3-compatible comme socle pour le lac de données Iceberg, les event logs Moteur de calcul, les artefacts MLflow, les uploads de PDF d'akko-rag, et tout autre besoin de stockage de blobs.
La surface publique est uniquement l'API S3, donc l'engine sous-jacent
peut être remplacé (ou pointé vers AWS S3, Wasabi, OVH cloud-object-
storage, etc.) sans changement de code dans la plateforme. Le
déploiement vit dans le sub-chart akko-storage et tourne dans le
même namespace akko que le reste de la plateforme.
Architecture¶
Polaris ──────┐
│ API S3 (signature v4)
Trino ────────┼──→ akko-storage:<port-s3> ──→ /data PVC
│
Spark ────────┤
│
MLflow ───────┘
akko-rag ─────→ /uploads/* via boto3
Dashboards ADEN ─→ /reports/*
Tous les moteurs de calcul (Moteur de requête (Trino), Moteur de calcul, akko-rag, MLflow) accèdent
à la couche de stockage via l'API S3 standard ; ils ne savent pas
quel est l'engine derrière. Pour swap vers un autre fournisseur
S3-compatible, changer l'URL endpoint + les credentials dans
values.yaml — aucun changement côté consommateur.
Ports¶
| Port | Rôle | Exposé |
|---|---|---|
| 8333 | API S3 (REST, signature v4) | ClusterIP |
| 8888 | UI admin | ClusterIP uniquement — port-forward pour ops |
| 9091 | /metrics Couche Métriques |
scrapé par ServiceMonitor |
Accès externe via Traefik :
https://storage.akko-ai.com
(ou la valeur de global.functionalAliases.storage dans vos overrides) —
gardé par oauth2-proxy + SSO Identité (Keycloak).
Buckets seedés à l'installation¶
Le Job post-install akko-storage-bucket-init crée les buckets dont
la plateforme a besoin :
| Bucket | Service propriétaire | Rôle |
|---|---|---|
akko-iceberg |
Catalogue Iceberg (Polaris) / Moteur de requête / Moteur de calcul | Données + métadonnées tables Iceberg |
akko-spark-logs |
Moteur de calcul master + workers | Event logs Moteur de calcul |
akko-mlflow |
MLflow | Artefacts du model registry |
akko-rag |
akko-rag | PDFs / DOCX / chunks uploadés |
akko-aden |
ADEN | Fichiers de dashboards persistés |
akko-airflow-logs |
Moteur d'orchestration (Airflow) | Logs de tâches |
Pour ajouter un bucket, ajouter une entrée à
helm/akko/charts/akko-storage/values.yaml :
Puis helm upgrade — le Job d'init est idempotent et ne crée que les
buckets manquants.
Isolation par identité & matrice d'accès¶
Le stockage objet n'est pas un bucket partagé unique avec une clé admin.
L'accès est segmenté sur deux axes, tous deux appliqués par le moteur IAM S3 de
SeaweedFS (defaultEffect: Deny — fail-closed) :
1. Comptes de service scopés — chaque service plateforme a sa propre
identité S3 restreinte aux buckets dont il a besoin
(helm/akko/charts/akko-storage/values.yaml → seaweedfs.serviceAccounts) :
| Identité | Buckets | Actions |
|---|---|---|
svc-trino |
akko-warehouse |
Read, List |
svc-spark |
akko-warehouse |
Read, Write, List, Tagging |
svc-polaris |
akko-warehouse, polaris |
Read, Write, List, Tagging |
svc-mlflow |
mlflow |
Read, Write, List, Tagging |
svc-airflow |
airflow, logs |
Read, Write, List, Tagging |
2. Espace personnel par utilisateur (STS / OIDC) — au spawn du notebook,
l'ID token Keycloak de l'utilisateur est échangé (AssumeRoleWithWebIdentity)
contre des credentials S3 temporaires (préfixe de clé ASIA… + un token de
session) scopés par la politique templatée AkkoUserSpacePolicy à
akko-users/${aws:username}/*. Un utilisateur ne peut lire/écrire/lister que
son propre préfixe.
Matrice observée (live, vérifiée E2E)¶
Pilotée depuis un vrai notebook en tant que bob_engineer (SSO), avec les
credentials STS injectés dans le pod — captures dans
tests/artifacts/C_bob_seaweedfs_separation*.png :
| Opération | Cible | Résultat |
|---|---|---|
| écriture + lecture | akko-users/bob_engineer/… (le sien) |
✅ OK |
| liste | akko-users/bob_engineer/ (son préfixe) |
✅ OK — uniquement ses objets |
| lecture | akko-users/alice_admin/… (un autre user) |
⛔ AccessDenied |
| liste | akko-users/alice_admin/ (un autre user) |
⛔ AccessDenied |
| écriture | akko-users/alice_admin/… (un autre user) |
⛔ AccessDenied |
| liste | akko-warehouse, mlflow, polaris, airflow (buckets de service) |
⛔ AccessDenied |
Un utilisateur ne peut donc pas lire l'espace d'un autre, ni atteindre les buckets de service — l'identité STS personnelle est confinée à son propre préfixe.
La clé admin akko additive
Une identité admin akko avec accès complet existe encore comme repli
transitoire tant que chaque consommateur n'est pas recâblé sur son compte de
service scopé (durcissement gouvernance T1b → T8). C'est un item connu, suivi,
pas un fail-open : les identités utilisateur et service sont déjà pleinement
scopées comme ci-dessus.
Credentials¶
L'access key + secret S3 sont stockés dans le Secret
akko-storage-creds et projetés dans chaque pod consommateur via les
env vars :
AKKO_S3_ENDPOINT=http://akko-storage:8333
AKKO_S3_ACCESS_KEY=<via secret>
AKKO_S3_SECRET_KEY=<via secret>
AKKO_S3_REGION=akko-local
Référence rapide — boto3 depuis un notebook¶
import os, boto3
s3 = boto3.client(
"s3",
endpoint_url=os.environ["AKKO_S3_ENDPOINT"],
aws_access_key_id=os.environ["AKKO_S3_ACCESS_KEY"],
aws_secret_access_key=os.environ["AKKO_S3_SECRET_KEY"],
region_name=os.environ.get("AKKO_S3_REGION", "akko-local"),
)
# Lister les buckets
print([b["Name"] for b in s3.list_buckets()["Buckets"]])
# Uploader un CSV
s3.upload_file("./customers.csv", "akko-iceberg", "raw/customers.csv")
# Le relire via Spark
df = spark.read.csv(
"s3a://akko-iceberg/raw/customers.csv", header=True, inferSchema=True
)
df.show()
Référence rapide — CLI aws¶
export AWS_ACCESS_KEY_ID=$AKKO_S3_ACCESS_KEY
export AWS_SECRET_ACCESS_KEY=$AKKO_S3_SECRET_KEY
export AWS_DEFAULT_REGION=akko-local
aws --endpoint-url $AKKO_S3_ENDPOINT s3 ls
aws --endpoint-url $AKKO_S3_ENDPOINT s3 cp ./report.pdf \
s3://akko-rag/uploads/report.pdf
Smoke test¶
Pour vérifier l'installation de bout en bout :
kubectl -n akko exec deploy/akko-akko-init-probe -- \
curl -s http://akko-storage:8333/ | head
# Attendu : XML ListAllMyBucketsResult
kubectl -n akko logs job/akko-storage-bucket-init --tail=20
# Attendu : "[+] bucket <nom> ready" pour chaque bucket seedé
Notes opérationnelles¶
- Réplication — déploiement single-node par défaut. Pour répliquer,
poser
storage.replication: "001"(une copie supplémentaire sur un rack différent — adapter à la topologie de votre cluster) et ajouter au moins 2 workers dansstorage.workerCount. - Chiffrement au repos — chiffrement chart-level supporté mais
désactivé par défaut. Activer
storage.encryptionAtRest.enabled: trueet provisionner la clé KMS selon le runbook de chiffrement. - Backup — l'approche recommandée est un CronJob
aws s3 syncvers un endpoint S3-compatible off-site. Le log de DR drill capture la procédure de bout en bout.
Liens¶
- Apache Polaris — catalogue Iceberg lisant
akko-iceberg - Moteur de requête — moteur de fédération utilisant le catalogue Iceberg
- Spark Connect — écrit les tables Iceberg via Catalogue Iceberg
- akko-rag — uploade les PDFs vers le bucket
akko-rag