cd /news/ai-agents/automated-incident-triage-on-gke-wit… · home › topics › ai-agents › article
[ARTICLE · art-154001] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Automated incident triage on GKE with Gemini 4 Argon and Model Context Protocol: Correlating cluster events and VPA logs

A developer has outlined an architecture for a read-only incident triage agent on Google Kubernetes Engine that uses Gemini 4 Argon and the open-source GKE MCP server to correlate Pod events, Cloud Logging stack traces, and KCP_VPA vertical autoscaling decision logs. The agent runs under least-privilege RBAC and Workload Identity Federation, with no direct cluster write access, and proposes remediation as a structured report and YAML diff requiring human or GitOps approval. The approach relies on enabling VPA decision logging, available in Public Preview on GKE 1.36.0-gke.1601000 and later.

by read8 min views1 publishedOct 20, 2026

When an on-call engineer gets paged at 2 AM for a degraded payment service, the hardest part of incident triage is rarely fixing the YAML manifest. Instead, the bottleneck is correlating telemetry across three different control loops: transient Kubernetes Pod events that expire after an hour, application stack traces in Cloud Logging, and vertical autoscaling actions emitted by the control plane.

Consider a common failure mode on Google Kubernetes Engine (GKE): a latency spike triggers an out-of-memory (OOMKilled) termination while the Vertical Pod Autoscaler (VPA) attempts an in-place resize (InPlaceOrRecreate) that gets capped by node capacity or GKE Autopilot compute ratio constraints. Diagnosing that sequence manually requires stitching together kubectl describe pod, container exit codes, and container.googleapis.com/vpa-controller decision logs.

With the release of Gemini 4 Argon and the open-source Google Kubernetes Engine MCP server, platform teams can deploy an in-cluster incident triage agent that autonomously queries Kubernetes state, inspects KCP_VPA control plane logs, identifies the root cause, and generates a verified remediation patch.

In this article, I will walk through the architecture of a read-only Gemini 4 Argon incident triage agent on GKE, show how to configure least-privilege RBAC and Workload Identity Federation for GKE, and demonstrate how the agent correlates live cluster telemetry through the Model Context Protocol (MCP).

Single-turn log summarization falls short during real Kubernetes incidents because the first symptom is almost never the root cause. A CrashLoopBackOff status on a Pod tells you only that the kubelet is delaying restarts; it does not tell you whether the failure stems from an expired secret, a failed readiness probe during JVM initialization, or a memory limit that VPA could not resize in place.

Gemini 4 Argon's long-horizon tool calling allows an agent to execute a structured diagnostic workflow across multiple MCP tools before forming a hypothesis:

list_k8s_events and get_k8s_resource through the GKE MCP server to pull active events, Pod termination reasons, and deployment spec history.get_k8s_logs to capture application stack traces and unhandled exceptions.query_vpa_decision_logs against Cloud Logging to evaluate KCP_VPA decisions, checking whether autoscaling recommendations were capped or throttled. To keep the cluster safe from unintended mutations during an active outage, the triage agent operates under a strict separation of duties:

Operational phase Access level Mechanism Security boundary
Telemetry discovery Read-only ( get ,list ,watch ) GKE MCP server + Kubernetes ClusterRole Denies create ,patch ,delete , and access toSecret payloads
Control plane log correlation Read-only ( roles/logging.viewer ) Cloud Logging API via Workload Identity Scoped to k8s_container ,k8s_pod , andvpa-controller logs
Remediation proposal Zero direct cluster write access Structured Markdown report and YAML diff Requires human approval or GitOps pipeline validation before apply

For the triage agent to inspect vertical autoscaling decisions alongside standard workload logs, enable VPA decision logging (KCP_VPA, available in Public Preview on GKE 1.36.0-gke.1601000 and later) on your GKE Autopilot or GKE Standard cluster. On GKE Autopilot clusters, Vertical Pod Autoscaling is enabled by default; on GKE Standard clusters, ensure --enable-vertical-pod-autoscaling is also enabled.

When updating --logging on an existing cluster, always include your existing logging components (such as SYSTEM and WORKLOAD) so you do not overwrite active settings:

export PROJECT_ID="production-ai-platform"
export PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}" --format="value(projectNumber)")
export LOCATION="us-central1"
export CLUSTER_NAME="commerce-production-cluster"

gcloud container clusters describe "${CLUSTER_NAME}" \
    --location="${LOCATION}" \
    --project="${PROJECT_ID}" \
    --format="value(loggingConfig.componentConfig.enableComponents)"

gcloud container clusters update "${CLUSTER_NAME}" \
    --location="${LOCATION}" \
    --project="${PROJECT_ID}" \
    --logging=SYSTEM,WORKLOAD,KCP_VPA

Never grant an AI agent cluster-admin permissions or read access to Kubernetes Secret objects. Create a dedicated sre-triage namespace and a ClusterRole that grants read-only visibility into workloads, events, HPA/VPA policies, and custom compute classes while explicitly excluding secrets.

Save the following manifest as triage-agent-rbac.yaml:

apiVersion: v1
kind: Namespace
metadata:
  name: sre-triage
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: gemini-triage-sa
  namespace: sre-triage
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: gemini-readonly-triage-role
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log", "events", "services", "nodes", "namespaces", "configmaps"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["events.k8s.io"]
  resources: ["events"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments", "statefulsets", "daemonsets", "replicasets"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["autoscaling"]
  resources: ["horizontalpodautoscalers"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["autoscaling.k8s.io"]
  resources: ["verticalpodautoscalers"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["cloud.google.com"]
  resources: ["computeclasses"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: gemini-readonly-triage-binding
subjects:
- kind: ServiceAccount
  name: gemini-triage-sa
  namespace: sre-triage
roleRef:
  kind: ClusterRole
  name: gemini-readonly-triage-role
  apiGroup: rbac.authorization.k8s.io

Apply the RBAC manifest and bind the gemini-triage-sa ServiceAccount to the Gemini platform, GKE Viewer, and Cloud Logging Viewer IAM roles via Workload Identity Federation:

kubectl apply -f triage-agent-rbac.yaml

for ROLE in roles/aiplatform.user roles/container.viewer roles/logging.viewer; do
  gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
      --member="principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/sre-triage/sa/gemini-triage-sa" \
      --role="${ROLE}" \
      --condition=None
done

The triage agent connects Gemini 4 Argon (gemini-4-argon) to an in-process gke-mcp session alongside a dedicated tool for querying container.googleapis.com/vpa-controller decision logs in Cloud Logging.

Here is the core implementation (triage_agent.py):

import asyncio
import os
from google import genai
from google.genai import types
from google.cloud import logging as cloud_logging
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"]
LOCATION = os.environ.get("VERTEX_LOCATION", "us-central1")
MODEL_ID = os.environ.get("GEMINI_MODEL_ID", "gemini-4-argon")

genai_client = genai.Client(vertexai=True, project=PROJECT_ID, location=LOCATION)
logging_client = cloud_logging.Client(project=PROJECT_ID)

def query_vpa_decision_logs(
    cluster_name: str, namespace: str, workload_name: str, limit: int = 10
) -> list[dict]:
    """Queries GKE KCP_VPA control plane logs for a specific workload to inspect resize or eviction decisions.

    Args:
        cluster_name: Name of the GKE cluster.
        namespace: Kubernetes namespace of the target workload.
        workload_name: Name of the target Deployment or workload.
        limit: Maximum number of log entries to return.
    """
    log_filter = (
        'resource.type="k8s_control_plane_component" '
        f'AND resource.labels.cluster_name="{cluster_name}" '
        f'AND logName="projects/{PROJECT_ID}/logs/container.googleapis.com%2Fvpa-controller" '
        f'AND jsonPayload.target.namespace="{namespace}" '
        f'AND jsonPayload.target.name="{workload_name}"'
    )
    entries = logging_client.list_entries(
        filter_=log_filter,
        order_by=cloud_logging.DESCENDING,
        max_results=limit,
    )
    results = []
    for entry in entries:
        payload = entry.payload if isinstance(entry.payload, dict) else {}
        results.append({
            "timestamp": str(entry.timestamp),
            "operation": payload.get("operation"),
            "state": payload.get("state"),
            "reason": payload.get("reason"),
            "confidence": payload.get("confidence"),
        })
    return results

async def investigate_workload_incident(cluster_name: str, namespace: str, workload_name: str) -> str:
    """Connects Gemini to the GKE MCP server and Cloud Logging to diagnose a failing workload."""
    server_params = StdioServerParameters(
        command="gke-mcp",
        args=[],
        env=os.environ.copy(),
    )

    async with stdio_client(server_params) as (read_stream, write_stream):
        async with ClientSession(read_stream, write_stream) as mcp_session:
            await mcp_session.initialize()

            prompt = (
                f"Investigate the degraded Deployment '{workload_name}' in namespace '{namespace}' "
                f"on GKE cluster '{cluster_name}'. "
                "1. List recent Kubernetes events and inspect the Deployment and Pod statuses. "
                "2. Check the container termination exit codes and recent Pod logs. "
                "3. Query VPA decision logs using query_vpa_decision_logs to see if an in-place resize "
                "or eviction occurred or was skipped. "
                "4. Provide a concise root-cause timeline and a git-ready YAML patch."
            )

            config = types.GenerateContentConfig(
                system_instruction=(
                    "You are a senior GKE Site Reliability Engineer. Always verify Pod status, exit codes, "
                    "Kubernetes events, and VPA decision logs before proposing a fix. Never guess when "
                    "telemetry can be queried via your tools."
                ),
                tools=[mcp_session, query_vpa_decision_logs],
                automatic_function_calling=types.AutomaticFunctionCallingConfig(
                    maximum_remote_calls=8
                ),
                temperature=0.1,
            )

            response = await genai_client.aio.models.generate_content(
                model=MODEL_ID,
                contents=prompt,
                config=config,
            )
            return response.text

By passing both mcp_session and query_vpa_decision_logs into tools, the google-genai SDK automatically discovers the GKE MCP server tools (list_k8s_events, get_k8s_resource, get_k8s_logs) and orchestrates the multi-step investigation up to the configured maximum_remote_calls limit. Passing env=os.environ.copy() to StdioServerParameters ensures the gke-mcp subprocess inherits in-cluster Kubernetes service environment variables (KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT) and GOOGLE_CLOUD_PROJECT.

To see what the triage agent captures in practice, suppose the checkout-service Deployment in the payments namespace starts restarting with exit code 137 (OOMKilled) while managed by a VerticalPodAutoscaler in InPlaceOrRecreate mode.

When triggered, Gemini 4 Argon executes four tool calls in sequence and produces a structured diagnostic report:

Root-cause summary for Deployment payments/checkout-service:
1. Pod status & exit code: Pod checkout-service-7c9d8b6f4-x82lp terminated with reason OOMKilled (exit code 137) at 06:14:22 UTC after memory usage reached its 1Gi limit.
2. Kubernetes events: BackOff restarting failed container; InPlaceResizeFailed event reported on node gke-commerce-pool-4a91f.
3. VPA control plane logs (KCP_VPA):
   - Operation: APPLY_RECOMMENDATION_IN_PLACE | State: FAILED | Confidence: HIGH
   - Reason: Recommended memory target (2Gi) exceeded maxAllowed policy bound (1Gi) defined in VerticalPodAutoscaler checkout-service-vpa.
4. Recommended remediation: Raise resourcePolicy.containerPolicies[0].maxAllowed.memory in checkout-service-vpa from 1Gi to 3Gi so VPA can apply high-confidence in-place memory resizes without triggering OOM terminations.

You can independently verify the agent's finding using kubectl and gcloud:

kubectl get vpa checkout-service-vpa -n payments -o yaml

gcloud logging read "resource.type=\"k8s_control_plane_component\" AND resource.labels.cluster_name=\"${CLUSTER_NAME}\" AND logName=\"projects/${PROJECT_ID}/logs/container.googleapis.com%2Fvpa-controller\" AND jsonPayload.target.name=\"checkout-service\"" \
    --project="${PROJECT_ID}" \
    --limit=3 \
    --format=json

When running an AI-assisted triage agent inside your production GKE environment, apply these operational safeguards:

kubectl apply or mutate resource limits during an active incident. Have the agent open a pull request against your GitOps repository (managed by Config Sync or Argo CD) so human operators can review the diff.pods/log output to the model API, and ensure your Gemini platform data governance policies disable prompt logging where required.Deployment or StatefulSet) and cap each investigation to a maximum of 8 tool-calling turns. Combining Gemini 4 Argon with the GKE Model Context Protocol server and structured control plane logs (KCP_VPA) turns multi-system incident correlation from a 30-minute manual scavenger hunt into an automated, auditable workflow.

To deploy this pattern on your clusters, explore the open-source Google Kubernetes Engine MCP server repository, read the GKE VPA decision logs documentation, and review Workload Identity Federation for GKE for zero-key IAM authentication.

── more in #ai-agents 4 stories · sorted by recency
── more on @google 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/automated-incident-t…] indexed:0 read:8min 2026-10-20 · —