Optimizer-based Autoscaler#
Overview#
Metric-based autoscalers react: they wait for a metric such as queue depth to cross a threshold,
then add replicas. The optimizer-based autoscaler plans instead. It combines an offline benchmark
of how each GPU type performs on a model with a latency or throughput SLO, watches the actual mix
of request sizes arriving at the gateway, and computes how many replicas are needed to serve that
mix within the SLO at the lowest cost. The result is handed to a PodAutoscaler as a metric,
so the final scaling action still goes through the same controller as every other strategy.
Because it reasons about GPU capacity rather than a single utilisation number, it is also the mechanism behind Heterogeneous GPU Inference (Experimental), where several GPU types serve one model and the optimizer decides how many of each to run.
How it works#
graph LR
GW["Gateway plugins<br/>request tracing"] -->|"request traces"| R["Redis"]
P["aibrix_gen_profile<br/>(offline)"] -->|"GPU profiles"| R
R --> O["GPU optimizer"]
O -->|"/metrics/ns/deployment<br/>vllm:deployment_replicas"| PA["PodAutoscaler<br/>external metric source"]
PA -->|scales| D["Deployment"]
O -.watches.-> D
Request tracing. With
AIBRIX_GPU_OPTIMIZER_TRACING_FLAG=trueon the gateway plugin, every request’s token statistics are recorded in Redis, giving the optimizer the live distribution of input and output lengths per model.Profiles.
aibrix_benchmarkmeasures a deployment on one GPU type across input and output length patterns.aibrix_gen_profileturns that benchmark into a capacity profile for a chosen SLO and GPU cost and stores it in Redis under the model’s name. One profile per GPU type.Optimization. The GPU optimizer (
aibrix-gpu-optimizerinaibrix-system) watchesDeploymentobjects that carry themodel.aibrix.ai/namelabel, loads the profiles for that model, and solves for the replica count per deployment that covers the observed request mix within the SLO at minimum cost.Scaling. The recommendation is exposed as the Prometheus metric
vllm:deployment_replicasat/metrics/<namespace>/<deployment>. APodAutoscalerwith anexternalmetric source consumes it.
Prerequisites#
The GPU optimizer is part of the default AIBrix install (
config/defaultincludesconfig/gpu-optimizer). Confirm it is running:kubectl get deploy -n aibrix-system aibrix-gpu-optimizer.Request tracing is off by default. Enable it by redeploying the gateway plugin with the experimental overlay, or by adding the environment variable yourself:
kubectl apply -k config/experimentals/gpu-optimizer # or: kubectl edit deployment aibrix-gateway-plugins -n aibrix-system # and add AIBRIX_GPU_OPTIMIZER_TRACING_FLAG=true to the gateway-plugin container
The
aibrixPython package on the machine where you run the benchmark:pip3 install aibrix. The profiling tools additionally needtiktokenandtransformers.Access to the AIBrix Redis instance from that machine (a
port-forwardis enough).
Step 1: Benchmark the deployment#
For each type of GPU, run aibrix_benchmark. See benchmark.sh for more options.
kubectl port-forward [pod_name] 8010:8000 1>/dev/null 2>&1 &
# Wait for port-forward taking effect.
aibrix_benchmark -m deepseek-llm-7b-chat -o [path_to_benchmark_output]
Step 2: Decide the SLO and generate the profile#
Run aibrix_gen_profile -h for help. The first argument is the profile name; the samples use
<model>-<gpu> so that one model can have one profile per GPU type.
kubectl -n aibrix-system port-forward svc/aibrix-redis-master 6379:6379 1>/dev/null 2>&1 &
# Wait for port-forward taking effect.
aibrix_gen_profile deepseek-llm-7b-chat-v100 --cost [cost1] [SLO-metric] [SLO-value] -o "redis://localhost:6379/?model=deepseek-llm-7b-chat"
Now the GPU Optimizer is ready to work. Once it has enough trace data it reloads the profiles on its own; to force a reload right away:
kubectl -n aibrix-system port-forward svc/aibrix-gpu-optimizer 8080:8080 1>/dev/null 2>&1 &
curl http://localhost:8080/update_profile/deepseek-llm-7b-chat
Step 3: Deploy the PodAutoscaler#
It is simply a matter of applying the podautoscaler yaml file. The GPU optimizer exposes custom metrics which can be used by podautoscalers to make scaling decisions as explained above. One important thing you should note is that the deployment name and the name in scaleTargetRef in PodAutoscaler must be the same. That’s how AIBrix PodAutoscaler refers to the right deployment.
All the sample files can be found in the following directory.
https://github.com/vllm-project/aibrix/tree/main/samples/autoscaling
Example Optimizer-based KPA yaml config#
apiVersion: autoscaling.aibrix.ai/v1alpha1
kind: PodAutoscaler
metadata:
name: deepseek-r1-distill-llama-8b-optimizer-scaling
namespace: default
labels:
app.kubernetes.io/name: aibrix
app.kubernetes.io/managed-by: kustomize
annotations:
autoscaling.aibrix.ai/scale-down-cooldown-window: 0s
spec:
scalingStrategy: KPA
minReplicas: 1
maxReplicas: 8
metricsSources:
- endpoint: aibrix-gpu-optimizer.aibrix-system.svc.cluster.local:8080
metricSourceType: external
path: /metrics/default/deepseek-r1-distill-llama-8b
protocolType: http
targetMetric: vllm:deployment_replicas
targetValue: "100"
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: deepseek-r1-distill-llama-8b
The part that makes this an optimizer-driven autoscaler is the metricsSources entry: the
controller requests <protocolType>://<endpoint>/<path> and reads targetMetric from the
response by its literal Prometheus name, so path must be the optimizer’s per-deployment
route /metrics/<namespace>/<deployment> and targetMetric stays
vllm:deployment_replicas. minReplicas and maxReplicas bound what the optimizer may
request. The autoscaling.aibrix.ai/scale-down-cooldown-window: 0s annotation removes the
default five minute scale-down cooldown so replicas can follow the recommendation down without
delay; Metric-based Autoscaling lists the rest of that annotation family.
Verify#
The optimizer-based autoscaler decides the number of GPUs based on the offline GPU capacity profiling. It proactively calculates the overall capacity needed for serving requests under SLO and ensures that the GPU capacity is fully used but not overloaded. The GPU optimizer’s output is exposed as custom metrics. The following shows how these custom metrics can be checked.
kubectl -n aibrix-system port-forward svc/aibrix-gpu-optimizer 8080:8080
curl http://localhost:8080/metrics/default/deepseek-llm-7b-chat-v100
# HELP vllm:deployment_replicas Number of suggested replicas.
# TYPE vllm:deployment_replicas gauge
vllm:deployment_replicas{model_name="deepseek-llm-7b-chat"} 1
You should observe that the number of workload pods changes in response to the requests sent to the gateway.
GPU optimizer logs#
Gpu optimizer is an individual component that plays the role of collecting metrics from each pod. You can check its logs in this way. kubectl logs <aibrix-gpu-optimizer-podname> -n aibrix-system -f
{"time": "2025-02-12 06:23:52,086", "level": "INFO", "logger": "aibrix.gpu_optimizer.load_monitor", "message": "deepseek-llm-7b-chat optimization took 6.660938262939453 ms, cost $51.3324, coverage: 72.62180974477958%: [deepseek-llm-7b-chat-v100: 2($51.3324)]"}
In the above logs, the GPU optimizer returns the number of GPUs suggested, which is 2 in this example.
Configuration reference#
GPU optimizer deployment (aibrix-gpu-optimizer, aibrix-system). It runs
python -m aibrix.gpu_optimizer.app and listens on port 8080.
Environment variable |
Default |
Meaning |
|---|---|---|
|
|
Redis that holds request traces and profiles. |
|
|
Redis port. |
|
(unset) |
Redis password, if any. |
Gateway plugin: AIBRIX_GPU_OPTIMIZER_TRACING_FLAG (default false) turns request
tracing on.
Deployment labels read by the optimizer:
Label |
Meaning |
|---|---|
|
Required. Deployments without it are ignored, and the value must match the |
|
Replicas to keep when there is no traffic at all. Defaults to |
PodAutoscaler: use scalingStrategy: KPA and an external metric source whose
endpoint is aibrix-gpu-optimizer.aibrix-system.svc.cluster.local:8080, path is
/metrics/<namespace>/<deployment> and targetMetric is vllm:deployment_replicas. Set
minReplicas to 0 when the optimizer should be free to turn a deployment off; a higher
value overrides the recommendation. See Autoscaling for the remaining fields.
aibrix_gen_profile
Argument |
Meaning |
|---|---|
|
Profile name. Use the deployment name of the GPU type being profiled. |
|
Benchmark result file produced by |
|
Throughput SLO targets: requests per second, and tokens per second. |
|
Latency SLO targets in seconds: end-to-end, time to first token, time per all tokens, time per output token. Set whichever ones your SLO is defined on. |
|
|
|
Relative cost of this GPU type, default |
|
Output. A file path, or |
|
Print details of the generated profile. |
HTTP endpoints on the optimizer:
Endpoint |
Purpose |
|---|---|
|
Prometheus metrics for one deployment, including |
|
Reload profiles for a model from Redis. |
|
Start or stop monitoring a deployment by hand. Normally unnecessary: the optimizer discovers labelled deployments itself. |
|
Force a replica count for a monitored deployment. |
|
Dashboard visualising the observed workload pattern for a model. |
Preliminary experiments with different autoscalers#
Here we show the preliminary experiment results to show how different autoscaling mechanisms and configurations for autoscalers affect performance(latency) and cost (compute cost).
- Set up
Model: Deepseek 7B chatbot model
GPU type: V100
Max number of GPU: 8
HPA, KPA, and APA use metrics as the scaling metrics: 70.
Optimizer-based KPA SLO: E2E P99 100s
- Workload
- The overall RPS trend starts with low RPS and goes up relatively fast until T=500 to evaluate how different autoscaler and config reacts to the rapid load increase. After that, it goes down to low RPS quickly to evaluate scaling down behavior and goes up again slowly.
Average RPS trend: 0.5 RPS -> 2 RPS -> 4 RPS -> 5 RPS -> 1 RPS -> 3 RPS
Experiments Results#
gpu_cache_usage_perc: 70