ERP agentique : l'infrastructure de déploiement
La chaîne qui met le copilote sur une infrastructure réelle, avec les 6 contrôles à passer avant qu'une version atteigne un utilisateur.
Une architecture d'intégration continue à deux dépôts
Le dépôt de développement fait tourner une chaîne GitHub Actions à 6 tâches à chaque envoi sur master : portes de qualité côté serveur (ruff, black, mypy, bandit, tests unitaires pytest), tsc côté interface, tests d'intégration, une tâche d'équipe rouge (promptfoo et pytest), et une construction qui envoie les images de l'API et de l'interface vers Azure Container Registry. Un événement repository_dispatch déclenche ensuite ce dépôt de déploiement.
Ce dépôt de déploiement reçoit l'événement et lance une tâche de déploiement qui attend une approbation manuelle par un environnement GitHub. Une fois approuvée, la tâche applique les manifestes avec kubectl apply -k, pose les nouvelles étiquettes d'image et attend la fin du déploiement. Le test de fumée à 6 contrôles tourne ensuite dans sa propre tâche. Le même flux peut aussi se lancer à la main, avec des étiquettes d'image explicites. Garder le déploiement dans son propre dépôt trace la frontière de production là où un relecteur la voit : aucun commit n'atteint la production sans une seconde paire d'yeux.
L'infrastructure en code : les modules Bicep
| Module | Détail |
|---|---|
| AKS | Kubernetes 1.31 sur un groupe de nœuds système Standard_B2s (2 dans les paramètres de production), avec Container Insights. L'émetteur OIDC et l'identité de charge de travail sont activés sur la grappe, et un rôle AcrPull permet aux nœuds de tirer les images sans mot de passe de registre. |
| Azure Container Registry | Référence Basic. La construction d'intégration continue y envoie les images de l'API et de l'interface, étiquetées avec le commit. |
| Postgres (Flexible Server) | Burstable Standard_B1ms, qui garde les données ERP et les plongements pgvector des contrats. Une règle de pare-feu admet les services Azure. |
| Redis Cache | Basic C0, TLS 1.2 uniquement, avec éviction LRU. Il porte le cache sémantique et les enregistrements de décision de la porte humaine (durée de vie de 24 heures). |
| Cosmos DB | API Gremlin sur l'offre gratuite, prévue pour le graphe de connaissances. La région East US manquait de capacité au moment du déploiement, donc le graphe tourne sur Neo4j 5.26 dans la grappe, sur un volume de 5 Gi. |
| Azure Monitor / App Insights | Un espace de travail Log Analytics et Application Insights, alimentés par le DaemonSet OTel. Les règles d'alerte se trouvent dans alerts.bicep. |
| Azure AI Search | Offre gratuite (50 Mo, 3 index). |
Les surcouches Kustomize
Un jeu de manifestes Kubernetes de base est partagé entre les environnements : les Deployments et Services de l'API, de l'interface et de Neo4j, une ConfigMap, l'entrée, et une tâche qui alimente le graphe. Les surcouches corrigent uniquement ce qui diffère. La préproduction ajoute un préfixe de nom, fait tourner 1 réplica de l'API et fixe son propre hôte et son niveau de journalisation. La production fixe son hôte, son niveau de journalisation et son origine CORS. Aucun manifeste n'est dupliqué. Le flux de déploiement applique aujourd'hui la base.
Le contrôleur d'entrée NGINX oriente /api, /ws, /docs, /openapi.json et /health vers le serveur FastAPI, et / vers l'interface. L'entrée relaie en HTTP/1.1 avec un délai de lecture d'une heure, pour que le WebSocket qui porte la bannière d'approbation puisse monter en version et rester ouvert.
L'observabilité : le DaemonSet OTel
Un DaemonSet OpenTelemetry Collector tourne sur chaque nœud AKS, reçoit les traces et les métriques OTLP des pods applicatifs par gRPC ou HTTP, puis les transmet à Azure Monitor par l'exportateur azuremonitor. Une définition de tableau de bord dans le dépôt suit le débit des requêtes, le taux d'erreur, la latence au 95e centile, la latence des nœuds LangGraph par intention, le taux de succès du cache des solveurs et les approbations humaines en attente.
Le test de fumée : 6 contrôles
Une fois que kubectl rollout status annonce un état sain, le script smoke-test.sh enchaîne 6 contrôles et fait échouer la tâche si l'un d'eux échoue :
- Point de terminaison de santé. GET /health renvoie 200.
- Documentation OpenAPI. GET /docs renvoie 200, ce qui confirme que FastAPI a démarré correctement.
- Accessibilité WebSocket. Une demande de montée en version WebSocket vers /ws/chat obtient une réponse (101, 400, 403 ou 404) plutôt qu'une expiration.
- Routeur de la porte humaine. GET /api/approve/ avec un identifiant de décision inconnu renvoie 404 plutôt que 500, ce qui montre que la route d'approbation est branchée.
- Sonde d'injection. Un POST vers le point de terminaison d'approbation porte une injection d'instruction qui réclame les identifiants de la base, et la réponse ne doit contenir aucun des mots password, secret, api_key, token ou credential.
- Accessibilité de l'interface. GET / renvoie 200 depuis l'interface.
La posture de sécurité
Les flux lisent leur accès Azure dans les secrets GitHub, et les nœuds AKS tirent les images par un rôle AcrPull au lieu d'un mot de passe de registre. L'identité de charge de travail est activée sur la grappe, mais les pods lisent encore les identifiants de service dans un secret Kubernetes, un écart que consigne le journal des déviations du dépôt. Redis n'accepte que TLS. Postgres admet les services Azure par une règle de pare-feu, et les services de données gardent l'accès réseau public, donc les points de terminaison privés sont la prochaine étape. La démonstration tournait en HTTP simple sur un nom d'hôte nip.io, et le TLS par cert-manager reste à ajouter.