Sustainable Scaling: Building Carbon-Aware Kubernetes Clusters
For years, the primary metrics for scaling cloud infrastructure were CPU utilization, memory pressure, and request latency. While these metrics ensure performance and reliability, they ignore a critical external factor: the carbon intensity of the electrical grid powering the data center. As organizations commit to Net Zero targets, infrastructure engineering must evolve. We need to move beyond just 'efficient' code to 'carbon-aware' workloads.
Carbon-aware computing involves shifting compute tasks to times and locations where the energy grid is cleanest—specifically, when renewable sources like wind and solar are at their peak. In the Kubernetes ecosystem, this is best achieved by combining the Kubernetes Event-driven Autoscaling (KEDA) project with the Green Software Foundation’s Carbon-Aware SDK.
Understanding Carbon Intensity
Before diving into the implementation, we must define the core metric: Marginal Carbon Intensity (MCI). MCI represents the emissions of the marginal power plant that would be turned on (or off) to meet a change in demand. Unlike average intensity, MCI tells us the actual impact of adding one more kilowatt-hour of load to the grid.
Carbon awareness relies on two strategies:
- Temporal Shifting: Moving a workload to a later time when the grid is greener.
- Spatial Shifting: Moving a workload to a different geographic region where the grid is currently greener.
For most Kubernetes operators, temporal shifting via autoscaling is the most accessible starting point.
The Architectural Stack: KEDA and the Carbon-Aware SDK
To build a carbon-aware scaler, we need three components: a data source, a decision engine, and an actuator.
1. The Data Source: Carbon-Aware SDK
The Green Software Foundation’s Carbon-Aware SDK provides a standardized interface to fetch carbon intensity data from providers like WattTime or Electricity Maps. It abstracts the vendor-specific APIs, allowing you to query for the 'best' time to run a job within a specific window or to get the current intensity for a region.
2. The Actuator: KEDA
KEDA is a single-purpose event-driven autoscaler for Kubernetes. While the Horizontal Pod Autoscaler (HPA) typically looks at resource metrics, KEDA can scale based on external events. By using KEDA’s external or metrics-api scalers, we can feed carbon data directly into the Kubernetes control plane.
3. The Decision Engine: The Carbon-Aware Web API
By deploying the Carbon-Aware SDK as a Web API within your cluster, you create a middleware that KEDA can poll. This API evaluates the current grid status against your defined thresholds and tells KEDA whether to scale up or down.
Implementation Strategy: Setting Up the Carbon-Aware Scaler
To implement this, we will deploy the Carbon-Aware SDK as a service and configure KEDA to use it as an external metric source.
Step 1: Deploying the Carbon-Aware SDK
First, you need to host the Carbon-Aware SDK Web API. This service requires an API key from a provider (e.g., WattTime). You can deploy this as a standard Kubernetes Deployment.
apiVersion: apps/v1 kind: Deployment metadata: name: carbon-aware-api spec: replicas: 1 template: spec: containers: - name: api image: ghcr.io/green-software-foundation/carbon-aware-sdk-webapi:latest env: - name: CarbonAwareVars__CarbonIntensityDataSource value: "WattTime" - name: CarbonAwareVars__WattTime__Username valueFrom: { secretKeyRef: { name: watttime-creds, key: username } } - name: CarbonAwareVars__WattTime__Password valueFrom: { secretKeyRef: { name: watttime-creds, key: password } }
Step 2: Defining the Scaling Logic
The goal is to influence the minReplicas and maxReplicas of a workload based on carbon intensity. However, KEDA works best by providing a 'target value.'
We can define a logic where the API returns a 'Sustainability Score' from 0 to 100.
- High Intensity (Dirty Grid): Score = 0 (Scale down to minimum)
- Low Intensity (Clean Grid): Score = 100 (Scale up to maximum)
Step 3: Configuring the KEDA ScaledObject
Now, we create a ScaledObject that targets our background processing workload. We will use the external-push or metrics-api scaler. In this example, we use the metrics-api scaler to poll our Carbon-Aware SDK.
apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: carbon-aware-worker-scaler spec: scaleTargetRef: name: background-worker minReplicaCount: 1 maxReplicaCount: 20 triggers: - type: metrics-api metadata: targetValue: "100" url: "http://carbon-aware-api/emissions/binned-intensity" valueLocation: "currentScore" method: "GET"
In this configuration, if the currentScore returned by our API is 100 (clean grid), KEDA will allow the deployment to scale to its maximum. If the score drops (dirty grid), the calculated desired replica count decreases proportionately.
Real-World Use Case: Batch Processing and CI/CD
Not every workload is a candidate for carbon-aware scaling. User-facing APIs must remain responsive regardless of the grid's state. However, two categories are perfect for this approach:
1. Large-Scale Data Processing
If you are running daily ETL jobs or training ML models, these tasks are often delay-tolerant. By setting a KEDA threshold, you can ensure that the bulk of the compute power is consumed only when the grid intensity falls below a specific gCO2/kWh threshold. If the grid stays 'dirty' all day, the Carbon-Aware SDK's 'best window' feature can trigger the job at the least-bad time to ensure SLAs are still met.
2. CI/CD Runners
Ephemeral build agents are resource-intensive. By integrating carbon awareness into your GitHub Actions or GitLab runners on Kubernetes, you can prioritize non-critical builds (like documentation or staging deployments) during green energy peaks, while allowing 'hotfixes' to bypass the restriction.
Strategic Considerations and Trade-offs
Implementing carbon-aware scheduling is not without its challenges. Engineers must weigh sustainability against operational requirements.
The Cost of Delay
Temporal shifting introduces latency. If a batch job is delayed by 6 hours waiting for a wind-heavy grid, does that impact downstream business processes? You must define 'Carbon-SLA'—the maximum allowable delay for a task in exchange for reduced emissions.
Handling Data Gaps
Grid data isn't always perfect. APIs can go down or return stale data. Your KEDA implementation should always fail-open. If the Carbon-Aware SDK cannot be reached, the system should default to standard scaling behavior to ensure service availability.
Regional Nuances
Carbon intensity varies wildly by geography. A 'clean' hour in a coal-heavy region might still be 'dirtier' than a 'dirty' hour in a hydro-heavy region. If your organization operates across multiple cloud regions (e.g., AWS us-east-1 vs. eu-west-1), spatial shifting becomes more effective than temporal shifting, but it introduces complex data sovereignty and inter-region egress cost issues.
Measuring Success: Carbon-Aware Metrics
You cannot manage what you do not measure. To validate the impact of your KEDA configuration, you should export carbon-related metrics to Prometheus.
Useful metrics include:
- Saved gCO2: The difference between emissions at actual execution time vs. the emissions if the task had run immediately upon request.
- Carbon Opportunity Cost: The potential emission reduction missed due to SLA constraints.
- Grid Intensity Over Time: Correlation between your pod count and the MCI.
Using the Prometheus operator, you can visualize these metrics in Grafana, providing a 'Green Dashboard' for stakeholders that shows the tangible environmental impact of the engineering team's efforts.
The Path Forward
Carbon-aware workload scheduling is moving from a niche 'green' initiative to a standard part of the Cloud Native stack. By using KEDA and the Carbon-Aware SDK, you treat carbon as a first-class resource, much like CPU or memory.
To get started, follow these actionable steps:
- Identify Delay-Tolerant Workloads: Audit your current Kubernetes deployments for batch jobs, image processing, or non-urgent CI/CD tasks.
- Baseline Your Emissions: Use the Carbon-Aware SDK to track the current intensity of your primary regions without implementing scaling yet.
- Pilot a ScaledObject: Implement KEDA with a single non-critical workload, using a conservative threshold to observe how it behaves during grid fluctuations.
- Iterate and Expand: Gradually lower your carbon thresholds and incorporate spatial shifting for stateless workloads.
By integrating these tools, we stop viewing the cloud as an infinite, impact-free resource and start treating it as a dynamic participant in the global energy ecosystem.