Airflow 3This feature is only available for Airflow 3.x Deployments.
syncSlots value sets that number, and each concurrent task uses one slot. A worker that has no free slot does not accept more tasks, and the extra tasks stay in the queue.
Tasks that wait on an external system, such as a warehouse query or an API call, use very little CPU and memory. These tasks fill every slot on a worker while CPU utilization stays low. A Horizontal Pod Autoscaler (HPA) that scales on CPU or memory does not add replicas in this state, so the queue grows and task latency increases.
This document shows how to scale worker Pods on queue depth instead. Queue depth is the number of tasks in the queued and running states.
Choose a scaling method
The Remote Execution Agent Helm chart includes support for two approaches:
Astronomer recommends you use KEDA autoscaling whenever possible. This method needs no Prometheus and no metrics adapter, all configuration stays in
values.yaml, and it is the only method that supports scale-to-zero.
You can only use one method (HPA or KEDA) for each worker deployment.
Learn more about:
Slot capacity and queue depth
The task capacity of a worker deployment issyncSlots multiplied by the number of replicas. A deployment with syncSlots: 20 and two replicas can run 40 tasks at the same time.
Use desired capacity to configure autoscaling:
- Set
maxReplicaCountto your peak number of concurrent tasks divided bysyncSlots, rounded up. For a peak of 200 concurrent tasks andsyncSlots: 20, setmaxReplicaCount: 10. - If you are using HPA, set the metric target explicitly to the same value as
the worker’s
syncSlots. (You do not need to configure this explicitly with KEDA: the chart configures it for you.) - Set
minReplicaCountto the capacity you want available at all times. The minimum value ofminReplicaCountis 1 with HPA and 0 with KEDA.
syncSlots is the other way to add capacity for I/O-bound tasks. A slot holds a task process, so more slots need more memory on the worker Pod. Test a higher value against your own tasks before you use it in production.
Prerequisites
KEDA-based autoscaling requires:- Helm chart version
2.4.0or later. - Remote Execution Agent version
1.8.4or later. - Sentinel enabled with
sentinel.enabled: true. See Sentinel for Remote Execution Agents. - KEDA must be installed in the cluster. See Deploying KEDA.
- Remote Execution Agent version
1.7.0or later. - A Prometheus instance that scrapes the agent worker Pods. See Scrape metrics from Remote Execution Agents.
- prometheus-adapter, which serves the metric through the Kubernetes custom metrics API.
Autoscale with KEDA (recommended)
Simply setkeda.enabled: true on a worker configuration, and the chart will create a KEDA ScaledObject for that worker’s Deployment, pre-configured to scale the worker based on queue depth.
The backlog metric is provided by the Sentinel, so even when workers are scaled to zero KEDA can still query the backlog and will scale back up when work arrives.
1
Enable KEDA on the worker
Configure the worker in
values.yaml:values.yaml
minReplicaCount defaults to 0, which lets the worker scale to zero when its queues are empty. Set it higher to keep idle capacity ready for the first tasks of a run.maxReplicaCount defaults to 10. This default caps the Deployment at 10 × syncSlots concurrent tasks, so set it from your own peak concurrency if needed.Consult the chart’s default values.yaml (e.g. via helm show values astronomer/astro-remote-execution-agent) for additional configuration options you can use to tweak KEDA’s behavior.2
Apply the configuration and check the scaling
READY and ACTIVE columns of the ScaledObject report whether KEDA can read the backlog metric. ACTIVE is False for an idle worker, which is the state in which KEDA holds the Deployment at minReplicaCount.Autoscale with HPA and prometheus-adapter
This method uses theastro_agent_client_queue_stats metric, which each agent worker exposes on its /metrics endpoint, and it needs Prometheus and prometheus-adapter in the cluster. Only one adapter can serve custom.metrics.k8s.io in a cluster, so this method is unavailable if another component already occupies that API.
1
Scrape the workers with the Deployment name in the job label
The adapter rule in the next step maps the If you use a standalone Prometheus with static scrape configs, relabel the target so that
job label to a Kubernetes Deployment, so job must hold the name of the worker Deployment. The Helm chart creates one Service for each worker, labeled deploymentName: <resourceNamePrefix>-worker-<name>, which a ServiceMonitor can copy into job:worker-servicemonitor.yaml
job holds the same value.2
Publish the queue metric through prometheus-adapter
Add the following rule to your prometheus-adapter configuration:The counts come from the Astro orchestration plane in the worker heartbeat response, so every Pod of a worker Deployment reports the same values.
prometheus-adapter-values.yaml
max by (job, queue, state) removes the duplicate series, and sum by (job) adds the queued and running series together into one queue-depth value for each worker Deployment.3
Confirm that the custom metric is available
Query the custom metrics API for the worker Deployment. Replace The response contains a
re with your namespace and astro-worker-default-worker with your Deployment name:value field with the current queue depth. An error means that the adapter rule or the job label does not match. Fix this before you enable the HPA.4
Enable the HPA on the worker
Configure the worker in The
values.yaml:values.yaml
averageValue target of 20 matches syncSlots: 20, so the HPA runs one replica for each 20 tasks in the queue. See Metric target types for why queue depth needs an AverageValue target.The chart ignores replicas when hpa.enabled is true.5
Apply the configuration and check the HPA
TARGETS column shows the current queue depth against the target. If it shows <unknown>, run kubectl describe hpa -n re to see which metric the HPA cannot read.Metric target types
Kubernetes computes the replica count differently for each target type. UseAverageValue for queue depth.
A
Value target multiplies the ratio by the current replica count. With a queue-depth metric, the replica count therefore multiplies again in each scaling cycle for as long as the queue stays above the target, until the HPA reaches maxReplicaCount. For the full algorithm, see the Kubernetes HPA documentation.
Combine queue depth with resource metrics
Queue depth measures outstanding work, not the load on a Pod. Tasks that use a lot of memory can still exhaust a worker before its slots are full. With KEDA, add acpu or memory trigger to the generated ScaledObject with keda.extraTriggers, which takes a list of KEDA trigger objects:
values.yaml
hpa.extraMetrics, which takes Kubernetes HPA metric objects:
values.yaml
resources.requests on the worker, because the HPA calculates utilization against the request.
Scale down without task failures
A worker that stops during a task fails that task. These settings protect running work:terminationGracePeriodSecondson the worker gives a Pod time to finish its tasks before Kubernetes stops it. The default is600. Set it higher than your longest task if your tasks run for more than 10 minutes.keda.cooldownPeriodmakes KEDA wait after the backlog last justified more thanminReplicaCountreplicas before it scales back down. The default is300.hpa.behavior.scaleDown.stabilizationWindowSecondsmakes the HPA use the highest replica count it recommended over the trailing window, so a brief dip in queue depth does not remove a worker. The HPA examples on this page use300.