Aller au contenu

Se brancher sur la plateforme

Cette page est un mode d’emploi : comment, depuis votre code (pipelines, jobs, requêtes), atteindre chaque brique de la plateforme — quel service joindre, avec quel secret, quel paramétrage. Elle suppose que votre code tourne dans un pod du cluster, dans un namespace autorisé par les NetworkPolicies.

Pour développer en local d’abord, montez un banc et rejouez la boucle complète : voir le tutoriel Monter le banc local. Pour ce que fait chaque brique et pourquoi elle est là, voir composants. Pour la liste de référence des endpoints (table + StorageClasses), voir le guide du développeur data.

Frontière (ADR 0022 / 0023). Ce dépôt fournit l’infrastructure (générique) ; le code métier vit dans le dépôt applicatif. Les exemples ci-dessous sont génériques et jetables — ils montrent le comment se brancher, pas la logique d’un projet. Les valeurs réelles (mots de passe, hostnames) viennent d’une config locale non versionnée.

Source de vérité des endpoints. Les adresses, ports, conventions de secrets et StorageClasses sont publiés comme contrat versionné sous contract/ — diff-able et consommable par un script (ADR 0043). En cas d’écart, le contrat fait foi ; cette page en est la version pédagogique.

Un cluster HA unique pg (namespace postgres) porte quatre bases logiques, chacune avec son rôle propriétaire (ADR 0024) : dagster (event log de l’orchestrateur), marquez (store de lineage), pgvector (recherche sémantique), mlflow (backend store du suivi de modèles, ADR 0082).

Connexion : écrire sur le service primary (pg-rw), lire sur le replica (pg-ro). Le mot de passe d’un rôle est dans le Secret pg-role-<rôle> (clé password, namespace postgres) — ne l’employez jamais en clair, lisez-le du Secret :

Fenêtre de terminal
# Exemple générique : récupérer le mot de passe du rôle dagster.
kubectl -n postgres get secret pg-role-dagster -o jsonpath='{.data.password}' | base64 -d
# Exemple générique (psycopg) — depuis un pod, le pwd vient d'une var d'env
# injectée par un secretKeyRef (jamais en dur).
import os, psycopg
conn = psycopg.connect(
host="pg-rw.postgres.svc.cluster.local", port=5432,
dbname="pgvector", user="pgvector", password=os.environ["PG_PASSWORD"],
)

Pour la recherche sémantique, l’extension pgvector est déjà activée par l’opérateur (CREATE EXTENSION vector fait) : utilisez le type vector(n) dans vos tables.

Prérequis réseau : votre pod doit avoir une NetworkPolicy egress vers postgres:5432 (modèle : platform/network-policies/<app>/allow-postgres-egress.yaml).

Traçabilité — émettre du lineage vers Marquez

Section intitulée « Traçabilité — émettre du lineage vers Marquez »

Émettez des événements OpenLineage vers l’API Marquez ; ils y sont ingérés et visualisés (ADR 0028). Le client OpenLineage standard lit ces variables d’environnement :

VariableValeur (intra-cluster)
OPENLINEAGE_URLhttp://marquez.marquez.svc.cluster.local:5000
OPENLINEAGE_ENDPOINTapi/v1/lineage
OPENLINEAGE_NAMESPACEle namespace logique de vos jobs (ex. dagster)

Pour requêter le lineage (lecture), l’API REST Marquez — p. ex. lister les jobs d’un namespace :

Fenêtre de terminal
# Depuis un pod du cluster :
wget -qO- http://marquez.marquez.svc.cluster.local:5000/api/v1/namespaces/<ns>/jobs

Réseau : Marquez n’accepte le POST de lineage que depuis un namespace autorisé (allow-openlineage-ingress.yaml) ; votre pod émetteur a besoin de l’egress correspondant (allow-marquez-egress.yaml côté Dagster en est le modèle — leçon du drift L19, cf. leçons des runs).

Loggez vos entraînements (paramètres, métriques, artefacts) vers le serveur MLflow ; ils y sont enregistrés et visualisables, et les modèles versionnés dans le registre (ADR 0082). Le serveur est livré vide : c’est votre code (côté atlas) qui le peuple. Le client MLflow lit une seule variable d’environnement (pendant de OPENLINEAGE_URL) :

VariableValeur (intra-cluster)
MLFLOW_TRACKING_URIhttp://mlflow.mlflow.svc.cluster.local:5000
# Exemple générique — depuis un pod du cluster, l'URI vient d'une var d'env.
import os, mlflow
mlflow.set_tracking_uri(os.environ["MLFLOW_TRACKING_URI"])
mlflow.set_experiment("mon-experiment")
with mlflow.start_run():
mlflow.log_param("epochs", 10)
mlflow.log_metric("accuracy", 0.92)
# mlflow.log_artifact(...) / mlflow.<saveur>.log_model(...) → artefact store S3

Le backend store (métadonnées des runs) est la base CNPG mlflow ; l’artefact store (modèles, fichiers volumineux) est du S3 (ADR 0036) — vous n’avez rien à câbler, le serveur les porte. Pas d’authentification intra-cluster (réseau privé mono-admin).

Réseau : votre pod émetteur a besoin d’un egress vers mlflow:5000 (modèle : platform/network-policies/<app>/allow-mlflow-egress.yaml).

Orchestration — brancher une code-location Dagster

Section intitulée « Orchestration — brancher une code-location Dagster »

L’orchestrateur est livré vide (aucune code-location) : c’est le socle, votre code (assets, jobs) s’y branche depuis le dépôt applicatif (ADR 0026, frontière ADR 0022).

  • Storage : Dagster persiste son event log dans la base dagster (déjà câblé via le Secret dagster-pg-auth).
  • Exécution : K8sRunLauncher — chaque run devient un Job Kubernetes.

Le socle monte un ConfigMap dagster-workspace (clé workspace.yaml) avec load_from: []zéro location : un orchestrateur sans code-location doit quand même recevoir un workspace, sinon le webserver/daemon échouent. Pour brancher votre code, deux objets à pousser (par GitOps, jamais kubectl apply — frontière ADR 0022) :

  1. Un Deployment + Service gRPC dans le ns dagster, qui sert votre image de code-location (dagster api grpc -h 0.0.0.0 -p 4000 -m <votre_module>). Référencez votre image en registry:80/<repo>:<tag> et injectez-y OPENLINEAGE_URL pour que vos runs émettent le lineage.

  2. Le branchement dans le workspace : surchargez le ConfigMap dagster-workspace par un patch GitOps (kustomize/Argo CD) plutôt que de le réécrire :

    # workspace.yaml côté code-location (exemple générique) :
    load_from:
    - grpc_server:
    host: <votre-code-location>.dagster.svc.cluster.local
    port: 4000
    location_name: <votre-code-location>

    Après réconciliation Argo CD, la location apparaît dans l’UI.

Accès Internet sortant (sync d’un snapshot ouvert). Sous default-deny (ADR 0019), le ns dagster n’a d’egress que vers DNS / api-server / Postgres / registry / Marquez. Un run qui synchronise un snapshot de données ouvert depuis un store objet public a besoin d’un egress Internet : le socle le fournit par allow-internet-egress.yaml (ports 443/80). Côté applicatif, bornez le volume par config : c’est une décision métier, pas d’infra.

Vos images applicatives (code-location, jobs) se poussent dans le registry interne (ADR 0011) :

  • Référencez-les en registry:80/<repo>:<tag> dans vos manifestes/CR.
  • Les nœuds tirent ce registry en HTTP (config containerd posée par la plateforme : use_local_image_pull, drifts L9/L13).
  • arm64 : sur le banc, les images maison sont buildées en interne ; en prod x86, les images officielles sont re-taguées (ADR 0006).

Bloc (PVC) — bases, état applicatif. StorageClasses disponibles (défaut = rook-ceph-block-replicated, RBD ×3, ADR 0001) :

# PVC générique :
spec:
accessModes: [ReadWriteOnce]
storageClassName: rook-ceph-block-replicated
resources: { requests: { storage: 10Gi } }

Objet (S3) — datalake, artefacts. Demandez un bucket via un ObjectBucketClaim (Rook provisionne le bucket + un Secret de creds dans votre namespace) :

apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata: { name: mon-bucket, namespace: <votre-ns> }
spec:
generateBucketName: mon-bucket
storageClassName: rook-ceph-datalake

Le Secret généré (même nom que l’OBC) porte AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY ; l’endpoint S3 est http://rook-ceph-rgw-datalake.rook-ceph:80 (path-style, HTTP intra-cluster). Détail : storage/ceph/storageClass/datalake/.

Pour exposer une UI, exposez-la en L4 sur un port du nœud : un Service type: NodePort (charts/bundles vendored) ou un hostPort sur le conteneur (briques dont vous possédez le manifeste). L’accès se fait alors en http://<IP-nœud>:<nodePort> (HTTP clair, zéro DNS, zéro LB-IPAM) ; le portail observe le port réel attribué (service.spec.ports[].nodePort) pour construire le lien — rien à figer côté hostname (ADR 0092).

Le socle fournit les opérateurs ; vous les consommez en émettant des objets :

  • Métriques : exposez un ServiceMonitor/PodMonitor → Prometheus scrape automatiquement.
  • Logs : écrivez sur stdout/stderr → Promtail les collecte (DaemonSet) et les pousse vers Loki, sans action de votre part.

Vous ne déployez pas vos workflows avec kubectl, mais par la boucle GitOps (build → push registry → commit manifeste dans la forge → webhook → réconciliation Argo CD). La mise en pratique pas à pas sur un banc — cloner la forge, pousser, observer la réconciliation — est décrite dans le tutoriel Monter le banc local.