🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Build an AI Kubernetes Resource Quota Advisor with Claude API

Namespace ResourceQuotas either get set once and forgotten (too tight, blocking legitimate scaling) or left unset entirely (no protection against a runaway team). Build a tool that recommends per-namespace quotas from actual usage patterns with Claude API.

Shubham4 min read
Share:Tweet

ResourceQuotas are one of the most under-tuned Kubernetes primitives — teams either skip them entirely (one runaway deployment can starve a shared cluster) or set them once at cluster creation with guessed numbers that are wrong within a month. This tool derives quota recommendations from actual observed usage and growth trends, not guesses.

Setup

bash
pip install anthropic kubernetes

Usage History Collector

python
import anthropic
from kubernetes import client, config
from datetime import datetime, timedelta
 
client_ai = anthropic.Anthropic()
 
 
def get_namespace_usage_history(namespace: str, days: int = 30) -> dict:
    """Pull actual resource usage over time — not current snapshot,
    the trend and peak-to-average pattern matter more than a point-in-time read."""
    import requests
    prometheus_query = lambda q: requests.get(
        "http://prometheus:9090/api/v1/query_range",
        params={"query": q, "start": (datetime.utcnow() - timedelta(days=days)).timestamp(),
                "end": datetime.utcnow().timestamp(), "step": "1h"}
    ).json()["data"]["result"]
 
    return {
        "cpu_usage_trend": prometheus_query(f'sum(rate(container_cpu_usage_seconds_total{{namespace="{namespace}"}}[5m]))'),
        "memory_usage_trend": prometheus_query(f'sum(container_memory_working_set_bytes{{namespace="{namespace}"}})'),
        "pod_count_trend": prometheus_query(f'count(kube_pod_info{{namespace="{namespace}"}})'),
        "current_quota": get_current_quota(namespace),
    }
 
 
def get_current_quota(namespace: str) -> dict:
    config.load_kube_config()
    v1 = client.CoreV1Api()
    quotas = v1.list_namespaced_resource_quota(namespace)
    if not quotas.items:
        return {"exists": False}
    q = quotas.items[0]
    return {"exists": True, "hard": q.status.hard, "used": q.status.used}

Recommendation Generation

python
import json
 
RECOMMEND_PROMPT = """Recommend a ResourceQuota for this Kubernetes namespace
based on its actual usage history.
 
Namespace: {namespace}
30-day CPU usage trend (cores over time): {cpu_trend}
30-day memory usage trend (bytes over time): {memory_trend}
30-day pod count trend: {pod_count_trend}
Current quota (if any): {current_quota}
 
Determine:
1. Peak usage observed (not average — quotas need headroom for legitimate spikes)
2. Growth trend — is usage trending up, flat, or down over the 30 days?
   Recommend headroom accordingly (more headroom for growing namespaces)
3. If a current quota exists and usage is consistently near or hitting it,
   flag this as likely causing throttled/blocked deployments
4. If a current quota exists and usage never comes close, flag as
   over-provisioned (wasting cluster capacity reservation)
 
Respond with ONLY valid JSON:
{{"recommended_quota": {{"requests.cpu": "...", "requests.memory": "...",
  "limits.cpu": "...", "limits.memory": "...", "pods": "..."}},
  "reasoning": "...", "current_quota_issue": "too_tight"|"over_provisioned"|"appropriate"|"none_set"}}"""
 
 
def recommend_quota(namespace: str, usage_history: dict) -> dict:
    response = client_ai.messages.create(
        model="claude-sonnet-5",
        max_tokens=800,
        messages=[{
            "role": "user",
            "content": RECOMMEND_PROMPT.format(
                namespace=namespace,
                cpu_trend=summarize_trend(usage_history["cpu_usage_trend"]),
                memory_trend=summarize_trend(usage_history["memory_usage_trend"]),
                pod_count_trend=summarize_trend(usage_history["pod_count_trend"]),
                current_quota=usage_history["current_quota"],
            )
        }]
    )
    text = response.content[0].text.strip()
    if text.startswith("```"):
        text = text.split("```")[1].replace("json", "", 1).strip()
    return json.loads(text)
 
 
def summarize_trend(series: list) -> dict:
    """Reduce a raw time series to peak/average/trend-direction — keeps
    the prompt compact instead of dumping thousands of data points."""
    if not series or not series[0].get("values"):
        return {"peak": 0, "average": 0, "trend": "no_data"}
    values = [float(v[1]) for v in series[0]["values"]]
    first_half_avg = sum(values[:len(values)//2]) / max(len(values)//2, 1)
    second_half_avg = sum(values[len(values)//2:]) / max(len(values) - len(values)//2, 1)
    trend = "increasing" if second_half_avg > first_half_avg * 1.1 else \
            "decreasing" if second_half_avg < first_half_avg * 0.9 else "flat"
    return {"peak": max(values), "average": sum(values) / len(values), "trend": trend}

Example Output

yaml
# Recommended ResourceQuota for namespace: payments
# Current quota: requests.cpu=4, requests.memory=8Gi — usage regularly
# hits 3.8 CPU cores during business hours, causing pending pods during
# peak traffic. Trend: increasing over 30 days (+18%).
 
apiVersion: v1
kind: ResourceQuota
metadata:
  name: payments-quota
  namespace: payments
spec:
  hard:
    requests.cpu: "6"          # 30% headroom over observed peak, accounting for growth trend
    requests.memory: "12Gi"
    limits.cpu: "8"
    limits.memory: "16Gi"
    pods: "40"
Reasoning: Current quota's requests.cpu=4 is regularly hit during business
hours (peak observed: 3.8 cores), which explains reports of pods stuck
Pending during traffic spikes. Usage is trending up 18% over 30 days,
consistent with the team's recent feature launches. Recommending 6 cores
gives headroom for the current trend to continue for roughly another
quarter before needing another review — set a reminder to re-run this
analysis in 90 days rather than setting-and-forgetting again.

Why Trend Matters More Than a Single Snapshot

A quota set from a single point-in-time usage snapshot is wrong the moment the namespace's workload changes even slightly — the value of analyzing 30 days of trend data instead of "current usage + 20%" is catching whether a namespace is actively growing (needs more headroom now, not just enough for today) or stable (a tight quota is fine and actually useful for cost control). Re-running this quarterly, rather than once at namespace creation, is what keeps quotas from drifting into either "blocking legitimate work" or "providing no real protection" over time.


More AI FinOps/Kubernetes tooling? Read our Kubernetes cost optimization strategies and Build AI Kubernetes resource optimizer with Claude API.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments