keda-scaler.md (5896B)
1 # KEDA External Scaler for GitHub Issue-Driven Agent Autoscaling 2 3 > Scale agent pods to zero when idle, up when work arrives — driven by GitHub Issues. 4 5 ## Overview 6 7 When running Squad on Kubernetes, agent pods sit idle when no work exists. [KEDA](https://keda.sh) (Kubernetes Event-Driven Autoscaler) solves this for queue-based workloads, but GitHub Issues isn't a native KEDA trigger. 8 9 The `keda-copilot-scaler` is a KEDA External Scaler (gRPC) that bridges this gap: 10 1. Polls GitHub API for issues matching specific labels (e.g., `squad:copilot`) 11 2. Reports queue depth as a KEDA metric 12 3. Handles rate limits gracefully (Retry-After, exponential backoff) 13 4. Supports composite scaling decisions 14 15 ## Quick Start 16 17 ### Prerequisites 18 - Kubernetes cluster with KEDA v2.x installed 19 - GitHub personal access token (PAT) with `repo` scope 20 - Helm 3.x 21 22 ### 1. Install the Scaler 23 24 ```bash 25 helm install keda-copilot-scaler oci://ghcr.io/tamirdresher/keda-copilot-scaler \ 26 --namespace squad-scaler --create-namespace \ 27 --set github.owner=YOUR_ORG \ 28 --set github.repo=YOUR_REPO \ 29 --set github.token=YOUR_TOKEN 30 ``` 31 32 Or with Kustomize: 33 ```bash 34 kubectl apply -k https://github.com/tamirdresher/keda-copilot-scaler/deploy/kustomize 35 ``` 36 37 ### 2. Create a ScaledObject 38 39 ```yaml 40 apiVersion: keda.sh/v1alpha1 41 kind: ScaledObject 42 metadata: 43 name: picard-scaler 44 namespace: squad 45 spec: 46 scaleTargetRef: 47 name: picard-deployment 48 minReplicaCount: 0 # Scale to zero when idle 49 maxReplicaCount: 3 50 pollingInterval: 30 # Check every 30 seconds 51 cooldownPeriod: 300 # Wait 5 minutes before scaling down 52 triggers: 53 - type: external 54 metadata: 55 scalerAddress: keda-copilot-scaler.squad-scaler.svc.cluster.local:6000 56 owner: your-org 57 repo: your-repo 58 labels: squad:copilot # Only count issues with this label 59 threshold: "1" # Scale up when >= 1 issue exists 60 ``` 61 62 ### 3. Verify 63 64 ```bash 65 # Check the scaler is running 66 kubectl get pods -n squad-scaler 67 68 # Check ScaledObject status 69 kubectl get scaledobject picard-scaler -n squad 70 71 # Watch scaling events 72 kubectl get events -n squad --watch 73 ``` 74 75 ## Scaling Behavior 76 77 | Open Issues | Target Replicas | Behavior | 78 |------------|----------------|----------| 79 | 0 | 0 | Scale to zero — save resources | 80 | 1–3 | 1 | Single agent handles work | 81 | 4–10 | 2 | Scale up for parallel processing | 82 | 10+ | 3 (max) | Maximum parallelism | 83 84 The threshold and max replicas are configurable per ScaledObject. 85 86 ## Rate Limit Awareness 87 88 The scaler tracks GitHub API rate limits: 89 - Reads `X-RateLimit-Remaining` from API responses 90 - Backs off when quota is low (< 100 remaining) 91 - Reports rate limit metrics as secondary KEDA triggers 92 - Never exhausts API quota from polling 93 94 ## Integration with Squad 95 96 ### Machine Capabilities (#514) 97 98 Combine with machine capability labels for intelligent scheduling: 99 100 ```yaml 101 # Only scale pods on GPU-capable nodes 102 spec: 103 template: 104 spec: 105 nodeSelector: 106 node.squad.dev/gpu: "true" 107 triggers: 108 - type: external 109 metadata: 110 labels: squad:copilot,needs:gpu 111 ``` 112 113 ### Cooperative Rate Limiting (#515) 114 115 The scaler exposes rate limit metrics that feed into the cooperative rate limiting system: 116 - Current `X-RateLimit-Remaining` value 117 - Predicted time to exhaustion (from predictive circuit breaker) 118 - Can return 0 target replicas when rate limited → pods scale to zero 119 120 ## Architecture 121 122 ``` 123 GitHub API KEDA Kubernetes 124 ┌──────────┐ ┌──────────┐ ┌──────────────┐ 125 │ Issues │◄── poll ──►│ Scaler │──metrics─►│ HPA / KEDA │ 126 │ (REST) │ │ (gRPC) │ │ Controller │ 127 └──────────┘ └──────────┘ └──────┬───────┘ 128 │ 129 scale up/down 130 │ 131 ┌──────▼───────┐ 132 │ Agent Pods │ 133 │ (0–N replicas)│ 134 └──────────────┘ 135 ``` 136 137 ## Configuration Reference 138 139 | Parameter | Default | Description | 140 |-----------|---------|-------------| 141 | `github.owner` | — | Repository owner | 142 | `github.repo` | — | Repository name | 143 | `github.token` | — | GitHub PAT with `repo` scope | 144 | `github.labels` | `squad:copilot` | Comma-separated label filter | 145 | `scaler.port` | `6000` | gRPC server port | 146 | `scaler.pollInterval` | `30s` | GitHub API polling interval | 147 | `scaler.rateLimitThreshold` | `100` | Stop polling below this remaining | 148 149 ## Source & Contributing 150 151 - **Repository:** [tamirdresher/keda-copilot-scaler](https://github.com/tamirdresher/keda-copilot-scaler) 152 - **License:** MIT 153 - **Language:** Go 154 - **Tests:** 51 passing (unit + integration) 155 - **CI:** GitHub Actions 156 157 The scaler is maintained as a standalone project. PRs and issues welcome. 158 159 ## References 160 161 - [KEDA External Scalers](https://keda.sh/docs/latest/concepts/external-scalers/) — KEDA documentation 162 - [Squad on AKS](https://github.com/tamirdresher/squad-on-aks) — Full Kubernetes deployment example 163 - [Machine Capabilities](machine-capabilities.md) — Capability-based routing (#514) 164 - [Cooperative Rate Limiting](cooperative-rate-limiting.md) — Multi-agent rate management (#515)