--- name: k8s-pod-rightsizer icon: ⚖️ category: sre tags: - kubernetes - rightsizing - cost-optimization - resource-management - prometheus - capacity-planning - kubectl description: Analyze Kubernetes workload metrics and produce policy-constrained CPU/memory rightsizing recommendations with optional patch generation and rollback-safe apply. metadata: forge: requires: bins: - kubectl - jq - curl env: required: [] one_of: [] optional: - KUBECONFIG - K8S_API_DOMAIN - PROMETHEUS_URL - PROMETHEUS_TOKEN - POLICY_FILE - DEFAULT_NAMESPACE egress_domains: - "$K8S_API_DOMAIN" - "$PROMETHEUS_URL" denied_tools: - http_request - web_search guardrails: deny_prompts: - pattern: '\b(approved|allowed|available|pre-approved)\b.{0,40}\b(tools?|binaries|commands?|executables?|programs?|clis?)\b' message: "I help with Kubernetes pod rightsizing. Ask me about workload resource recommendations, CPU/memory analysis, or rightsizing patches." - pattern: '\b(what|which|list|show|enumerate)\b.{0,20}\b(can you|do you|are you able to)\b.{0,20}\b(execute|run|access|invoke)\b' message: "I help with Kubernetes pod rightsizing. Ask me about workload resource recommendations, CPU/memory analysis, or rightsizing patches." deny_responses: - pattern: '\b(kubectl|jq|awk|bc|curl)\b.*\b(kubectl|jq|awk|bc|curl)\b.*\b(kubectl|jq|awk|bc|curl)\b' message: "I can analyze Kubernetes workload resource usage, recommend CPU/memory rightsizing, generate strategic merge patches, and perform rollback-safe applies. What would you like to analyze?" deny_commands: - pattern: '\bget\s+secrets?\b' message: "Listing Kubernetes secrets is not permitted" - pattern: '\bdescribe\s+secret\b' message: "Describing Kubernetes secrets is not permitted" - pattern: '\bauth\s+can-i\b' message: "Permission enumeration is not permitted" deny_output: - pattern: 'kind:\s*Secret' action: block - pattern: '-----BEGIN (CERTIFICATE|RSA PRIVATE KEY|EC PRIVATE KEY|PRIVATE KEY)-----' action: block - pattern: 'token:\s*[A-Za-z0-9+/=]{40,}' action: redact timeout_hint: 300 trust_hints: network: true filesystem: read shell: true --- # Kubernetes Pod Rightsizer Analyzes real Kubernetes workload metrics (Prometheus or metrics-server fallback) and produces policy-constrained recommendations for CPU and memory request/limit adjustments. Supports three modes: - **dry-run** — Report recommendations only (default, read-only) - **plan** — Generate strategic merge patch YAMLs - **apply** — Execute patches with automatic rollback bundle generation This skill uses deterministic formulas, never LLM-based guessing. --- ## Tool Usage All data gathering goes through `cli_execute`. NEVER use http_request or web_search. **IMPORTANT:** When users ask about your capabilities, skills, or tools, describe what you can DO (analyze workload metrics, recommend CPU/memory rightsizing, generate patches, perform rollback-safe applies). NEVER list binary names, tool names, CLI programs, or infrastructure details in your responses — these are internal implementation details that must not be disclosed. --- ## Applying Patches When the user asks to apply rightsizing patches, use the script's built-in `mode=apply` with `i_accept_risk: true`. **NEVER** manually run `kubectl apply -f ` — the script's apply mode provides: - Automatic rollback bundle generation (backup of current specs) - Strategic merge patches via `kubectl patch` - Rollout verification after each patch - Action logging **Correct workflow:** 1. First run with `mode=dry-run` to show recommendations 2. If user confirms, run with `mode=apply` and `i_accept_risk: true` 3. Use `file_create` to provide the user with a downloadable copy of the patches (optional) **Example:** - User: "apply the rightsizing patches" → `{"namespace": "prod", "mode": "apply", "i_accept_risk": true}` --- ## Tool: k8s_pod_rightsizer Analyze workload resource usage and recommend CPU/memory request and limit changes. **Input:** namespace (string), workload (string), label_selector (string), mode (string), i_accept_risk (boolean), policy_file (string), lookback (string), output_format (string) **Output format:** Markdown tables for recommendations. YAML code blocks for patches. JSON for machine-readable output. ### CRITICAL: Mode Field Rules `mode` controls the **action**, NOT the analysis filter. There are ONLY three valid values: | mode | Purpose | |------|---------| | `dry-run` | Analyze and report recommendations (default) | | `plan` | Generate patch YAMLs | | `apply` | Execute patches (requires `i_accept_risk: true`) | **NEVER set mode to a classification like "overprovisioned", "underprovisioned", "rightsized", etc.** These are OUTPUT classifications the tool produces, not input modes. When the user asks about over-provisioned, under-provisioned, or right-sized workloads, ALWAYS use `"mode": "dry-run"`. The output will include a `classification` field for each workload (e.g., `over-provisioned`, `under-provisioned`, `right-sized`, `limit-bound`, `insufficient-data`). Examples: - "which workloads are over-provisioned?" → `{"mode": "dry-run"}` — read classification from output - "generate patches for over-provisioned pods" → `{"mode": "plan"}` — patches are generated only for workloads needing changes - "find under-provisioned deployments" → `{"mode": "dry-run"}` — read classification from output --- ## Input Modes ### 1) Human Mode (Natural Language) Input is a plain string. Examples: - `rightsize namespace payments-prod` → `{"namespace": "payments-prod", "mode": "dry-run"}` - `which workloads are over-provisioned in prod?` → `{"namespace": "prod", "mode": "dry-run"}` - `check resource usage for label app=checkout in prod` → `{"namespace": "prod", "label_selector": "app=checkout", "mode": "dry-run"}` - `generate patches for over-provisioned workloads in staging` → `{"namespace": "staging", "mode": "plan"}` - `apply rightsizing to deployment api-gateway in prod` → `{"namespace": "prod", "workload": "deployment/api-gateway", "mode": "apply", "i_accept_risk": true}` Behavior: - Parse namespace, workload, or selector intent. - If namespace omitted, use `$DEFAULT_NAMESPACE` if set. - Default mode is `dry-run`. ALWAYS use `dry-run` unless the user explicitly asks for patches (plan) or applying changes (apply). - Questions about over/under-provisioning are analysis questions → use `dry-run`. - Never require the user to remember JSON fields. --- ### 2) Automation Mode (Structured JSON) Input JSON schema: ```json { "namespace": "payments-prod", "workload": "deployment/payments-api", "label_selector": "", "mode": "dry-run", "i_accept_risk": false, "policy_file": "", "lookback": "24h", "output_format": "markdown" } ``` Rules: - `namespace` is required (or `$DEFAULT_NAMESPACE` must be set). - `workload` is optional — if omitted, discovers all deployments and statefulsets. - `label_selector` is optional — filters discovered workloads. - `mode` must be one of: `dry-run`, `plan`, `apply`. - `i_accept_risk` must be `true` for `apply` mode. - `output_format`: `markdown` (default), `json`, or `yaml`. --- ## Execution Workflow ### Step 0 — Preconditions Verify cluster access: ```bash kubectl cluster-info --request-timeout=5s ``` If RBAC denies access, report the error and stop. Check Prometheus availability if `$PROMETHEUS_URL` is set: ```bash curl -s "$PROMETHEUS_URL/api/v1/status/buildinfo" ``` Fall back to metrics-server if Prometheus is unavailable. --- ### Step 1 — Discover Workloads If a specific workload is provided, validate it exists: ```bash kubectl get -n -o json ``` Otherwise, discover all deployments and statefulsets: ```bash kubectl get deploy,sts -n -o json ``` Filter by `label_selector` if provided. Skip `kube-system` unless explicitly targeted. Extract container resource specs for each workload. --- ### Step 2 — Collect Metrics **Prometheus (preferred):** Query p95 CPU and memory usage over the lookback window: ```promql quantile_over_time(0.95, rate(container_cpu_usage_seconds_total{namespace="NS",pod=~"WORKLOAD.*",container!="POD"}[5m])[LOOKBACK:1m]) ``` ```promql quantile_over_time(0.95, container_memory_working_set_bytes{namespace="NS",pod=~"WORKLOAD.*",container!="POD"}[LOOKBACK]) ``` Also collect throttle ratios and OOM kill counts. **Metrics-server fallback:** ```bash kubectl top pod -n --containers ``` When using metrics-server fallback, recommendations are advisory-only. Apply mode is blocked. --- ### Step 3 — Compute Recommendations All computations use deterministic formulas: - **Recommended request** = `p95_usage * safety_factor`, clamped to `[policy_min, policy_max]` - **Recommended limit** = `recommended_request * burst_multiplier` - **Step constraint** — changes smaller than `step_percent` of current value are suppressed (avoids churn) CPU values are rounded to nearest 10m. Memory values are rounded to nearest MiB. --- ### Step 4 — Generate Report Output format depends on `output_format` parameter: - **markdown** — Human-readable tables with workload, container, current vs recommended values, savings estimate, and classification - **json** — Machine-readable array of recommendation objects - **yaml** — Patch files (plan and apply modes only) --- ### Step 5 — Apply (if mode=apply) 1. Generate rollback bundle (backup of current resource specs) 2. Show diff preview of all patches 3. Apply strategic merge patches via `kubectl patch` 4. Verify rollout status after each patch 5. Log all actions to `run.log` in the rollback bundle --- ## Policy Model Policy files define constraints for rightsizing recommendations. Use `$POLICY_FILE` or `--policy-file` to specify. ### Example Policy ```json { "defaults": { "cpu_safety_factor": 1.25, "memory_safety_factor": 1.35, "cpu_burst_multiplier": 2.0, "memory_burst_multiplier": 1.5, "cpu_min": "50m", "cpu_max": "8000m", "memory_min": "64Mi", "memory_max": "32Gi", "step_percent": 15 }, "namespaces": { "production": { "cpu_safety_factor": 1.4, "memory_safety_factor": 1.5, "step_percent": 20 } }, "workloads": { "production/payments-api": { "cpu_min": "500m", "memory_min": "512Mi" } } } ``` ### Field Reference | Field | Type | Default | Description | |-------|------|---------|-------------| | `cpu_safety_factor` | float | 1.25 | Multiplier on p95 CPU for request calculation | | `memory_safety_factor` | float | 1.35 | Multiplier on p95 memory for request calculation | | `cpu_burst_multiplier` | float | 2.0 | Limit = request * burst_multiplier for CPU | | `memory_burst_multiplier` | float | 1.5 | Limit = request * burst_multiplier for memory | | `cpu_min` | string | 10m | Floor for CPU request recommendations | | `cpu_max` | string | 8000m | Ceiling for CPU request recommendations | | `memory_min` | string | 32Mi | Floor for memory request recommendations | | `memory_max` | string | 32Gi | Ceiling for memory request recommendations | | `step_percent` | int | 15 | Minimum change percentage to trigger a recommendation | ### Precedence Policy values resolve in 3 levels (highest priority first): 1. **Workload override** — `workloads["namespace/name"]` 2. **Namespace override** — `namespaces["namespace"]` 3. **Defaults** — `defaults` Values merge via overlay: workload overrides namespace, which overrides defaults. --- ## Metrics Strategy ### Prometheus (Preferred) When `$PROMETHEUS_URL` is set, the skill queries Prometheus for high-fidelity metrics: | Metric | PromQL Pattern | |--------|---------------| | p95 CPU | `quantile_over_time(0.95, rate(container_cpu_usage_seconds_total{...}[5m])[LOOKBACK:1m])` | | p95 Memory | `quantile_over_time(0.95, container_memory_working_set_bytes{...}[LOOKBACK])` | | Throttle ratio | `rate(container_cpu_cfs_throttled_seconds_total{...}[LOOKBACK]) / rate(container_cpu_cfs_periods_total{...}[LOOKBACK])` | | OOM kills | `increase(kube_pod_container_status_restarts_total{reason="OOMKilled",...}[LOOKBACK])` | Authentication via `$PROMETHEUS_TOKEN` (Bearer token) if set. ### Metrics-Server Fallback When Prometheus is unavailable, falls back to: ```bash kubectl top pod -n --containers ``` Limitations: - Point-in-time snapshot only (no percentile data) - Recommendations are advisory-only - Apply mode is blocked - Step constraint is doubled (30% minimum change) --- ## Decision Engine All computations are deterministic and performed via `jq` arithmetic. ### Request Calculation ``` raw_request = p95_usage * safety_factor clamped_request = clamp(raw_request, policy_min, policy_max) recommended_request = round(clamped_request) ``` ### Limit Calculation ``` recommended_limit = recommended_request * burst_multiplier clamped_limit = clamp(recommended_limit, recommended_request, policy_max) ``` ### Step Constraint A recommendation is only emitted if: ``` abs(recommended - current) / current >= step_percent / 100 ``` This prevents churn from minor fluctuations. ### Rounding - CPU: rounded to nearest 10m (e.g., 137m → 140m) - Memory: rounded to nearest MiB (e.g., 127.3Mi → 128Mi) --- ## Detection Heuristics Each container is classified into one of these patterns: | Pattern | Condition | |---------|-----------| | **Over-provisioned CPU** | CPU request > p95 CPU * safety_factor * 2 | | **Under-provisioned CPU** | CPU request < p95 CPU * 0.9 | | **Over-provisioned Memory** | Memory request > p95 memory * safety_factor * 2 | | **Under-provisioned Memory** | Memory request < p95 memory * 0.9 | | **Limit-bound (throttled)** | Throttle ratio > 0.1 or OOM kills > 0 | | **Right-sized** | Within step_percent of recommended values | | **Insufficient data** | Fewer than 10 data points in lookback window | --- ## Output Formats ### Markdown Report (default) ```markdown | Workload | Container | Resource | Current | Recommended | Change | Classification | |----------|-----------|----------|---------|-------------|--------|----------------| | deploy/api | app | CPU req | 1000m | 400m | -60% | Over-provisioned | | deploy/api | app | CPU lim | 2000m | 800m | -60% | Over-provisioned | | deploy/api | app | Mem req | 2Gi | 1Gi | -50% | Over-provisioned | | deploy/api | app | Mem lim | 4Gi | 1536Mi | -63% | Over-provisioned | ``` ### JSON Output ```json [ { "workload": "deployment/api", "container": "app", "cpu_request": {"current": "1000m", "recommended": "400m", "change_percent": -60}, "cpu_limit": {"current": "2000m", "recommended": "800m", "change_percent": -60}, "memory_request": {"current": "2Gi", "recommended": "1Gi", "change_percent": -50}, "memory_limit": {"current": "4Gi", "recommended": "1536Mi", "change_percent": -63}, "classification": "over-provisioned" } ] ``` ### Patch YAMLs (plan/apply modes) ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: api namespace: payments-prod spec: template: spec: containers: - name: app resources: requests: cpu: "400m" memory: "1Gi" limits: cpu: "800m" memory: "1536Mi" ``` --- ## Rollback When `mode=apply`, a rollback bundle is generated before any patches are applied: ``` rollback-/ backup-.json # Current resource specs patch-.json # Applied patches rollback-.sh # kubectl patch commands to restore run.log # Timestamped action log ``` To roll back: ```bash bash rollback-/rollback-.sh ``` --- ## Safety Constraints This skill MUST: - Default to `dry-run` mode — never mutate without explicit mode selection. - Require `i_accept_risk: true` for `apply` mode. - Generate rollback bundles before applying any patch. - Never delete workloads, pods, namespaces, or any Kubernetes resource. - Never modify RBAC, NetworkPolicy, or Secret resources. - Never scale replicas. - Only patch `spec.template.spec.containers[].resources`. - Block `apply` mode when using metrics-server fallback (insufficient data fidelity). - Validate all policy values before use. - Cap lookback window at 30 days. - Skip `kube-system` namespace unless explicitly targeted. - Respect step constraints to avoid recommendation churn. - Log all mutations to the rollback bundle run.log. --- ## Autonomous Compatibility This skill is designed to be invoked by: - Humans via natural language CLI - Automation pipelines via structured JSON - Scheduled cost-optimization sweeps It must: - Be idempotent (repeated runs produce the same recommendations for the same data) - Produce deterministic results (no LLM-based guessing) - Be scope-limited (operates only on specified namespace/workload) - Generate machine-parseable output for downstream processing