Tekko

Language

Get in Touch →

Usually respond within 24 hours

Back to BlogDevOps

Carbon-Aware Scheduling: Shifting Kubernetes Workloads for Sustainability

8 min read
KubernetesGreen SoftwareCloud NativeSustainabilityCarbon-Aware SDK
Carbon-Aware Scheduling: Shifting Kubernetes Workloads for Sustainability

As engineers, we are trained to optimize for latency, throughput, and cost. We meticulously tune our JVM heap sizes, refactor SQL queries for better execution plans, and leverage spot instances to shave pennies off our cloud bills. However, there is a new metric entering the architectural conversation: carbon intensity.

Data centers account for nearly 1% of global energy-related greenhouse gas emissions. While moving to the cloud often improves efficiency through hardware multi-tenancy, the timing of our workloads still matters immensely. The energy powering your cloud provider’s region isn't a static mix; it fluctuates based on the availability of wind, solar, and fossil fuels.

This article explores how to implement carbon-aware workload scheduling using the Green Software Foundation’s (GSF) Carbon-Aware SDK and Kubernetes to dynamically shift non-urgent tasks to hours when the grid is cleanest.

The Concept: Temporal Shifting

Carbon-aware computing relies on two primary strategies: spatial shifting and temporal shifting.

Spatial shifting involves moving a workload to a different geographic region where the energy mix is currently greener. While effective, it introduces complexities regarding data residency, sovereignty, and cross-region egress costs.

Temporal shifting—the focus of this guide—is the practice of delaying a workload until a later time when the carbon intensity of the local grid is lower. For many of our daily engineering tasks—batch processing, CI/CD pipelines, model training, and data backups—the exact minute of execution is less important than the completion deadline. By aligning these tasks with peaks in renewable energy production, we can significantly reduce the carbon footprint of our infrastructure without changing a single line of business logic.

Understanding Carbon Intensity Data

To build a carbon-aware system, we need data. Carbon intensity is measured in grams of CO2 equivalent per kilowatt-hour (gCO2eq/kWh).

There are two types of intensity data:

  1. Average Carbon Intensity: The mean intensity of all energy generation in a region.
  2. Marginal Carbon Intensity: The intensity of the power plant that would be turned on (or off) to meet a change in demand.

For workload shifting, Marginal Carbon Intensity is the gold standard. If we choose to run a job at 2 PM instead of 6 PM, we want to know the actual difference in carbon emitted by the specific plants responding to that change. Organizations like WattTime and Electricity Maps provide APIs that offer this data, and the Carbon-Aware SDK acts as a standardized wrapper around these providers.

The Carbon-Aware SDK

The Green Software Foundation’s Carbon-Aware SDK provides a consistent interface to fetch carbon data. Instead of writing custom integrations for every energy data provider, you can use the SDK to query for the "best" time to run a job within a specific window.

The SDK is available as a CLI tool, a Web API (containerized), or a library for C#. For a Kubernetes environment, the Web API approach is usually the most flexible, as it allows various services to query carbon data via standard HTTP calls.

Setting Up the SDK

You can deploy the Carbon-Aware SDK as a sidecar or a standalone service in your cluster. Here is a conceptual example of how you might query the SDK's REST API to find the optimal window for a 2-hour job within the next 12 hours:

GET /emissions/bests?location=eastus&windowSize=120&startTime=2023-10-27T00:00:00Z&endTime=2023-10-27T12:00:00Z

The response provides the timestamp when the marginal carbon intensity is predicted to be at its lowest, allowing your scheduler to make an informed decision.

Identifying "Delayable" Workloads

Before implementing scheduling logic, you must categorize your workloads. Not everything should be carbon-aware.

  • User-Facing Services: These must remain highly available and responsive. We do not delay a user's login request because the sun isn't shining.
  • Critical Background Tasks: Immediate fraud detection or real-time alerts cannot wait.
  • Delayable/Non-Urgent Tasks: This is our target. Examples include daily database vacuuming, non-critical image resizing, periodic report generation, and training non-production ML models.

For these delayable tasks, we define a SLA (Service Level Agreement) or a Deadline. If a report must be ready by 8 AM, and it takes 30 minutes to run, our "search window" for the cleanest energy is from 8 PM the night before until 7:30 AM.

Implementing Carbon-Aware Scheduling in Kubernetes

There are three primary ways to implement this in a Kubernetes ecosystem: custom controllers, KEDA (Kubernetes Event-driven Autoscaling), or modified CronJobs.

1. The Carbon-Aware CronJob Wrapper

The simplest entry point is to wrap your existing CronJob logic. Instead of the Job starting the business logic immediately, the entrypoint script queries the Carbon-Aware SDK.

# Pseudo-code for a wrapper script import requests import time import os def main(): target_region = os.getenv("REGION") max_wait_hours = int(os.getenv("MAX_WAIT")) # Query SDK for the best time in the next X hours best_time = requests.get(f"http://carbon-aware-sdk/best?location={target_region}&window={max_wait_hours}").json() # Wait until the best time wait_seconds = (best_time['timestamp'] - current_time).total_seconds() if wait_seconds > 0: time.sleep(wait_seconds) # Execute actual workload run_batch_process()

While simple, this has a drawback: the pod sits in a Running state (consuming some idle resources) while sleeping. A more sophisticated approach involves a custom operator.

2. Using KEDA for Carbon-Aware Scaling

KEDA is a powerful tool for autoscaling Kubernetes workloads based on external events. While KEDA is often used for message queue depths, it can be extended to support carbon intensity.

By using the external or metrics-api scaler in KEDA, you can scale a deployment or a job to zero when carbon intensity is high and scale it up when it drops below a certain threshold.

Example ScaledObject configuration:

apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: carbon-aware-worker spec: scaleTargetRef: name: data-processor minReplicaCount: 0 maxReplicaCount: 10 triggers: - type: metrics-api metadata: targetValue: "100" # Threshold: 100g CO2eq/kWh url: "http://carbon-aware-sdk/current-intensity?location=eastus" valueLocation: "$.data[0].value"

In this scenario, if the current carbon intensity is above 100g, KEDA scales the data-processor pods to zero. When the intensity drops, KEDA spins them back up to handle the queue.

3. The Carbon-Aware K8s Operator (The Advanced Path)

For a truly robust solution, a custom Kubernetes Operator can manage the lifecycle of "Carbon-Aware Jobs." You would define a Custom Resource Definition (CRD) called CarbonAwareJob that includes a deadline and a durationEstimate.

The Operator's reconciliation loop would:

  1. Watch for new CarbonAwareJob objects.
  2. Query the Carbon-Aware SDK for the optimal execution window before the deadline.
  3. Schedule a standard Kubernetes Job to be created at that specific time.

This is the most efficient method as it doesn't consume cluster resources for "waiting" pods.

Operational Challenges and Considerations

Implementing carbon-aware scheduling is not without its hurdles. As a senior engineer, you must weigh these against the sustainability benefits.

Data Availability and Accuracy

Energy grids are complex. Forecasts provided by the SDK are just that—forecasts. A sudden drop in wind or a cloud cover over a solar farm can change the intensity rapidly. Your system must be resilient to missing or fluctuating data. A "fail-open" strategy is recommended: if the SDK is unreachable, run the job anyway to ensure business continuity.

Resource Contention

If every company in a region uses the same carbon-aware scheduling logic, we might inadvertently create a new "peak" in demand when the carbon intensity is low. This could potentially stress the grid or cause cloud provider capacity issues. Adding a small amount of jitter to your scheduled start times can help mitigate this.

Observability and Reporting

To prove the value of this implementation, you need to measure the carbon saved. This involves logging the carbon intensity at the time the job would have run (the original trigger) versus the intensity when it actually ran. This data can be aggregated into a Software Carbon Intensity (SCI) score, a standard metric for reporting the carbon footprint of software systems.

Practical Example: CI/CD Pipelines

Consider a large-scale CI/CD environment where hundreds of non-production integration tests run daily. These tests are resource-heavy but rarely need to be completed within minutes of a code push—overnight results are often sufficient for daily builds.

By integrating the Carbon-Aware SDK into your CI runner (e.g., a GitHub Actions runner or a GitLab Runner on K8s), you can queue these tests for the "greenest" window of the night. If your organization runs 1,000 hours of compute daily for testing, shifting this to a period with 40% lower carbon intensity results in a significant, measurable reduction in your annual carbon footprint.

Conclusion

Carbon-aware computing represents a shift in the definition of "efficient code." We are moving from an era where we treated the power grid as an infinite, clean resource to one where we acknowledge our role in grid demand management.

To get started:

  1. Audit your workloads: Identify batch jobs and non-production tasks with flexible deadlines.
  2. Deploy the Carbon-Aware SDK: Use the GSF’s containerized API to start gathering intensity data for your primary cloud regions.
  3. Start small: Implement a simple temporal shift for a single non-critical CronJob using a wrapper script or KEDA.
  4. Measure and iterate: Calculate your carbon savings and use those metrics to build the business case for wider adoption.

Sustainability is no longer just a corporate social responsibility (CSR) goal; it is an engineering challenge. By leveraging the tools we already use—Kubernetes, APIs, and smart scheduling—we can build software that is not only fast and reliable but also fundamentally kinder to the planet.