Przejdź do treści
Marcin Baniowski Connecting people with machines
← Wróć do notatek

Agentic DevOps, od lokalnego IDE do autonomicznych agentów

Punkt startowy: LLM w IDE

W przypadku mniejszych projektów, zarządzanie infrastrukturą z pomocą LLM z lokalnego IDE jest w zupełności wystarczające. Odpowiedni zestaw uprawnień, kontekst w CLAUDE.md, skills i MCP spisują się doskonale. Oczywiście możliwe są pomyłki: akcja nie na tym środowisku co potrzeba, lub niedoczytanie wykonywanej przez LLM komendy przed "yes", ale potencjalne wpadki są mniej prawdopodobne i szybsze do wykrycia i neutralizacji.

Są jednak sytuacje wymagające nadania LLM-om większej autonomii, np. kiedy chcemy automatyzacji reakcji na alerty o zbyt dużym zużyciu zasobów przez pody lub ich częste restarty. Do tego potrzebny jest zestaw narzędzi: kubernetes, aws, logi i metryki, co pociąga za sobą problem uprawnień i bezpieczeństwa. Każdy agent musi być wyizolowany fizycznie z dostępem wyłącznie do minimalnych zasobów i serwisów. Umiejscowienie agenta na jednym podzie z dostępem do różnych środowisk, z instrukcjami w prompcie jako jedynym zabezpieczeniem, jest przepisem na rychłą katastrofę.

Taka granulacja dostępu potrafi znacznie skomplikować konfigurację - każdy serwis / środowisko / poziom dostępu wymaga osobnego serwisu/poda, np. stage Kubernetes RO vs production Kubernetes RO vs stage Kubernetes RW vs production AWS RO, itd. Przy potencjalnej mnogości agentów i innych uczestników (ludzie), kluczowa staje się kwestia przejrzystego zarządzania uprawnieniami.

Proponowana architektura zakłada centralizację zarządzania rolami i jeden punkt wejścia - MCP Gateway. Zakładamy że w tym przypadku wszystkie serwisy dostępowe deployowane są w postaci podów w klastrze EKS.

Architektura MCP Gateway

Agentic DevOps — Secure MCP Gateway Architecture

Uprawnienia serwerów MCP

Każdy serwer MCP odpowiadający za dostęp do konkretnych serwisów musi być umiejscowiony na oddzielnym podzie, per środowisko, per poziom dostępu (RW, RO). Uprawnienia nadawane są przez mechanizm IRSA - pody przez Service Account zmapowane są do odpowiednio przyciętych polityk w rolach IAM.

Zarządzanie kluczami

Klucze API są przechowywane w AWS SSM Parameter Store, synchronizowane automatycznie do Kubernetes ConfigMaps.

Przykład skryptu tworzącego klucz dla nowego agenta:

./create-keys.sh --role dev-readonly --user incident-agent-01

Agent używa klucza w headerze:

Authorization: Bearer <base64-encoded-key>

Skrypt generuje klucz i zapisuje go do SSM Parameter Store w formacie hash:rola/user_name - JSON z agregowanymi kluczami jest synchronizowany automatycznie do Secrets, przez External Secrets. MCP Gateway periodycznie pobiera mapę kluczy (nginx ze skryptem Lua), i porównuje je do przychodzących nagłówków z Bearer token.

RBAC — role i uprawnienia

Każda rola to zbiór dozwolonych backendów:

roleMapping:
  devops-full:
    - eks-rw
    - eks-ro-prod
    - aws-rw
    - costs
    - cloudwatch
    - postgres-rw
    - prometheus-dev
    - prometheus-prod
    - cloudflare-edit
    - argo-sync
    - github-rw
  dev-readonly:
    - eks-ro
    - eks-ro-prod
    - aws-ro
    - costs
    - cloudwatch
    - postgres-ro
    - prometheus-dev
    - prometheus-prod
    - cloudflare-ro
    - argo-view
    - github-ro

Trasa zapytania


1. Agent → Gateway: POST /mcp/eks-ro/mcp (Bearer abc123)

2. Gateway:
   abc123 → role "dev-readonly" ✅
   dev-readonly ma dostęp do eks-ro? ✅
   → proxy do poda mcp-eks-ro

3. Pod mcp-eks-ro:
   Agent wywołuje: eks_get_pods(namespace="production")
   Pod → k8s API → GET /api/v1/namespaces/production/pods
   k8s RBAC: Service Account mcp-eks-ro → Role mcp-viewer → get pods ✅

   Agent wywołuje: eks_delete_pod(name="nginx-abc")
   k8s RBAC: mcp-viewer nie ma "delete" verb → 403 FORBIDDEN ❌

4. Pod mcp-aws-ro:
   aws_describe_instances → ec2:Describe* → OK ✅
   aws_terminate_instance → brak ec2:TerminateInstances → AccessDenied ❌

Przykład agenta do reakcji na incydenty

ALERT: "PodCrashLooping: payment-service in production (47 restarts)"

AGENT: "Pod crashuje. Sprawdzam status i eventy"
  └─► eks.get_events(namespace="production", name="payment-service")
  ◄── [{reason: "OOMKilled", count: 47}]

AGENT: "OOMKilled. Sprawdzam zużycie pamięci"
  └─► prometheus.query("container_memory_usage_bytes{pod=~'payment-service.*'}")
  ◄── 500MB

AGENT: "Sprawdzam limit"
  └─► eks.get_resource(kind="Deployment", name="payment-service")
  ◄── limits.memory: 512Mi

AGENT: "500/512MB — blisko limitu. Sprawdzam logi"
  └─► eks.get_pod_logs(pod="payment-service-abc", tail_lines=200)
  ◄── ["DB connection pool exhausted", "Retrying query...", "OOM"]

AGENT: "Connection pool wyczerpany — problem z bazą"
  └─► postgres.diagnostics(env="production")
  ◄── {active_connections: 98, max_connections: 100,
       slow_queries: [{query: "SELECT ... JOIN ...", mean_ms: 4500}]}

AGENT → generuje raport → Slack + opcjonalnie PR z fix'em

Każde wywołanie narzędzia przechodzi przez MCP Gateway i podlega weryfikacji uprawnień. Agent z rolą dev-readonly może zbierać dane do raportu, ale nie może restartować podów ani skalować deploymentów.

Ten flow można rozszerzyć np. o wystawianie PR-ów do repozytorium ArgoCD z propozycją podbicia zasobów, bądź wręcz commitować bezpośrednio uruchamiając automatyczny sync i deployment z nowymi wartościami pamięci czy CPU np. na stage.

Audyt i observability

Dwa komplementarne poziomy:

Warstwa Co loguje Narzędzie
Gateway audit Kto wywołał jakie narzędzie MCP, z jakimi parametrami, czas trwania Loki (stdout → Promtail → Grafana)
LLM trace Pełna pętla agenta: model, tokeny, reasoning, wywołania narzędzi Langfuse

Przykład strukturalnego logu z gateway:

{
  "type": "mcp_tool_call",
  "user": "incident-agent-01",
  "role": "dev-readonly",
  "backend": "eks-ro",
  "tool_name": "eks_get_pods",
  "tool_args": {"namespace": "production"},
  "http_status": 200,
  "duration_ms": 245
}

Gateway audit działa dla wszystkich klientów (IDE, Cloud code, headless agenty). Langfuse wymaga instrumentacji po stronie klienta.

Wnioski

Przejście od interaktywnego LLM w IDE do autonomicznych agentów to nie kwestia promptów — to zmiana architektury bezpieczeństwa. Kluczowe zasady:

  1. Fizyczna izolacja zamiast instrukcji w prompcie — agent nie może zrobić czegoś, do czego nie ma credentials
  2. Defense in depth — trzy niezależne warstwy (gateway, k8s RBAC, AWS IAM) zapewniają ochronę nawet przy kompromitacji jednej z nich
  3. Least privilege — każdy agent dostaje dokładnie te uprawnienia, których potrzebuje
  4. Pełny audyt — każde wywołanie narzędzia jest logowane z tożsamością, rolą i parametrami