Passer au contenu principal
Ce guide déploie la stack complète d’AgentEye sur un cluster Kubernetes dédié :
  • ClickHouse 24.8 — store analytique canonique pour les événements et les évaluations (StatefulSet avec volume persistant de 100 Gi). Obligatoire : le serveur refuse de démarrer sans lui.
  • PostgreSQL 16 — store relationnel/métadonnées pour les organisations, clés API, utilisateurs, tableaux de bord, requêtes sauvegardées et authentification (StatefulSet avec volume persistant de 50 Gi)
  • Redis 7.2 — cache partagé optionnel et backend de limitation de débit ; le serveur et le tableau de bord se dégradent gracieusement s’il est indisponible
  • Serveur AgentEye — API Rust pour l’ingestion d’événements, l’analytique et la gestion des clés (2 réplicas)
  • Tableau de bord AgentEye — interface web Next.js (2 réplicas)
  • Assistant IA (service agent) — assistant intégré au tableau de bord, en lecture seule, optionnel sur le port 9100 ; inactif tant qu’aucun endpoint LLM n’est configuré
  • Traefik (public) — contrôleur d’ingress pour le trafic collecteur, protégé par mTLS
  • Traefik (tableau de bord) — contrôleur d’ingress pour le tableau de bord, restreint par VPN/liste d’autorisation d’IP
  • cert-manager — certificats TLS et CA mTLS
  • CronJob de sauvegarde — dump combiné quotidien de PostgreSQL + ClickHouse à 03:00 UTC
  • Moniteur de renouvellement de certificats — alerte lorsque les certificats clients approchent de leur expiration
Durée estimée : 60 à 90 minutes pour un premier déploiement. Pour le modèle de déploiement géré où Exosphere s’en charge entièrement en votre nom, voir enterprise-docs/managed-deployment.md.

Prérequis

Exécutez chaque commande de vérification avant de commencer. Chaque vérification doit réussir.
PrérequisMinimumCommande de vérificationRésultat attendu
Cluster Kubernetes1.27+kubectl versionServer Version >= v1.27
Kustomize (intégré à kubectl)Kustomize v1.14+ (inclus dans kubectl 1.27+)kubectl kustomize --helpAffiche le texte d’utilisation
Helmv3helm versionVersion:"v3.x.x"
RBAC cluster-adminkubectl auth can-i create namespacesyes
StorageClass par défautkubectl get storageclassAu moins une ligne marquée (default)
Support LoadBalancerDépend du cloud (EKS, GKE, AKS le supportent tous par défaut)
PAT GitHubecho $AGENTEYE_TOKENNon vide (voir enterprise-docs/github-token.md)
opensslopenssl versionOpenSSL 1.x ou 3.x
Bucket de stockage cloudPour les sauvegardes PostgreSQL + ClickHouse (S3, GCS ou Azure Blob)
Dimensionnement du cluster : Minimum 3 nœuds, 4 vCPU / 8 Go de RAM chacun. Voir enterprise-docs/managed-deployment.md pour les exigences complètes.

Exécuter toutes les vérifications en une seule fois

Architecture du déploiement

Le point de terminaison d’ingestion est servi sur un nom d’hôte que vous contrôlez (ex. ingest.votre-entreprise.example). cert-manager demande un certificat TLS approuvé publiquement auprès de Let’s Encrypt via HTTP-01, de sorte que les collecteurs vérifient le certificat serveur par rapport au magasin de confiance du système, sans épinglage de CA par client. Le point de terminaison du tableau de bord fonctionne de la même façon : il est servi sur un second nom d’hôte que vous contrôlez (ex. agenteye.votre-entreprise.example) pointant vers le LoadBalancer Traefik du tableau de bord, et cert-manager émet son certificat Let’s Encrypt via ce LoadBalancer. Les navigateurs obtiennent un certificat de confiance sans avertissement.
La délivrance et le renouvellement des certificats se valident via HTTP-01, donc les deux LoadBalancers doivent être accessibles depuis l’internet public sur le port 80. Si vous devez restreindre l’accès IP au LoadBalancer du tableau de bord, coordonnez d’abord un solveur DNS-01 avec le support — sinon les renouvellements échouent silencieusement et le certificat expire.

Récupérer les manifestes

Vérification :
Résultat attendu : le fichier existe. S’il n’existe pas, le clonage a échoué — vérifiez votre AGENTEYE_TOKEN. Structure des répertoires :
La base contient toutes les ressources nécessaires à un déploiement complet, y compris les certificats Let’s Encrypt pour les deux noms d’hôte publics que vous configurez à la Phase 3.1. Un overlay surcharge la base pour un environnement spécifique (ex. tags d’image personnalisés, limites de ressources, câblage des variables d’environnement). Le répertoire third-party contient les fichiers de valeurs Helm pour l’infrastructure externe.
Surveillance de la santé (optionnel) : la sonde de disponibilité du serveur reflète déjà la santé de Postgres + ClickHouse, et third-party/robusta/ ajoute des alertes optionnelles de défaillance de pods Kubernetes-native vers Slack. Voir enterprise-docs/health-monitoring.md.

Phase 1 — Infrastructure tierce (~30 min)

1.1 Installer cert-manager

cert-manager gère les certificats TLS pour HTTPS et la CA privée utilisée pour les certificats clients mTLS.
Vérification :
Résultat attendu : 3 pods tous Runningcert-manager, cert-manager-cainjector, cert-manager-webhook.
Résultat attendu : au moins certificates.cert-manager.io, clusterissuers.cert-manager.io, issuers.cert-manager.io. En cas d’échec : Des pods en CrashLoopBackOff indiquent généralement que les CRDs n’ont pas été installés. Relancez avec --set crds.install=true. Si les pods webhook échouent à leur sonde de disponibilité, attendez 30 secondes et vérifiez à nouveau — ils peuvent prendre un moment à démarrer.

1.2 Installer Traefik — Contrôleur d’ingestion public

Cette instance Traefik gère le trafic collecteur sur un LoadBalancer externe. Elle termine TLS et applique le mTLS (vérification du certificat client) sur le point de terminaison d’ingestion.
Vérification :
Résultat attendu : 1 pod Running.
Résultat attendu : l’IngressClass existe (ce n’est pas la classe par défaut). En cas d’échec : Vérifiez kubectl describe pod -n traefik-public <nom-du-pod> pour des erreurs de tirage d’image ou des contraintes de ressources.

1.3 Installer Traefik — Contrôleur du tableau de bord

Cette instance Traefik sert le tableau de bord sur un LoadBalancer dédié, restreint par liste d’autorisation d’IP.
Deux mécanismes de liste d’autorisation sont fournis pour cette instance. Ce guide utilise values-dashboard.yaml, qui restreint l’accès avec le champ portable service.loadBalancerSourceRanges. Un values-internal.yaml parallèle est également fourni pour les environnements AWS qui préfèrent l’annotation service.beta.kubernetes.io/aws-load-balancer-source-ranges. Choisissez l’un et utilisez-le de manière cohérente ; les étapes ci-dessous supposent values-dashboard.yaml.
Avant d’installer, modifiez third-party/traefik/values-dashboard.yaml pour définir les IP sources autorisées. Le champ loadBalancerSourceRanges contrôle quelles IP peuvent accéder au tableau de bord. Par défaut, il est défini à 0.0.0.0/0 (toutes les IP) ; restreignez-le à votre VPN, bureau ou IP de sortie connues.

Autoriser une seule IP

Autoriser plusieurs IP

Ajoutez une entrée par IP ou bloc CIDR. Le suffixe /32 correspond à une seule adresse IPv4 ; un bloc CIDR (ex. /24) correspond à une plage. Vous pouvez mélanger librement des IP individuelles et des plages :
Conseils pour maintenir la liste :
  • Gardez une entrée par ligne et ajoutez un court commentaire # identifiant le propriétaire ou l’objet de chaque IP ; c’est ce que les futurs opérateurs utilisent pour décider si une entrée est toujours nécessaire.
  • Utilisez toujours la notation CIDR. Une IP nue comme 203.0.113.10 est rejetée par le fournisseur cloud ; utilisez 203.0.113.10/32.
  • Pour les plages IPv6, utilisez l’équivalent /128 (adresse unique) ou un CIDR plus grand, ex. 2001:db8::1/128. Tous les fournisseurs cloud ne supportent pas les plages sources IPv6 ; consultez la documentation LoadBalancer de votre fournisseur.
  • La liste est un OU : le trafic est autorisé si la source correspond à n’importe quelle entrée.
Après avoir modifié le fichier, procédez à helm install ci-dessous. Si le contrôleur est déjà installé, exécutez helm upgrade avec les mêmes options, ou modifiez le Service à l’exécution (section suivante).

Mettre à jour la liste d’autorisation à l’exécution

Vous pouvez modifier les IP autorisées sans mise à niveau Helm en patchant directement le Service. Le patch remplace la liste entière ; incluez toujours chaque IP que vous souhaitez conserver, pas seulement la nouvelle. Pour remplacer la liste par un nouvel ensemble d’IP :
Pour ajouter une IP en toute sécurité sans perdre les entrées existantes, lisez d’abord la liste actuelle, puis patchez avec l’ensemble combiné :
Les patches d’exécution ne sont pas répercutés dans values-dashboard.yaml. Pour conserver la modification à travers les futures mises à niveau Helm, mettez également à jour le fichier de valeurs et commitez-le.
Puis installez :
Vérification :
Résultat attendu : 1 pod Running.
Résultat attendu : l’IngressClass existe.

1.4 Attendre les LoadBalancers

Les deux instances Traefik ont besoin d’IP externes avant de continuer.
Vérification : Les deux services affichent une EXTERNAL-IP (pas <pending>). Si toujours en attente, surveillez l’attribution :
Appuyez sur Ctrl+C une fois l’IP apparue. L’attribution d’IP prend généralement 2 à 5 minutes. En cas d’échec : <pending> après 10 minutes signifie généralement que le fournisseur cloud ne peut pas provisionner un LoadBalancer. Vérifiez : les tags de sous-réseau (EKS requiert kubernetes.io/role/elb), la configuration VPC, les quotas de service, et que l’annotation LB interne correcte est définie pour l’instance interne.

Phase 2 — Création des secrets (~10 min)

Tous les secrets sont créés manuellement avant de déployer l’application. Cela garantit que les valeurs sensibles n’apparaissent jamais dans les fichiers de manifeste.

2.1 Créer le namespace

Vérification :
Résultat attendu : statut Active.

2.2 Secret de tirage d’image

Ce secret s’authentifie auprès de ghcr.io pour tirer les images conteneur d’AgentEye. Voir enterprise-docs/github-token.md pour savoir comment générer votre PAT.
Vérification :
Résultat attendu : kubernetes.io/dockerconfigjson. Vérification approfondie — vérifier que le token peut réellement tirer des images : Utilisez le tag d’image server épinglé dans le kustomization.yaml de votre overlay (actuellement v0.0.1-beta.48 dans l’overlay acme fourni et le déploiement de base). Substituez le tag ci-dessous par celui que vous déployez pour que cette vérification ne dérive pas entre les versions :
Résultat attendu : ok affiché dans les logs. En cas d’échec : ErrImagePull ou 401 Unauthorized signifie que le PAT est invalide ou manque le scope read:packages. Revérifiez enterprise-docs/github-token.md.

2.3 Identifiants PostgreSQL

Important : Nous utilisons -hex (et non -base64) pour générer le mot de passe. La sortie Base64 peut contenir des caractères +, / et = qui cassent la chaîne de connexion DATABASE_URL. Voir enterprise-docs/troubleshooting.md pour plus de détails.
Stockez POSTGRES_PASSWORD dans votre gestionnaire de secrets immédiatement. Vous en aurez besoin si vous restaurez un jour depuis une sauvegarde ou vous connectez directement à la base de données.
Vérification :
Résultat attendu : le secret existe.
Résultat attendu : 48 (24 octets hex = 48 caractères).

2.4 Clé API admin

La clé admin est le credential d’amorçage. Le serveur la crée ou la met à jour à chaque démarrage avec toutes les permissions. Utilisez-la pour créer des clés collecteur avec des portées limitées à la Phase 7. Voir enterprise-docs/api-keys.md pour le modèle de permissions complet.
Stockez ADMIN_KEY dans votre gestionnaire de secrets immédiatement.
Vérification :
Résultat attendu : le secret existe.

2.5 Configuration de l’authentification (connexion au tableau de bord)

Le tableau de bord utilise email + OTP pour la connexion utilisateur. Sans ce secret, le serveur démarre quand même et le chemin API ADMIN_KEY continue de fonctionner, mais aucun utilisateur ne peut se connecter via l’interface. Toutes les clés sont référencées avec optional: true dans le manifeste de base, donc des secrets partiels (ou aucun secret) sont acceptables ; le serveur revient aux valeurs par défaut documentées. Regrouper tout dans un seul secret agenteye-auth permet de faire pivoter la surface d’authentification en un seul endroit.
CléRôle
ADMIN_EMAILUtilisateur admin d’amorçage. Créé ou mis à jour à chaque démarrage avec toutes les permissions, protégé contre la suppression/modification de permissions via le tableau de bord. Sans cette clé, aucun admin n’est initialisé et la première connexion est impossible.
ALLOWED_EMAILSListe d’autorisation séparée par des virgules. Supporte les adresses exactes (user@example.com) et les wildcards de domaine (*@example.com). Sans elle, aucun utilisateur ne peut se connecter ou être créé.
SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_FROMRelais SMTP pour l’envoi des codes OTP. Si SMTP_HOST n’est pas défini, les codes OTP sont enregistrés dans stdout du serveur au lieu d’être envoyés par email (utile pour les tests de fumée au premier démarrage). Fournissez toutes les clés SMTP ensemble pour une vraie livraison par email.
SMTP_TLSL’une des valeurs : starttls (par défaut), tls, ou none.
DEFAULT_ORG_NAME, DEFAULT_ORG_SLUGOptionnel. Donnez à l’organisation default intégrée un nom d’affichage convivial et un slug d’URL pour qu’elle soit accessible à ex. /acme au lieu de /default. Appliqué au premier démarrage uniquement ; une fois que vous renommez l’org avec agenteye-orgctl org rename (voir §7.6), ces valeurs sont ignorées. Le slug doit contenir 1 à 40 caractères alphanumériques minuscules avec des tirets internes simples. Laissez les deux non définis pour conserver le default générique.
Stockez les identifiants SMTP dans votre gestionnaire de secrets.
Vérification :
Résultat attendu : les clés que vous avez renseignées apparaissent dans la sortie.

2.6 Clé d’isolation des orgs multi-tenant (optionnel)

Ignorez cette section pour un déploiement mono-tenant ; le serveur fonctionne avec une valeur dev par défaut intégrée et sert correctement l’unique org default. Avant de créer une seconde organisation, définissez un ORG_CH_SECRET fort et stable : le mot de passe ClickHouse de chaque org est dérivé comme HMAC(ORG_CH_SECRET, org_id), donc la valeur dev connue publiquement produirait des credentials par org dérivables publiquement. La commande agenteye-orgctl org create (voir §7.6 Provisionner les organisations) refuse de s’exécuter tant que le serveur utilise encore la valeur dev intégrée.
Le serveur lit cette valeur via un secretKeyRef optionnel, donc un cluster mono-tenant qui ne la crée jamais démarre normalement. Gardez la valeur stable et identique sur tous les réplicas ; la faire pivoter invalide le mot de passe ClickHouse dérivé de chaque org jusqu’à ce que la réconciliation au démarrage reprovisionne les utilisateurs (un redémarrage progressif avec la valeur cohérente partout suffit à réparer). Voir deploy/base/server/secret.example.yaml.
Stockez ORG_CH_SECRET dans votre gestionnaire de secrets et ne le faites pas pivoter sans réflexion.

2.7 Vérifier tous les secrets

Sortie attendue (parmi les secrets par défaut éventuels) :
Les quatre secrets de base (agenteye-admin-key, agenteye-auth, agenteye-image-pull, agenteye-postgres) doivent être présents avant de continuer. agenteye-org-ch-secret n’est requis que pour les déploiements multi-tenant (voir §2.6).

Phase 3 — Déployer l’application (~5 min)

3.1 Configurer les noms d’hôte publics

cert-manager a besoin des noms d’hôte d’ingestion et de tableau de bord avant de pouvoir demander leurs certificats Let’s Encrypt. Copiez le template et définissez les deux :
domain.env est dans le gitignore ; il reste local à chaque déploiement. La compilation kustomize échoue explicitement si l’une ou l’autre clé est manquante.
Le DNS doit résoudre en premier. Vous n’avez pas à pointer le DNS vers les LBs maintenant (ils n’existent pas tant que la Phase 1.2 n’est pas complète), mais la délivrance ACME à l’étape 3.2 réessaiera jusqu’à ce que chaque nom d’hôte résolve vers son LoadBalancer. Vous pouvez soit définir le DNS maintenant (en utilisant les noms d’hôte LB capturés à la Phase 1.4), soit continuer et ajouter les enregistrements à la Phase 4.

3.2 Appliquer les manifestes

Appliquez directement la base pour une nouvelle installation, ou un overlay si vous en avez créé un pour cet environnement (les overlays épinglent seulement les tags d’image, les variables d’environnement et les limites de ressources ; ils héritent des certificats et du routage de la base) :
L’overlay inclut automatiquement la base ; appliquez l’un ou l’autre, pas les deux.

3.3 Attendre les pods

L’attente est limitée aux pods du plan de données principal. Les pods optionnels agent (assistant IA) et redis démarrent en parallèle ; l’assistant reste inactif jusqu’à ce que vous fournissiez son endpoint LLM (voir enterprise-docs/assistant.md), et Redis est un cache au mieux, donc aucun des deux n’a besoin d’être Ready pour que la plateforme serve du trafic. Vérification :
Résultat attendu (les pods optionnels agent et redis apparaissent également et atteignent l’état Running) :
En cas d’échec :
Statut du podCause probableCommande de débogage
ImagePullBackOffSecret de tirage d’image incorrect ou PAT invalidekubectl describe pod <name> -n agenteye
CrashLoopBackOffVariables d’environnement incorrectes (ex. DATABASE_URL)kubectl logs <name> -n agenteye
PendingCPU/mémoire insuffisants ou aucun nœud disponiblekubectl describe pod <name> -n agenteye (vérifier les Events)

3.4 Vérifier le stockage

Résultat attendu, tous deux avec le statut Bound :
PVCCapacitéSoutient
postgres-data-postgres-050GiStore relationnel/métadonnées PostgreSQL
clickhouse-data-clickhouse-0100GiStore analytique d’événements + évaluations ClickHouse
Un PVC redis-data-redis-0 (1 Gi) apparaît également pour le cache optionnel. En cas d’échec : Pending signifie qu’aucune StorageClass ne peut provisionner le volume. Vérifiez kubectl get storageclass et assurez-vous qu’une valeur par défaut existe. Pour la production, superposez le volume ClickHouse sur une StorageClass SSD rapide (ex. gp3 sur AWS, pd-ssd sur GCP) dans votre overlay ; le débit de compaction souffre sur des disques lents.

3.5 Vérifier les certificats

Résultat attendu : 3 certificats, tous Ready: True :
NomÉmetteurRôle
mtls-caselfsignedCA privée pour émettre les certificats clients mTLS (validité de 10 ans)
ingest-tlsletsencrypt-prodCertificat TLS public pour le point de terminaison d’ingestion (90 jours, auto-renouvelé)
dashboard-tlsletsencrypt-prodCertificat TLS public pour le tableau de bord (90 jours, auto-renouvelé)
Si ingest-tls ou dashboard-tls n’est pas Ready : Exécutez kubectl describe certificate <name> -n agenteye et lisez les Events. Les causes courantes :
  • Le DNS ne pointe pas encore vers le LB. Let’s Encrypt résout le nom d’hôte et frappe le port 80 pour valider — INGEST_DOMAIN doit résoudre vers le LB public, DASHBOARD_DOMAIN vers le LB du tableau de bord. Tant que le CNAME/Alias ne se propage pas, la commande reste pending. Une fois le DNS correct, cert-manager réessaie automatiquement (pas besoin de supprimer le Certificate).
  • Nom d’hôte non substitué. Si dnsNames affiche encore INGEST_DOMAIN_PLACEHOLDER / DASHBOARD_DOMAIN_PLACEHOLDER, vous avez sauté l’étape 3.1 — créez base/certificates/domain.env et réappliquez.
  • Traefik du tableau de bord ne peut pas servir le challenge (uniquement dashboard-tls). L’instance Traefik du tableau de bord doit être installée avec le fichier de valeurs fourni (Phase 1.2), qui active le fournisseur Ingress limité servant le solveur HTTP-01 de cert-manager. Une instance installée sans lui laisse le challenge non routable et la commande pending indéfiniment.
Si mtls-ca n’est pas Ready : cert-manager lui-même est défaillant. Revérifiez les pods cert-manager de l’étape 1.1.

3.6 Vérifier les CronJobs

Résultat attendu :
NomPlanificationRôle
agenteye-backup0 3 * * *Sauvegarde quotidienne Postgres + ClickHouse à 03:00 UTC
cert-renewal-check0 3,15 * * *Alertes d’expiration de certificats à 03:00 et 15:00 UTC

3.7 Vérifier le démarrage correct du serveur

Vérification : Recherchez une ligne de démarrage indiquant que le serveur écoute sur le port 8080. Il ne doit y avoir aucune erreur de connexion à la base de données (le serveur requiert que PostgreSQL et ClickHouse soient accessibles avant de signaler Ready). En cas d’échec : La cause la plus courante est un POSTGRES_PASSWORD contenant des caractères non sûrs pour les URL qui cassent la DATABASE_URL. Voir enterprise-docs/troubleshooting.md.

3.8 Vérifier la connexion du tableau de bord au serveur

Vérification : Recherchez Ready dans la sortie sans erreur ECONNREFUSED ou similaire. En cas d’échec : Vérifiez que le Service server existe (kubectl get svc server -n agenteye) et que AGENTEYE_SERVER_URL est défini à http://server:8080 dans le déploiement du tableau de bord.

Phase 4 — Accès réseau (~5 min)

4.1 Récupérer les adresses des LoadBalancers

Sur AWS EKS, les LoadBalancers retournent un nom d’hôte au lieu d’une IP. Remplacez .ip par .hostname dans les commandes ci-dessus.
Vérification :
Les deux doivent être non vides.

4.2 Pointer le DNS vers les LoadBalancers

Créez des enregistrements DNS pour que les noms d’hôte de base/certificates/domain.env résolvent vers leurs LoadBalancers — INGEST_DOMAIN vers le LB Traefik public, DASHBOARD_DOMAIN vers le LB Traefik du tableau de bord :
  • AWS Route 53 : enregistrement A avec Alias = Yes, cible = le nom d’hôte du LB. N’utilisez pas un simple A → IP ; les IP ELB changent.
  • Tout autre fournisseur : CNAME du nom d’hôte vers le nom d’hôte du LB.
Vérification :
Doit retourner les mêmes adresses que $PUBLIC_IP et $INTERNAL_IP respectivement (ou, sur EKS, résoudre vers les mêmes noms d’hôte *.elb.amazonaws.com). Une fois le DNS résolu, cert-manager termine les commandes ACME en attente de la Phase 3.5 en moins d’une minute. Relancez kubectl get certificates -n agenteye jusqu’à ce que ingest-tls et dashboard-tls affichent Ready: True.

4.3 Atteindre le point de terminaison d’ingestion

Le point de terminaison d’ingestion public applique le mutual TLS, donc chaque requête (y compris /health) doit présenter un certificat client. Vous émettez votre premier certificat client à la Phase 5 ; si vous en avez déjà un, vérifiez l’accessibilité maintenant :
Résultat attendu : {"status":"ok"}. L’option -k n’est pas nécessaire — le certificat serveur est chaîné à une CA publique pour INGEST_DOMAIN, donc il est validé par rapport au magasin de confiance du système. Accédez au point de terminaison d’ingestion par son nom d’hôte INGEST_DOMAIN (qui correspond au certificat émis), et non par l’IP/nom d’hôte brut du LoadBalancer. Le point de terminaison du tableau de bord est servi sur DASHBOARD_DOMAIN avec un certificat de confiance publique et n’est pas derrière mTLS, donc ni -k ni certificat client ne sont nécessaires :
Accédez au tableau de bord par son nom d’hôte, pas par l’adresse LB brute — le certificat est lié à DASHBOARD_DOMAIN, donc l’adresse brute affiche une non-correspondance de nom de certificat. En cas d’échec : Si curl se bloque, vérifiez que le LB est accessible depuis votre machine (VPN, groupes de sécurité, règles de pare-feu). Une erreur de handshake certificate required sur le nom d’hôte d’ingestion signifie qu’aucun certificat client n’a été présenté ; complétez d’abord la Phase 5. Une erreur de validation TLS sur le nom d’hôte d’ingestion signifie que le certificat serveur n’a pas fini d’être émis ; revenez à la Phase 3.5 et résolvez le problème là-bas.

Phase 5 — Émettre des certificats clients mTLS (~10 min par cluster)

Les collecteurs s’authentifient avec deux facteurs : un certificat client (couche transport, prouve que la requête provient d’un cluster autorisé) et une clé API (couche application, prouve que la requête provient d’un collecteur avec la permission events:add). Une clé compromise est inutile sans le certificat ; un certificat volé est inutile sans une clé valide.

5.1 Émettre un certificat

Chaque cluster exécutant des collecteurs a besoin de son propre certificat client. Depuis le répertoire des manifestes :
Remplacez <cluster-name> par un identifiant significatif (ex. us-east-1-prod, staging). Vérification : Le script affiche ==> Done! et liste les fichiers de sortie.
Résultat attendu : Ready: True. Fichiers de sortie dans issued/<cluster-name>/ :
FichierRôle
client.crtCertificat client (validité de 90 jours)
client.keyClé privée client
ca.crtCertificat CA pour la vérification du serveur
collector-mtls-secret.yamlSecret Kubernetes prêt à appliquer pour le cluster collecteur

5.1b Livraison alternative : AWS Secrets Manager

Si le consommateur du certificat est un Pod Kubernetes qui a besoin de client.crt et client.key sur disque — cas typique lorsque vous exécutez l’agenteye-collector comme sidecar dans votre pod applicatif — poussez le bundle de certificat dans AWS Secrets Manager. Le pod applicatif le monte ensuite via le Secrets Store CSI Driver avec IRSA, et la rotation des certificats est entièrement automatisée.
Lors d’une ré-exécution (renouvellement), le script appelle PutSecretValue sur le même secret, donc l’ARN et le nom restent stables. Le CSI Driver récupère la nouvelle version lors de son prochain cycle de rotation et réécrit les fichiers dans le pod. Prérequis :
  • CLI aws v2 authentifiée sur votre compte AWS.
  • jq installé.
  • Variable d’environnement AWS_REGION définie.
  • Permissions IAM sur votre identité appelante (limitez Resource à arn:aws:secretsmanager:<region>:<account>:secret:agenteye/mtls-client/*) :
    • secretsmanager:CreateSecret
    • secretsmanager:DescribeSecret
    • secretsmanager:PutSecretValue
    • secretsmanager:TagResource
Ce que fait le script dans ce mode :
ÉtapeAction
1Émet / ré-extrait le certificat via cert-manager (identique au mode par défaut).
2Appelle DescribeSecret sur agenteye/mtls-client/<cluster-name> pour décider création ou mise à jour.
3Première exécution : CreateSecret avec un payload JSON à trois clés (client.crt, client.key, ca.crt), tagué AgentEyeCluster=<cluster-name>. Exécutions suivantes : PutSecretValue pour publier une nouvelle version ; tag rafraîchi via TagResource.
4Supprime issued/<cluster-name>/ uniquement après un téléversement réussi. En cas d’échec, le répertoire est conservé pour permettre une nouvelle tentative.
Si le secret est planifié pour suppression, le script échoue avec un message clair vous indiquant d’exécuter aws secretsmanager restore-secret --secret-id agenteye/mtls-client/<cluster-name> avant de réessayer. Pour le câblage complet du pod (SecretProviderClass, configuration IRSA, comportement de rotation, dépannage), voir enterprise-docs/single-pod-deployment.md.

5.2 Vérifier que le certificat fonctionne

Testez le certificat émis contre l’ingress mTLS :
Résultat attendu : {"status":"ok"} En cas d’échec :
ErreurCauseCorrection
certificate requiredCertificat non présentéVérifiez les chemins de fichiers dans la commande curl
bad certificateIncompatibilité de CAVérifiez que mtls-ca-issuer a émis le cert : kubectl describe certificate mtls-client-<name> -n agenteye
connection refusedMauvais nom d’hôte ou LB inaccessibleVérifiez /etc/hosts ou le DNS

5.3 Livrer au cluster collecteur

Envoyez collector-mtls-secret.yaml à l’équipe qui opère le cluster collecteur. Ils l’appliquent :
Puis configurez le collecteur pour monter le secret et utiliser les chemins de certificat :
Voir enterprise-docs/collector-installation.md pour la configuration complète du collecteur, y compris les montages de volumes Kubernetes. Vérification (dans le cluster collecteur) :
Résultat attendu : le secret existe avec 3 clés de données (client.crt, client.key, ca.crt).

5.4 Cycle de vie des certificats

PropriétéValeur
Validité du certificat client90 jours
Auto-renouvellementcert-manager renouvelle 15 jours avant expiration
Validité de la CA10 ans
Alertes d’expirationCronJob alerte 30 jours avant expiration (Phase 6)
cert-manager renouvelle automatiquement le certificat sur le cluster AgentEye, mais le certificat renouvelé doit être re-livré au cluster collecteur. Relancez issue-client-cert.sh et ré-appliquez collector-mtls-secret.yaml avant l’expiration de l’ancien certificat. Si vous utilisez --save-to aws-secrets-manager (voir § 5.1b), relancez la même commande. Le script appelle PutSecretValue sur le même secret ; les pods montant le secret via le Secrets Store CSI Driver récupèrent la nouvelle version lors de leur prochain cycle de rotation (par défaut : toutes les heures), sans redémarrage de pod requis.

5.5 Révoquer un certificat

Pour bloquer immédiatement l’accès du collecteur d’un cluster :
Vérification : La commande curl de l’étape 5.2 échoue désormais avec une erreur de handshake TLS.

Phase 6 — Surveillance du renouvellement de certificats (~2 min)

Un CronJob intégré s’exécute toutes les 12 heures (03:00 et 15:00 UTC) et vérifie tous les certificats clients étiquetés agenteye.io/cert-type=mtls-client. Il alerte quand un certificat est dans les 30 jours avant expiration.

6.1 Activer les notifications Slack (optionnel)

Sans ce secret, le CronJob s’exécute quand même et enregistre le statut des certificats dans stdout. Vérification :
Résultat attendu : le secret existe.

6.2 Tester le CronJob

Résultat attendu : une liste de certificats avec leur statut d’expiration. Si le webhook Slack est configuré, vérifiez le canal Slack pour le message d’alerte. En cas d’échec : Vérifiez le RBAC — le ServiceAccount du CronJob a besoin des permissions get, list sur les ressources Certificate de cert-manager. Vérifiez avec : kubectl describe role cert-renewal-check -n agenteye. Nettoyez le job de test :

Phase 7 — Vérification de bout en bout

Cette phase confirme que l’ensemble du pipeline fonctionne : vérification de santé, création de clé, ingestion d’événements et affichage dans le tableau de bord.
Note : Les exemples ci-dessous atteignent le point de terminaison d’ingestion par son adresse LoadBalancer brute (${PUBLIC_IP}) pour des raisons pratiques, d’où l’utilisation de -k ; le certificat serveur est lié à INGEST_DOMAIN, pas à l’IP du LB, donc la vérification du nom d’hôte est ignorée. Le point de terminaison d’ingestion applique le mutual TLS sur chaque chemin, donc chaque appel doit également présenter un certificat client (--cert/--key). Pour valider également le certificat public, ciblez https://ingest.votre-entreprise.example/... au lieu de ${PUBLIC_IP} et supprimez -k.

7.1 Vérification de santé

Résultat attendu : {"status":"ok"} avec HTTP 200.

7.2 Créer des clés collecteur avec portée limitée

La clé admin est pour l’amorçage et la gestion. Créez des clés events:add dédiées pour les collecteurs :
Vérification : La réponse inclut "id", "name": "prod-collector", "permissions": ["events:add"], "created_at". Vérification : Confirmez que la clé apparaît dans la liste des clés :
Résultat attendu : prod-collector apparaît dans la réponse. Voir enterprise-docs/api-keys.md pour la référence complète de gestion des clés.

7.3 Ingérer un événement de test

Résultat attendu : {"accepted":1,"skipped":0} avec HTTP 200. En cas d’échec :
Statut HTTPCause
401Clé API invalide ou manquante
403La clé n’a pas la permission events:add
Erreur de handshake TLSProblème de certificat client — voir le dépannage de la Phase 5

7.4 Vérifier l’événement dans le tableau de bord

Ouvrez https://agenteye.votre-entreprise.example (votre DASHBOARD_DOMAIN) dans un navigateur. Le certificat est de confiance publique, donc il n’y a pas d’avertissement.
Si le LoadBalancer du tableau de bord est restreint par liste d’autorisation IP et que vous ne pouvez pas vous connecter, vérifiez que votre IP est autorisée :
Gardez à l’esprit que Let’s Encrypt renouvelle le certificat du tableau de bord via HTTP-01 sur le port 80, et les plages sources s’appliquent à l’ensemble du LoadBalancer — avant de le restreindre aux plages d’entreprise, coordonnez un solveur DNS-01 avec le support ou les renouvellements échoueront silencieusement.
Vérification : L’événement de test de fumée doit apparaître dans la liste des événements avec la session test et l’agent smoke-test. En cas d’échec : Vérifiez les logs du tableau de bord (kubectl logs -n agenteye -l app=dashboard --tail=50). Vérifiez que AGENTEYE_SERVER_URL et AGENTEYE_API_KEY sont correctement définis.

7.5 Tester le CronJob de sauvegarde

Résultat attendu : Backup created: agenteye-YYYYMMDD-HHMMSS.tar.gz (NNN) dans les logs ; l’archive regroupe le dump Postgres et les tables ClickHouse.
L’étape de téléversement S3 est intégrée dans le CronJob et s’exécute dès que BACKUP_BUCKET est défini (la base fournit une valeur de bucket par défaut). Elle est ignorée uniquement si BACKUP_BUCKET est vide ou littéralement PLACEHOLDER. Pointez-le vers votre propre bucket et accordez au ServiceAccount agenteye-backup un accès en écriture avant de vous y fier (voir la section Sauvegardes ci-dessous).
Nettoyage :

7.6 Provisionner les organisations (multi-tenant)

Ignorez cette section pour un déploiement mono-tenant ; toutes les données résident dans l’org default intégrée et rien ici n’est requis. Si vous exécutez plusieurs tenants isolés, les organisations et leurs membres sont créés avec le CLI agenteye-orgctl. Il est fourni dans l’image server (aux côtés de agenteye-server) et vous l’exécutez dans le Deployment server existant avec kubectl exec; il n’y a pas de pod, Job ou Deployment séparé, ni d’API HTTP ou de bouton dans le tableau de bord pour le cycle de vie des tenants. L’exécuter dans le pod serveur signifie qu’il réutilise le DATABASE_URL, CLICKHOUSE_URL et le ORG_CH_SECRET du §2.6 du pod.
Prérequis : complétez d’abord le §2.6. org create refuse de s’exécuter tant que le serveur utilise encore le ORG_CH_SECRET dev intégré, et l’utilisateur ClickHouse par org qu’il provisionne dépend de ce secret étant fort et stable.
Créer une org et ajouter son premier admin :
Le nouveau membre reçoit un OTP lors de sa première connexion au tableau de bord et travaille ensuite entièrement dans l’interface sous le préfixe d’URL de l’org (ex. /acme/...). Autres commandes (exécutez-les de la même façon avec kubectl -n agenteye exec deploy/server -- agenteye-orgctl …) :
CommandeCe qu’elle fait
org listLister les organisations et leur état.
org rename --slug <slug> --name <new name>Renommer une org (slug inchangé).
org delete --slug <slug>Suppression logique + suppression de l’utilisateur ClickHouse de l’org ; données conservées.
org purge --slug <slug>Effacement irréversible des données ; l’org doit être deleted en premier ; jamais l’org default.
member list --org <slug>Lister les membres et leurs permissions.
member update --org <slug> --email <email> [--set ...] [--add ...] [--remove ...]Modifier les permissions d’un membre.
member remove --org <slug> --email <email>Retirer un membre de l’organisation.
Vérification : org list affiche l’organisation avec le statut active. member list --org acme affiche le membre avec les permissions attendues. Connectez-vous au tableau de bord avec l’adresse e-mail du membre et vérifiez que l’interface utilise le préfixe /acme/ et que les données de l’organisation par défaut ne sont pas visibles.

Dépannage

Si une étape échoue, collectez d’abord les informations de diagnostic suivantes :
Pour une liste complète des problèmes courants et de leurs solutions, consultez le guide de dépannage.

Documentation associée

GuideDescription
Déploiement géréGuide de configuration d’un déploiement géré sur votre cluster Kubernetes
Bien démarrerProcédure complète avec Docker Compose
DéploiementDéploiement Docker, variables d’environnement et configuration
Gestion des tenantsProvisionnement des organisations et membres avec le CLI agenteye-orgctl
Configuration du jeton GitHubGénération du PAT GitHub pour accéder aux artefacts
Installation du collecteurToutes les méthodes d’installation du collecteur
SDK PythonRéférence complète de l’API du SDK
Clés APICréation et gestion des clés API
DépannageProblèmes courants et solutions

Support

Envoyez un e-mail à support@exosphere.host pour obtenir de l’aide sur le déploiement.