Saltar al contenido principal
Esta guía despliega el stack completo de AgentEye en un clúster de Kubernetes dedicado:
  • ClickHouse 24.8 — almacén canónico de análisis de eventos y evaluaciones (StatefulSet con volumen persistente de 100 Gi). Obligatorio: el servidor no arranca sin él.
  • PostgreSQL 16 — almacén relacional/de metadatos para organizaciones, claves de API, usuarios, paneles, consultas guardadas y autenticación (StatefulSet con volumen persistente de 50 Gi)
  • Redis 7.2 — caché compartida opcional y backend de límite de tasa; el servidor y el panel degradan con elegancia si no está disponible
  • AgentEye Server — API en Rust para ingestión de eventos, análisis y gestión de claves (2 réplicas)
  • AgentEye Dashboard — interfaz web en Next.js (2 réplicas)
  • AI assistant (servicio de agente) — asistente opcional de solo lectura en el panel en el puerto 9100; inactivo hasta que se configure un endpoint de LLM
  • Traefik (público) — controlador de ingreso para el tráfico del colector, protegido con mTLS
  • Traefik (panel) — controlador de ingreso para el panel, solo accesible desde VPN/lista de IPs permitidas
  • cert-manager — certificados TLS y CA de mTLS
  • Backup CronJob — volcado diario combinado de PostgreSQL + ClickHouse a las 03:00 UTC
  • Cert Renewal Monitor — alerta cuando los certificados de cliente están próximos a caducar
Tiempo estimado: 60—90 minutos para un primer despliegue. Para el modelo de despliegue gestionado donde Exosphere se encarga de todo esto en tu nombre, consulta enterprise-docs/managed-deployment.md.

Requisitos previos

Ejecuta cada comando de verificación antes de comenzar. Todas las comprobaciones deben pasar.
RequisitoMínimoComando de verificaciónResultado esperado
Clúster de Kubernetes1.27+kubectl versionServer Version >= v1.27
Kustomize (incluido con kubectl)Kustomize v1.14+ (incluido en kubectl 1.27+)kubectl kustomize --helpMuestra texto de uso
Helmv3helm versionVersion:"v3.x.x"
cluster-admin RBACkubectl auth can-i create namespacesyes
StorageClass por defectokubectl get storageclassAl menos una fila marcada como (default)
Soporte de LoadBalancerDepende del proveedor (EKS, GKE, AKS lo soportan por defecto)
GitHub PATecho $AGENTEYE_TOKENNo vacío (ver enterprise-docs/github-token.md)
opensslopenssl versionOpenSSL 1.x o 3.x
Bucket de almacenamiento en la nubePara copias de seguridad de PostgreSQL + ClickHouse (S3, GCS o Azure Blob)
Dimensionamiento del clúster: Mínimo 3 nodos, 4 vCPU / 8 GB de RAM cada uno. Consulta enterprise-docs/managed-deployment.md para los requisitos completos.

Ejecutar todas las comprobaciones a la vez

Estructura del despliegue

El endpoint de ingestión se sirve en un nombre de host que controlas (por ejemplo, ingest.tu-empresa.example). cert-manager solicita un certificado TLS de confianza pública a Let’s Encrypt mediante HTTP-01, de modo que los colectores verifican el certificado del servidor contra el almacén de confianza del sistema, sin necesidad de anclar una CA por cliente. El endpoint del panel funciona de la misma manera: se sirve en un segundo nombre de host que controlas (por ejemplo, agenteye.tu-empresa.example) apuntando al LoadBalancer de Traefik del panel, y cert-manager emite su certificado Let’s Encrypt a través de ese LoadBalancer. Los navegadores obtienen un certificado de confianza sin advertencias.
La emisión y renovación de certificados se validan mediante HTTP-01, por lo que ambos LoadBalancers deben ser accesibles desde internet en el puerto 80. Si necesitas restringir el LoadBalancer del panel por IP, coordina primero con soporte un solver DNS-01, de lo contrario las renovaciones fallarán silenciosamente y el certificado caducará.

Obtener los manifiestos

Verificación:
Resultado esperado: el archivo existe. Si no existe, la clonación ha fallado — verifica tu AGENTEYE_TOKEN. Estructura de directorios:
La base contiene todos los recursos necesarios para un despliegue completo, incluidos los certificados Let’s Encrypt para los dos nombres de host públicos que configuras en la Fase 3.1. Un overlay parchea la base para un entorno específico (por ejemplo, etiquetas de imagen personalizadas, límites de recursos, variables de entorno). El directorio third-party contiene archivos de valores de Helm para infraestructura externa.
Monitorización de salud (opcional): la sonda de disponibilidad del servidor ya refleja el estado de Postgres + ClickHouse, y third-party/robusta/ añade alertas opcionales de fallo de pod en Kubernetes nativas a Slack. Consulta enterprise-docs/health-monitoring.md.

Fase 1 — Infraestructura de terceros (~30 min)

1.1 Instalar cert-manager

cert-manager gestiona los certificados TLS para HTTPS y la CA privada utilizada para los certificados de cliente mTLS.
Verificación:
Resultado esperado: 3 pods en estado Runningcert-manager, cert-manager-cainjector, cert-manager-webhook.
Resultado esperado: al menos certificates.cert-manager.io, clusterissuers.cert-manager.io, issuers.cert-manager.io. Si falla: Los pods en CrashLoopBackOff normalmente indican que los CRDs no se instalaron. Vuelve a ejecutar con --set crds.install=true. Si los pods del webhook fallan en la verificación de disponibilidad, espera 30 segundos y vuelve a comprobar — pueden tardar un momento en arrancar.

1.2 Instalar Traefik — Controlador de ingestión público

Esta instancia de Traefik gestiona el tráfico del colector en un LoadBalancer externo. Termina TLS y aplica mTLS (verificación de certificado de cliente) en el endpoint de ingestión.
Verificación:
Resultado esperado: 1 pod en estado Running.
Resultado esperado: la IngressClass existe (no es la clase por defecto). Si falla: Comprueba kubectl describe pod -n traefik-public <nombre-pod> para errores de descarga de imagen o restricciones de recursos.

1.3 Instalar Traefik — Controlador del panel

Esta instancia de Traefik sirve el panel en un LoadBalancer dedicado, restringido por lista de IPs permitidas.
Esta instancia incluye dos mecanismos de lista de permitidos. Esta guía utiliza values-dashboard.yaml, que restringe el acceso con el campo portable service.loadBalancerSourceRanges. También se proporciona un values-internal.yaml paralelo para entornos AWS que prefieren la anotación service.beta.kubernetes.io/aws-load-balancer-source-ranges. Elige uno y úsalo de forma consistente; los pasos siguientes asumen values-dashboard.yaml.
Antes de instalar, edita third-party/traefik/values-dashboard.yaml para establecer las IPs de origen permitidas. El campo loadBalancerSourceRanges controla qué IPs pueden acceder al panel. Por defecto está configurado como 0.0.0.0/0 (todas las IPs); restrínguelo a tu VPN, oficina o IPs de salida conocidas.

Permitir una sola IP

Permitir múltiples IPs

Añade una entrada por IP o bloque CIDR. Un sufijo /32 coincide con una sola dirección IPv4; un bloque CIDR (por ejemplo, /24) coincide con un rango. Puedes mezclar IPs individuales y rangos libremente:
Consejos para mantener la lista:
  • Mantén una entrada por línea y añade un comentario # breve que identifique el propietario o propósito de cada IP; esto es lo que los futuros operadores usarán para decidir si una entrada sigue siendo necesaria.
  • Usa siempre notación CIDR. Una IP sin sufijo como 203.0.113.10 es rechazada por el proveedor de nube; usa 203.0.113.10/32.
  • Para rangos IPv6, usa el equivalente /128 (dirección única) o un CIDR mayor, por ejemplo 2001:db8::1/128. No todos los proveedores de nube admiten rangos de origen IPv6; consulta la documentación de LoadBalancer de tu proveedor.
  • La lista es un OR: el tráfico se permite si el origen coincide con cualquier entrada.
Después de editar el archivo, procede con helm install a continuación. Si el controlador ya está instalado, ejecuta helm upgrade con las mismas opciones, o parchea el Service en tiempo de ejecución (sección siguiente).

Actualizar la lista de permitidos en tiempo de ejecución

Puedes cambiar las IPs permitidas sin actualizar Helm parcheando el Service directamente. El parche reemplaza la lista completa; incluye siempre todas las IPs que quieras conservar, no solo la nueva. Para reemplazar la lista con un nuevo conjunto de IPs:
Para añadir una IP de forma segura sin perder las entradas existentes, lee primero la lista actual y luego aplica el parche con el conjunto combinado:
Los parches en tiempo de ejecución no se persisten en values-dashboard.yaml. Para mantener el cambio en futuras actualizaciones de Helm, actualiza también el archivo de valores y confírmalo en el repositorio.
Luego instala:
Verificación:
Resultado esperado: 1 pod en estado Running.
Resultado esperado: la IngressClass existe.

1.4 Esperar a los LoadBalancers

Ambas instancias de Traefik necesitan IPs externas antes de continuar.
Verificación: Ambos servicios muestran un EXTERNAL-IP (no <pending>). Si aún están pendientes, espera a que se asigne:
Pulsa Ctrl+C cuando aparezca la IP. La asignación de IP suele tardar entre 2 y 5 minutos. Si falla: <pending> después de 10 minutos normalmente significa que el proveedor de nube no puede aprovisionar un LoadBalancer. Comprueba: etiquetas de subred (EKS requiere kubernetes.io/role/elb), configuración de VPC, cuotas de servicio y que la anotación correcta del LB interno esté configurada para la instancia interna.

Fase 2 — Crear secretos (~10 min)

Todos los secretos se crean manualmente antes de desplegar la aplicación. Esto garantiza que los valores sensibles nunca aparezcan en los archivos de manifiesto.

2.1 Crear el namespace

Verificación:
Resultado esperado: estado Active.

2.2 Secreto de extracción de imagen

Este secreto autentica con ghcr.io para extraer las imágenes de contenedor de AgentEye. Consulta enterprise-docs/github-token.md para saber cómo generar tu PAT.
Verificación:
Resultado esperado: kubernetes.io/dockerconfigjson. Verificación profunda — comprueba que el token puede extraer imágenes realmente: Usa la etiqueta de imagen server fijada en el kustomization.yaml de tu overlay (actualmente v0.0.1-beta.48 tanto en el overlay acme incluido como en el despliegue base). Sustituye la etiqueta en el comando siguiente por la que estés desplegando para que esta comprobación no quede desactualizada entre versiones:
Resultado esperado: ok impreso en los logs. Si falla: ErrImagePull o 401 Unauthorized significa que el PAT no es válido o no tiene el permiso read:packages. Revisa enterprise-docs/github-token.md.

2.3 Credenciales de PostgreSQL

Importante: Usamos -hex (no -base64) para generar la contraseña. La salida en base64 puede contener +, / y =, que rompen la cadena de conexión DATABASE_URL. Consulta enterprise-docs/troubleshooting.md para más detalles.
Guarda POSTGRES_PASSWORD en tu gestor de secretos inmediatamente. La necesitarás si alguna vez restauras desde una copia de seguridad o te conectas directamente a la base de datos.
Verificación:
Resultado esperado: el secreto existe.
Resultado esperado: 48 (24 bytes hexadecimales = 48 caracteres).

2.4 Clave de API de administrador

La clave de administrador es la credencial de arranque inicial. El servidor la inserta o actualiza en cada inicio con todos los permisos. Úsala para crear claves de colector con permisos restringidos en la Fase 7. Consulta enterprise-docs/api-keys.md para el modelo completo de permisos.
Guarda ADMIN_KEY en tu gestor de secretos inmediatamente.
Verificación:
Resultado esperado: el secreto existe.

2.5 Configuración de autenticación (inicio de sesión en el panel)

El panel utiliza email + OTP para el inicio de sesión de usuarios. Sin este secreto, el servidor sigue arrancando y la ruta de API de ADMIN_KEY sigue funcionando, pero ningún usuario puede iniciar sesión a través de la interfaz de usuario. Todas las claves están referenciadas como optional: true en el manifiesto base, por lo que los secretos parciales (o ningún secreto) son válidos; el servidor recurre a los valores predeterminados documentados. Agrupar todo en un único secreto agenteye-auth permite rotar toda la superficie de autenticación en un solo lugar.
ClavePropósito
ADMIN_EMAILUsuario administrador inicial. Se inserta o actualiza en cada inicio con todos los permisos y está protegido contra eliminación/edición de permisos desde el panel. Sin él, no se crea ningún administrador y el primer inicio de sesión es imposible.
ALLOWED_EMAILSLista de permitidos separada por comas. Admite direcciones exactas (user@example.com) y comodines de dominio (*@example.com). Sin él, ningún usuario puede iniciar sesión ni ser creado.
SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_FROMRelay SMTP para enviar códigos OTP. Si SMTP_HOST no está configurado, los códigos OTP se registran en stdout del servidor en lugar de enviarse por email (útil para pruebas de humo en el primer arranque). Proporciona todas las claves SMTP juntas para el envío real de emails.
SMTP_TLSUno de starttls (por defecto), tls o none.
DEFAULT_ORG_NAME, DEFAULT_ORG_SLUGOpcional. Asigna un nombre de visualización amigable y un slug de URL a la organización default integrada, de modo que viva en, por ejemplo, /acme en lugar de /default. Se aplica solo en el primer arranque; una vez que renombres la organización con agenteye-orgctl org rename (ver §7.6) se ignoran. El slug debe tener entre 1 y 40 caracteres alfanuméricos en minúsculas con guiones internos simples. Déjalos sin configurar para mantener el default genérico.
Guarda las credenciales SMTP en tu gestor de secretos.
Verificación:
Resultado esperado: las claves que has configurado aparecen en la salida.

2.6 Clave de aislamiento de organizaciones multi-tenant (opcional)

Omite esto para un despliegue de un solo tenant; el servidor funciona con un valor de desarrollo integrado y sirve correctamente a la organización default. Antes de crear una segunda organización, configura un ORG_CH_SECRET fuerte y estable: la contraseña de ClickHouse de cada organización se deriva como HMAC(ORG_CH_SECRET, org_id), de modo que el valor de desarrollo por defecto, que es público, generaría credenciales por organización derivables públicamente. El comando agenteye-orgctl org create (ver §7.6 Aprovisionar organizaciones) se niega a ejecutarse mientras el servidor siga usando el valor de desarrollo integrado.
El servidor lo lee a través de una referencia secretKeyRef opcional, por lo que un clúster de un solo tenant que nunca lo crea sigue arrancando normalmente. Mantén el valor estable e idéntico en todas las réplicas; rotarlo invalida la contraseña de ClickHouse derivada de cada organización hasta que la reconciliación al arrancar vuelva a aprovisionar los usuarios (un reinicio gradual con el valor consistente en todas partes lo soluciona). Consulta deploy/base/server/secret.example.yaml.
Guarda ORG_CH_SECRET en tu gestor de secretos y no lo rotes sin motivo.

2.7 Verificar todos los secretos

Salida esperada (entre los secretos predeterminados):
Los cuatro secretos principales (agenteye-admin-key, agenteye-auth, agenteye-image-pull, agenteye-postgres) deben estar presentes antes de continuar. agenteye-org-ch-secret solo es necesario para despliegues multi-tenant (ver §2.6).

Fase 3 — Desplegar la aplicación (~5 min)

3.1 Configurar los nombres de host públicos

cert-manager necesita los nombres de host de ingestión y del panel antes de poder solicitar sus certificados Let’s Encrypt. Copia la plantilla y configura ambos:
domain.env está en el gitignore; permanece local a cada despliegue. La compilación de kustomize falla con un error claro si falta cualquiera de las claves.
El DNS debe resolver primero. No es necesario apuntar el DNS a los LBs todavía (no existen hasta que se complete la Fase 1.2), pero la emisión de ACME en el paso 3.2 reintentará hasta que cada nombre de host resuelva a su LoadBalancer. Puedes configurar el DNS ahora (usando los nombres de host de LB capturados en la Fase 1.4) o continuar y añadir los registros en la Fase 4.

3.2 Aplicar los manifiestos

Aplica la base directamente para una instalación nueva, o un overlay si has creado uno para este entorno (los overlays solo fijan etiquetas de imagen, variables de entorno y límites de recursos; heredan los certificados y el enrutamiento de la base):
El overlay incluye la base automáticamente; aplica uno, no ambos.

3.3 Esperar a los pods

La espera está limitada a los pods del plano de datos principales. Los pods opcionales agent (asistente de IA) y redis arrancan junto a ellos; el asistente permanece inactivo hasta que le proporciones su endpoint de LLM (ver enterprise-docs/assistant.md), y Redis es una caché de mejor esfuerzo, por lo que ninguno de los dos necesita estar listo para que la plataforma sirva tráfico. Verificación:
Resultado esperado (los pods opcionales agent y redis también aparecen y alcanzan el estado Running):
Si falla:
Estado del podCausa probableComando de diagnóstico
ImagePullBackOffSecreto de extracción de imagen o PAT incorrectokubectl describe pod <nombre> -n agenteye
CrashLoopBackOffVariables de entorno incorrectas (por ejemplo, DATABASE_URL)kubectl logs <nombre> -n agenteye
PendingCPU/memoria insuficiente o sin nodoskubectl describe pod <nombre> -n agenteye (revisa Events)

3.4 Verificar el almacenamiento

Resultado esperado, ambos con estado Bound:
PVCCapacidadRespaldo
postgres-data-postgres-050GiAlmacén relacional/de metadatos de PostgreSQL
clickhouse-data-clickhouse-0100GiAlmacén de análisis de eventos + evaluaciones de ClickHouse
También aparece un PVC redis-data-redis-0 (1 Gi) para la caché opcional. Si falla: Pending significa que ninguna StorageClass puede aprovisionar el volumen. Comprueba kubectl get storageclass y asegúrate de que existe una por defecto. Para producción, sobrescribe el volumen de ClickHouse en tu overlay con una StorageClass de SSD rápida (por ejemplo, gp3 en AWS, pd-ssd en GCP); el rendimiento de compactación se degrada en discos lentos.

3.5 Verificar los certificados

Resultado esperado: 3 certificados, todos con Ready: True:
NombreEmisorPropósito
mtls-caselfsignedCA privada para emitir certificados de cliente mTLS (validez de 10 años)
ingest-tlsletsencrypt-prodCertificado TLS público para el endpoint de ingestión (90 días, renovación automática)
dashboard-tlsletsencrypt-prodCertificado TLS público para el panel (90 días, renovación automática)
Si ingest-tls o dashboard-tls no están listos: Ejecuta kubectl describe certificate <nombre> -n agenteye y lee los Events. Las causas más comunes son:
  • DNS aún no apunta al LB. Let’s Encrypt resuelve el nombre de host y accede al puerto 80 para validar — INGEST_DOMAIN debe resolver al LB público y DASHBOARD_DOMAIN al LB del panel. Hasta que se propague el CNAME/Alias, la orden permanece en pending. Una vez que el DNS sea correcto, cert-manager reintentará automáticamente (no es necesario eliminar el Certificate).
  • Nombre de host no sustituido. Si dnsNames sigue mostrando INGEST_DOMAIN_PLACEHOLDER / DASHBOARD_DOMAIN_PLACEHOLDER, omitiste el paso 3.1 — crea base/certificates/domain.env y vuelve a aplicar.
  • Traefik del panel no puede servir el desafío (solo dashboard-tls). La instancia de Traefik del panel debe instalarse con el archivo de valores incluido (Fase 1.2), que habilita el proveedor de Ingress con alcance limitado que sirve el solver HTTP-01 de cert-manager. Una instancia instalada sin él deja el desafío sin ruta y la orden en pending indefinidamente.
Si mtls-ca no está listo: cert-manager en sí está en mal estado. Revisa los pods de cert-manager del paso 1.1.

3.6 Verificar los CronJobs

Resultado esperado:
NombreProgramaciónPropósito
agenteye-backup0 3 * * *Copia de seguridad diaria de Postgres + ClickHouse a las 03:00 UTC
cert-renewal-check0 3,15 * * *Alertas de caducidad de certificados a las 03:00 y 15:00 UTC

3.7 Verificar que el servidor arrancó correctamente

Verificación: Busca una línea de inicio que indique que el servidor está escuchando en el puerto 8080. No debe haber errores de conexión a la base de datos (el servidor requiere que tanto PostgreSQL como ClickHouse sean accesibles antes de reportar estado Ready). Si falla: La causa más común es un POSTGRES_PASSWORD que contiene caracteres no seguros para URLs que rompen la DATABASE_URL. Consulta enterprise-docs/troubleshooting.md.

3.8 Verificar que el panel se conectó al servidor

Verificación: Busca Ready en la salida sin errores ECONNREFUSED ni similares. Si falla: Comprueba que el Service server existe (kubectl get svc server -n agenteye) y que AGENTEYE_SERVER_URL está configurado como http://server:8080 en el despliegue del panel.

Fase 4 — Acceso de red (~5 min)

4.1 Obtener las direcciones de los LoadBalancers

En AWS EKS, los LoadBalancers devuelven un nombre de host en lugar de una IP. Reemplaza .ip por .hostname en los comandos anteriores.
Verificación:
Ambos deben ser no vacíos.

4.2 Apuntar el DNS a los LoadBalancers

Crea registros DNS para que los nombres de host de base/certificates/domain.env resuelvan a sus LoadBalancers — INGEST_DOMAIN al LB de Traefik público, DASHBOARD_DOMAIN al LB de Traefik del panel:
  • AWS Route 53: registro A con Alias = Yes, destino = el nombre de host del LB. No uses A → IP simple; las IPs de ELB rotan.
  • Cualquier otro proveedor: CNAME desde el nombre de host al nombre de host del LB.
Verifica:
Deben devolver las mismas direcciones que $PUBLIC_IP y $INTERNAL_IP respectivamente (o, en EKS, resolver a los mismos nombres de host *.elb.amazonaws.com). Una vez que el DNS resuelva, cert-manager completará las órdenes ACME pendientes de la Fase 3.5 en un minuto. Vuelve a ejecutar kubectl get certificates -n agenteye hasta que tanto ingest-tls como dashboard-tls muestren Ready: True.

4.3 Acceder al endpoint de ingestión

El endpoint de ingestión público aplica mutual TLS, por lo que cada solicitud (incluyendo /health) debe presentar un certificado de cliente. Emites tu primer certificado de cliente en la Fase 5; si ya tienes uno, verifica la accesibilidad ahora:
Resultado esperado: {"status":"ok"}. No se necesita -k — el certificado del servidor encadena a una CA pública para INGEST_DOMAIN, por lo que se valida contra el almacén de confianza del sistema. Accede al endpoint de ingestión por su nombre de host INGEST_DOMAIN (que coincide con el certificado emitido), no por la IP/nombre de host bruto del LoadBalancer. El endpoint del panel se sirve en DASHBOARD_DOMAIN con un certificado de confianza pública y no está detrás de mTLS, por lo que no se necesita -k ni certificado de cliente:
Accede al panel por su nombre de host, no por la dirección bruta del LB — el certificado está vinculado a DASHBOARD_DOMAIN, por lo que la dirección bruta mostrará un error de nombre en el certificado. Si falla: Si curl se cuelga, comprueba que el LB es accesible desde tu máquina (VPN, grupos de seguridad, reglas de firewall). Un error de handshake certificate required en el nombre de host de ingestión significa que no se presentó ningún certificado de cliente; completa primero la Fase 5. Un error de validación TLS en el nombre de host de ingestión significa que el certificado del servidor aún no ha terminado de emitirse; vuelve a la Fase 3.5 y resuelve el problema allí.

Fase 5 — Emitir certificados de cliente mTLS (~10 min por clúster)

Los colectores se autentican con dos factores: un certificado de cliente (capa de transporte, demuestra que la solicitud proviene de un clúster autorizado) y una clave de API (capa de aplicación, demuestra que la solicitud es de un colector con permiso events:add). Una clave filtrada es inútil sin el certificado; un certificado robado es inútil sin una clave válida.

5.1 Emitir un certificado

Cada clúster que ejecuta colectores necesita su propio certificado de cliente. Desde el directorio de manifiestos:
Reemplaza <nombre-clúster> con un identificador significativo (por ejemplo, us-east-1-prod, staging). Verificación: El script imprime ==> Done! y lista los archivos de salida.
Resultado esperado: Ready: True. Archivos de salida en issued/<nombre-clúster>/:
ArchivoPropósito
client.crtCertificado de cliente (validez de 90 días)
client.keyClave privada del cliente
ca.crtCertificado de CA para verificación del servidor
collector-mtls-secret.yamlSecret de Kubernetes listo para aplicar en el clúster del colector

5.1b Entrega alternativa: AWS Secrets Manager

Si el consumidor del certificado es un Pod de Kubernetes que necesita client.crt y client.key en disco — el caso típico cuando ejecutas el agenteye-collector como sidecar en tu pod de aplicación — envía el bundle del certificado a AWS Secrets Manager. El pod de la aplicación lo monta entonces mediante el Secrets Store CSI Driver con IRSA, y la rotación del certificado es completamente automática.
En cada reejecución (renovación), el script llama a PutSecretValue en el mismo secreto, de modo que el ARN y el nombre permanecen estables. El CSI Driver recoge la nueva versión en su próximo ciclo de rotación y reescribe los archivos dentro del pod. Requisitos previos:
  • CLI de aws v2 autenticado en tu cuenta de AWS.
  • jq instalado.
  • Variable de entorno AWS_REGION configurada.
  • Permisos IAM en tu identidad (limita Resource a arn:aws:secretsmanager:<región>:<cuenta>:secret:agenteye/mtls-client/*):
    • secretsmanager:CreateSecret
    • secretsmanager:DescribeSecret
    • secretsmanager:PutSecretValue
    • secretsmanager:TagResource
Lo que hace el script en este modo:
PasoAcción
1Emite o reextrae el certificado mediante cert-manager (igual que el modo por defecto).
2Llama a DescribeSecret en agenteye/mtls-client/<nombre-clúster> para decidir si crear o actualizar.
3En la primera ejecución: CreateSecret con un payload JSON de tres claves (client.crt, client.key, ca.crt), etiquetado con AgentEyeCluster=<nombre-clúster>. En ejecuciones posteriores: PutSecretValue para publicar una nueva versión; etiqueta actualizada mediante TagResource.
4Elimina issued/<nombre-clúster>/ solo después de una carga exitosa. En caso de error, el directorio se conserva para poder reintentar.
Si el secreto está programado para eliminación, el script falla con un mensaje claro indicándote que ejecutes aws secretsmanager restore-secret --secret-id agenteye/mtls-client/<nombre-clúster> antes de reintentar. Para la configuración completa del pod (SecretProviderClass, configuración de IRSA, comportamiento de rotación y solución de problemas), consulta enterprise-docs/single-pod-deployment.md.

5.2 Verificar que el certificado funciona

Prueba el certificado emitido contra el ingreso mTLS:
Resultado esperado: {"status":"ok"} Si falla:
ErrorCausaSolución
certificate requiredEl certificado no se está presentandoVerifica las rutas de archivo en el comando curl
bad certificateDiscrepancia de CAVerifica que mtls-ca-issuer emitió el certificado: kubectl describe certificate mtls-client-<nombre> -n agenteye
connection refusedNombre de host incorrecto o LB no accesibleComprueba /etc/hosts o DNS

5.3 Entregar al clúster del colector

Envía collector-mtls-secret.yaml al equipo que opera el clúster del colector. Ellos lo aplican:
Luego configura el colector para montar el secreto y usar las rutas del certificado:
Consulta enterprise-docs/collector-installation.md para la configuración completa del colector, incluyendo los montajes de volumen de Kubernetes. Verificación (en el clúster del colector):
Resultado esperado: el secreto existe con 3 claves de datos (client.crt, client.key, ca.crt).

5.4 Ciclo de vida del certificado

PropiedadValor
Validez del certificado de cliente90 días
Renovación automáticacert-manager renueva 15 días antes de la caducidad
Validez de la CA10 años
Alertas de caducidadCronJob alerta 30 días antes de la caducidad (Fase 6)
cert-manager renueva automáticamente el certificado en el clúster de AgentEye, pero el certificado renovado debe volver a entregarse al clúster del colector. Vuelve a ejecutar issue-client-cert.sh y vuelve a aplicar collector-mtls-secret.yaml antes de que caduque el certificado antiguo. Si estás usando --save-to aws-secrets-manager (ver §5.1b), vuelve a ejecutar el mismo comando. El script llama a PutSecretValue en el mismo secreto; los pods que montan el secreto mediante el Secrets Store CSI Driver recogen la nueva versión en su próximo ciclo de rotación (por defecto: cada hora), sin necesidad de reiniciar el pod.

5.5 Revocar un certificado

Para bloquear inmediatamente el acceso del colector de un clúster:
Verificación: El comando curl del paso 5.2 ahora falla con un error de handshake TLS.

Fase 6 — Monitorización de renovación de certificados (~2 min)

Un CronJob integrado se ejecuta cada 12 horas (03:00 y 15:00 UTC) y comprueba todos los certificados de cliente etiquetados con agenteye.io/cert-type=mtls-client. Alerta cuando algún certificado está a menos de 30 días de caducar.

6.1 Habilitar notificaciones de Slack (opcional)

Sin este secreto, el CronJob sigue ejecutándose y registra el estado de los certificados en stdout. Verificación:
Resultado esperado: el secreto existe.

6.2 Probar el CronJob

Resultado esperado: una lista de certificados con su estado de caducidad. Si el webhook de Slack está configurado, comprueba el canal de Slack para ver el mensaje de alerta. Si falla: Comprueba el RBAC — la ServiceAccount del CronJob necesita permisos get, list en los recursos Certificate de cert-manager. Verifica con: kubectl describe role cert-renewal-check -n agenteye. Limpia el job de prueba:

Fase 7 — Verificar el funcionamiento end-to-end

Esta fase confirma que toda la cadena funciona: comprobación de salud, creación de clave, ingestión de eventos y visualización en el panel.
Nota: Los ejemplos siguientes acceden al endpoint de ingestión por su dirección bruta del LoadBalancer (${PUBLIC_IP}) por comodidad, por lo que se pasa -k; el certificado del servidor está vinculado a INGEST_DOMAIN, no a la IP del LB, por lo que se omite la verificación del nombre de host. El endpoint de ingestión aplica mutual TLS en todas las rutas, por lo que cada llamada también debe presentar un certificado de cliente (--cert/--key). Para validar también el certificado público, apunta a https://ingest.tu-empresa.example/... en lugar de ${PUBLIC_IP} y elimina el -k.

7.1 Comprobación de salud

Resultado esperado: {"status":"ok"} con HTTP 200.

7.2 Crear claves de colector con permisos restringidos

La clave de administrador es para el arranque inicial y la gestión. Crea claves dedicadas con permiso events:add para los colectores:
Verificación: La respuesta incluye "id", "name": "prod-collector", "permissions": ["events:add"], "created_at". Verificación: Comprueba que la clave aparece en la lista de claves:
Resultado esperado: prod-collector aparece en la respuesta. Consulta enterprise-docs/api-keys.md para la referencia completa de gestión de claves.

7.3 Ingestar un evento de prueba

Resultado esperado: {"accepted":1,"skipped":0} con HTTP 200. Si falla:
Estado HTTPCausa
401Clave de API inválida o ausente
403La clave no tiene el permiso events:add
Error de handshake TLSProblema con el certificado de cliente — ver solución de problemas de la Fase 5

7.4 Verificar que el evento aparece en el panel

Abre https://agenteye.tu-empresa.example (tu DASHBOARD_DOMAIN) en un navegador. El certificado tiene confianza pública, por lo que no hay advertencias.
Si el LoadBalancer del panel está restringido por lista de IPs y no puedes conectarte, verifica que tu IP está permitida:
Ten en cuenta que Let’s Encrypt renueva el certificado del panel mediante HTTP-01 en el puerto 80, y los rangos de origen se aplican a todo el LoadBalancer — antes de restringirlo a rangos corporativos, coordina un solver DNS-01 con soporte o las renovaciones fallarán silenciosamente.
Verificación: El evento de prueba debe aparecer en la lista de eventos con la sesión test y el agente smoke-test. Si falla: Comprueba los logs del panel (kubectl logs -n agenteye -l app=dashboard --tail=50). Verifica que AGENTEYE_SERVER_URL y AGENTEYE_API_KEY están configurados correctamente.

7.5 Probar el CronJob de copia de seguridad

Resultado esperado: Backup created: agenteye-YYYYMMDD-HHMMSS.tar.gz (NNN) en los logs; el archivo agrupa el volcado de Postgres y las tablas de ClickHouse.
El paso de carga a S3 viene integrado en el CronJob y se ejecuta siempre que BACKUP_BUCKET esté configurado (la base incluye un valor de bucket por defecto). Solo se omite cuando BACKUP_BUCKET está vacío o literalmente es PLACEHOLDER. Apúntalo a tu propio bucket y concede al ServiceAccount agenteye-backup acceso de escritura antes de confiar en él (ver la sección de Copias de seguridad más abajo).
Limpieza:

7.6 Aprovisionar organizaciones (multi-tenant)

Omite esto para un despliegue de un solo tenant; todos los datos viven en la organización default integrada y nada aquí es necesario. Si ejecutas múltiples tenants aislados, las organizaciones y sus miembros se crean con la CLI agenteye-orgctl. Esta herramienta se incluye dentro de la imagen del servidor (junto a agenteye-server) y se ejecuta dentro del Deployment server existente con kubectl exec; no hay pod, Job ni Deployment separado, y no hay API HTTP ni botón en el panel para el ciclo de vida del tenant. Ejecutarla en el pod del servidor significa que reutiliza la DATABASE_URL, CLICKHOUSE_URL y el ORG_CH_SECRET del §2.6 del pod.
Requisito previo: completa primero el §2.6. org create se niega a ejecutarse mientras el servidor siga usando el ORG_CH_SECRET de desarrollo integrado, y el usuario de ClickHouse por organización que aprovisiona depende de que ese secreto sea fuerte y estable.
Crear una organización y añadir su primer administrador:
El nuevo miembro recibe un OTP en su primer inicio de sesión en el panel y luego trabaja completamente en la interfaz de usuario bajo el prefijo de URL de la organización (por ejemplo, /acme/...). Otros comandos (se ejecutan de la misma manera con kubectl -n agenteye exec deploy/server -- agenteye-orgctl …):
ComandoQué hace
org listLista las organizaciones y su estado.
org rename --slug <slug> --name <nuevo nombre>Renombra una organización (el slug no cambia).
org delete --slug <slug>Eliminación suave + eliminación del usuario de ClickHouse de la organización; los datos se conservan.
org purge --slug <slug>Borrado irreversible de datos; la organización debe estar eliminada primero; nunca para la organización default.
member list --org <slug>Lista los miembros y sus permisos.
member update --org <slug> --email <email> [--set ...] [--add ...] [--remove ...]Cambia los permisos de un miembro.
member remove --org <slug> --email <email>Elimina un miembro de la organización.
Los conjuntos de permisos integrados son admin, standard y read-only. Las claves de API por organización se siguen creando en el panel/API por los miembros de la organización (el §7.2 muestra la API de claves); solo el ciclo de vida de la organización y los miembros es exclusivo para operadores. Referencia completa y un ejemplo detallado: enterprise-docs/tenant-management.md.

Lista de verificación post-despliegue

Usa esta lista para confirmar que todo funciona. Cada elemento debe estar comprobado antes de entregar a los colectores.
  • Todos los pods en estado Running en el namespace agenteye
  • PVC de PostgreSQL vinculado (50 Gi) y PVC de ClickHouse vinculado (100 Gi)
  • Los 3 certificados con Ready: True
  • Ambas IPs de LoadBalancer asignadas
  • DNS o /etc/hosts configurado y resolviendo
  • /health devuelve HTTP 200
  • Prueba de certificado mTLS superada (curl con certificado de cliente a /health)
  • Clave de colector con permisos restringidos creada y probada
  • Evento de prueba ingestado (accepted: 1)
  • Evento visible en el panel
  • Certificados de cliente emitidos para cada clúster de colector
  • CronJob de copia de seguridad probado manualmente
  • CronJob de renovación de certificados probado manualmente
  • Webhook de Slack para alertas de certificados configurado (opcional)
  • Bucket de copia de seguridad configurado en el overlay (ver más abajo)
  • Clave de administrador y contraseña de Postgres guardadas en el gestor de secretos

Copias de seguridad

Un único CronJob agenteye-backup se ejecuta diariamente a las 03:00 UTC. Vuelca ambos almacenes: PostgreSQL (estado relacional) y ClickHouse (las tablas de análisis events + evaluations), en un archivo comprimido en el pod, y luego lo carga en el almacenamiento de objetos que configures en tu overlay. Cada ejecución produce un objeto, agenteye-<timestamp>.tar.gz, que se descomprime en:
ClickHouse se lee a través de su API HTTP (el mismo endpoint que usa el servidor), por lo que el job no necesita