Skip to content

Repository files navigation

Laboratorio local: k3d + Terraform + Helm + observabilidad LGTM

Entorno local sobre Docker, provisionado con Terraform, para probar despliegues de aplicaciones y observabilidad. Terraform crea el cluster k3d, los namespaces y el stack LGTM; deploy.ps1 aplica los manifiestos declarativos de las aplicaciones desde este mismo repositorio.

Sobre esa base corre un flujo de diagnóstico agéntico de incidentes: una alerta de Grafana se enriquece con la traza de error y se envía a un agente que genera un análisis de causa raíz (RCA) con un LLM local, leyendo el código fuente real (ADR-0006).

Demo

Flujo de diagnóstico agéntico: una excepción en una aplicación dispara la alerta y el agente genera el informe de causa raíz

Una aplicación provoca un error 500 → Grafana dispara la alerta → el alert-enricher adjunta la traza de error de Tempo → el ai-sre-agent genera el informe de causa raíz con Ollama.

Arquitectura

Terraform + Helm                         Kubernetes (k3d)
  ├─ cluster k3d (k3s)                     ├─ platform: Grafana, Loki, Tempo, Mimir, OTel Collector
  ├─ namespaces platform/workloads         └─ workloads: nginx, podinfo, telemetrygen, alert-enricher
  └─ charts de observabilidad

scripts/deploy.ps1
  ├─ kubectl apply -k gitops/observability/mimir
  └─ kubectl apply -k gitops/workloads/<app>

Telemetría: apps --OTLP--> OTel Collector --> Loki / Tempo / Mimir
                                      ^-- Grafana consulta los tres

Diagnóstico: Grafana (alerta) --> alert-enricher --> ai-sre-agent (host)
  • Terraform gestiona la infraestructura y los cuatro charts Helm de observabilidad.
  • Los manifiestos Kubernetes de las aplicaciones viven en gitops/ y se aplican localmente con kubectl apply -k; siguen estando versionados y son reproducibles.
  • build.ps1 construye e importa las imágenes locales al cluster. No se usa ningún registry.
  • alert-enricher reenvía el webhook al proceso ai-sre-agent que corre en el host.

Requisitos

  • Windows + Docker Desktop (backend Linux/WSL2) en marcha.
  • terraform, k3d, helm, kubectl y git.
  • Conexión a internet en el primer arranque para descargar providers, charts e imágenes.

up.ps1 instala k3d, Helm y Terraform con winget si faltan.

Uso

Desde la raíz del repositorio:

# Crear el cluster, namespaces y observabilidad, y aplicar los manifiestos locales
./scripts/up.ps1

# Construir/importar una imagen local
./scripts/build.ps1 0.1.2 alert-enricher

# Reaplicar Mimir y los workloads después de importar imágenes o editar manifiestos
./scripts/deploy.ps1

# Destruir el laboratorio
./scripts/down.ps1

Para ejecutar Terraform directamente:

terraform -chdir=infrastructure/terraform init
terraform -chdir=infrastructure/terraform apply -target="null_resource.k3d_cluster"
terraform -chdir=infrastructure/terraform apply
./scripts/deploy.ps1

El primer apply crea el cluster para que k3d escriba el kubeconfig. El segundo instala los charts de observabilidad. deploy.ps1 aplica después Mimir y las aplicaciones.

Acceso

Los Ingress usan Traefik, incluido en k3s, y el puerto host 8080.

Servicio URL Credenciales
Grafana http://grafana.localhost:8080 admin / admin
podinfo http://podinfo.localhost:8080 —
nginx http://nginx.localhost:8080 —

Si el DNS *.localhost no está disponible, prueba contra http://127.0.0.1:8080 enviando el encabezado Host correspondiente.

Alternativa por port-forward:

kubectl -n platform port-forward svc/grafana 3000:80  # http://localhost:3000

Crear y desplegar una aplicación nueva

El proyecto separa el código de una aplicación de su definición Kubernetes:

  • apps/<app>/ contiene el código y el Dockerfile cuando la imagen se construye localmente.
  • gitops/workloads/<app>/ contiene los manifiestos Kustomize que se aplican al namespace workloads.

Una aplicación puede usar una imagen publicada —por ejemplo nginx o podinfo— o una imagen local —como alert-enricher—. En ambos casos necesita su carpeta de manifiestos.

1. Crear los manifiestos Kubernetes

Crea gitops/workloads/<app>/kustomization.yaml y, como mínimo, un Deployment cuyo nombre sea exactamente <app>:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: workloads
resources:
  - deployment.yaml
  - service.yaml
  # - ingress.yaml       # solo si debe exponerse fuera del cluster

El deployment.yaml debe contener metadata.name: <app> y la imagen que se quiere ejecutar. Añade service.yaml para el acceso interno y ingress.yaml si la aplicación necesita una URL desde el host. Puedes tomar gitops/workloads/podinfo/ como ejemplo.

scripts/deploy.ps1 descubre automáticamente las carpetas que contienen kustomization.yaml. Aplica todos los workloads antes de esperar sus rollouts, para permitir dependencias entre ellos. El Deployment debe llamarse igual que su carpeta.

2. Preparar la imagen

Para una imagen publicada, pon su referencia en deployment.yaml y asegúrate de que el cluster puede descargarla:

image: ghcr.io/empresa/<app>:1.0.0
imagePullPolicy: IfNotPresent

Para una aplicación con código local, crea apps/<app>/Dockerfile y construye e importa la imagen en el cluster k3d:

./scripts/build.ps1 -Tag 0.1.0 -App <app> -Cluster lab

El script construye apps/<app>/, genera la imagen <app>:0.1.0 y la importa con k3d image import. El tag utilizado debe coincidir con el image: del Deployment:

image: <app>:0.1.0

Si modificas el código, usa un tag nuevo (0.1.1, 0.1.2, etc.), actualiza el Deployment y vuelve a importar la imagen. Esto es importante porque los deployments usan imagePullPolicy: IfNotPresent.

3. Aplicar la aplicación

Con Docker Desktop y el cluster levantados, ejecuta:

./scripts/deploy.ps1

El script prepara PostgreSQL y NATS, reaplica Mimir y todas las aplicaciones descubiertas, y espera a que cada Deployment termine su rollout.

Comprueba el resultado con:

kubectl --context k3d-lab -n workloads get pods,svc,ingress
kubectl --context k3d-lab -n workloads rollout status deployment/<app>
kubectl --context k3d-lab -n workloads logs deployment/<app>

Si tiene Ingress, prueba http://<host>.localhost:8080. Si solo tiene Service, utiliza un port-forward:

kubectl --context k3d-lab -n workloads port-forward svc/<app> 8081:<puerto-del-service>

4. Levantar el laboratorio desde cero

Para un cluster ya preparado, ./scripts/up.ps1 crea o actualiza la infraestructura y termina aplicando los workloads. Una imagen local debe estar importada antes de esperar el rollout de su Deployment. Si el cluster se ha recreado y la aplicación usa una imagen local, ejecuta el flujo en dos fases:

# Crear primero el cluster para que exista el destino de k3d image import
terraform -chdir=infrastructure/terraform init
terraform -chdir=infrastructure/terraform apply `
  -target="null_resource.k3d_cluster" `
  -auto-approve

# En Windows, corrige el endpoint si k3d ha escrito host.docker.internal y no conecta
$server = kubectl config view --context k3d-lab -o jsonpath='{.clusters[0].cluster.server}'
if ($server -match "host\.docker\.internal") {
  kubectl config set-cluster k3d-lab --server=$server.Replace("host.docker.internal", "127.0.0.1")
}

# Construir e importar las imágenes locales
./scripts/build.ps1 -Tag 0.1.0 -App <app> -Cluster lab

# Instalar observabilidad y aplicar los workloads
terraform -chdir=infrastructure/terraform apply -auto-approve
./scripts/deploy.ps1

5. Eliminar una aplicación

El despliegue local no hace pruning automático. Antes de borrar la carpeta de una aplicación, elimina sus recursos del cluster:

kubectl --context k3d-lab delete -k gitops/workloads/<app> --ignore-not-found

Después elimina gitops/workloads/<app>/ y apps/<app>/ si existe.

Verificar la observabilidad

  • En Grafana, los datasources Mimir, Loki y Tempo deben dar OK.
  • En Explore verás trazas, métricas y logs generados por telemetrygen.
  • La regla ErrorEnCualquierServicio envía las alertas al alert-enricher, que busca la traza correspondiente en Tempo.

Estructura

infrastructure/terraform/  Cluster, namespaces y charts Helm de observabilidad
infrastructure/helm-values/ Valores de Grafana, Loki, Tempo y OTel Collector
infrastructure/k3d/        Definición del cluster k3d
gitops/workloads/           Manifiestos Kustomize de las aplicaciones
gitops/observability/mimir/ Manifiestos Kustomize de Mimir
apps/                       Código + Dockerfile de las apps incluidas
scripts/                    up.ps1 / down.ps1 / build.ps1 / deploy.ps1
docs/adr/                   Decisiones de arquitectura y contexto histórico

Las decisiones de diseño y los problemas resueltos están documentados en docs/adr/. Los ADRs que mencionan ArgoCD describen una etapa histórica del proyecto y se conservan como registro de decisiones anteriores.

La evolución prevista de los microservicios de BlastMap está en docs/ROADMAP-MICROSERVICIOS.md.

Los tres servicios de negocio están implementados en carpetas independientes de apps/: carrito, pagos simulados y notificaciones por JetStream, con bases de datos propias y tests. Consulta uso y contratos y escenarios de fallo. El despliegue de estos servicios en k3d queda pendiente; para verificarlos sin tocar el cluster:

./scripts/test.ps1 -IncludeIncidents

Notas y resolución de problemas

  • Helm: no cached repo found: ejecuta helm repo update y vuelve a lanzar up.ps1. El script también actualiza los repositorios grafana y open-telemetry antes de Terraform.
  • Endpoint del API de k3d en Windows: si el kubeconfig usa host.docker.internal y el puerto publicado rechaza conexiones, up.ps1 lo normaliza a 127.0.0.1 después de crear el cluster.
  • Imágenes locales: k3d image import solo afecta al cluster actual. Tras recrearlo hay que ejecutar build.ps1 otra vez.
  • Solo Windows: los local-exec y scripts usan PowerShell.

Flujo de diagnóstico agéntico

  1. Grafana evalúa ErrorEnCualquierServicio cuando una traza tiene status_code=STATUS_CODE_ERROR.
  2. alert-enricher busca la traza de error en Tempo, extrae excepción, stacktrace y atributos del span, y reenvía el cuerpo enriquecido.
  3. ai-sre-agent, proceso Node en el host, genera un RCA contra Ollama y guarda el informe en reports/*.json.

El receptor final se arranca aparte:

cd C:\Users\aaron\Documents\Repos\ai-sre-agent
$env:PORT = "3100"; node server.js

Consulta ADR-0006 para el detalle del flujo.

About

Autonomous SRE on-call agent: detect, RCA, and propose GitOps PR fixes

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages