celld-operator

Autoscaling

CPU is the wrong primary signal for celld: fleets are capacity-bound by resident cells and memory, and upstream ships no metrics endpoint yet. The operator closes both gaps: it exports the metrics and encodes the scaling policy.

The metrics source

The operator already polls each fleet pod's internal /state endpoint for rollout gating (it is the one authorized cross-namespace caller). It re-exports what it sees as Prometheus metrics, per WorkerApp and per pod, on the leader every --state-poll-interval (default 15s):

MetricMeaning
celld_resident_cellsOccupied (resident) cells per pod.
celld_resident_cell_utilizationoccupied / maxResidentCells (0..1), the primary scale signal.
celld_restoringCold activations in flight, cold activations in flight; a health signal, and the rollout controller's step gate — not an autoscaling input.
celld_evictingCells being evicted.
celld_shedding1 while pressure-shedding; the hard out-of-capacity signal.
celld_state_up1 if /state answered the last poll.

Point Prometheus at the operator (the chart exposes metrics on :8443), and tell the operator where KEDA can query them with --prometheus-url.

What the operator materializes

With spec.autoscaling.enabled: true, the operator creates a KEDA ScaledObject targeting the fleet's StatefulSet:

autoscaling:
  enabled: true
  minReplicas: 3     # keep ≥ 2 for HA
  maxReplicas: 10    # the tenant's cost ceiling
  targets:
    residentCellUtilization: 70
    p95LatencyMs: 250  # optional; needs Istio telemetry

celld-aware policy, encoded

These behaviors are set by the operator rather than left to tuning:

Requirements and degradation

Autoscaling needs KEDA installed and a Prometheus scraping the operator. When the KEDA CRDs are absent the WorkerApp reports AutoscalingReady: False with reason KEDAUnavailable and spec.autoscaling has no effect. (A missing or unreachable Prometheus is not detected: KEDA owns that failure, and the condition stays True.)

Enabling autoscaling hands replica count to KEDA permanently. Once spec.autoscaling.enabled is true the operator stops reconciling replicas altogether, so later edits to spec.replicas are ignored — and if KEDA is absent the fleet simply stays at whatever count it already had. Set the floor and ceiling with minReplicas/maxReplicas, and to hand control back, disable autoscaling first.

Sizing rule of thumb (upstream): ~1,000 resident cells per 8 GiB node. Set resources.maxResidentCells and resources.memoryGi together; the operator derives the container limit, CELLD_MAX_RSS_MB (~80% of the limit), and the admission cap from these two numbers.