Carbon Aware SDK: Přesouvání pracovní zátěže napříč časem a prostorem
ICT sektor spotřebuje přibližně 460-700 TWh elektřiny ročně v globálních datových centrech, toto číslo se do roku 2030 zdvojnásobí díky explozi pracovních zátěží AI. Ale ne všechno energie je stejná: jedna kilowatthodina vyrobená v době, kdy je síť napájena z 90 %. Obnovitelná energie způsobuje desetinu emisí CO₂ ve srovnání s energií produkovanou během špičky pokryty plynovými elektrárnami. Stejná logika platí ve vesmíru: spusťte úlohu v evropském cloudovém regionu s čistým energetickým mixem může být čtyřikrát ekologičtější než region poháněný energií hlavně z uhlí.
Tento koncept — spusťte software Když e Kde elektřina je čistič — říká se tomu přesouvání poptávky o uhlíkové výpočty, a je to jeden ze základních principů zeleného softwarového inženýrství. Tam Green Software Foundation vytvořila referenční open source sadu nástrojů na tomto principu: the Carbon Aware SDK, dostupné na GitHubu pod licencí MIT a používané ve výrobě společnostmi jako UBS, Vestas a samotný Microsoft.
V tomto článku uvidíme, jak Carbon Aware SDK funguje zevnitř: architektura, zdroje dat, REST API, integrace s akcemi Python, Kubernetes a GitHub. Vytvoříme konkrétní příklady posouvání času (přesunout úlohu o 6-8 hodin, když intenzita uhlíku klesne) e posun umístění (směrujte úlohy do nejzelenější oblasti cloudu v reálném čase).
Co se naučíte
- Jak Carbon Aware SDK funguje: Architektura WebAPI, CLI a klientská knihovna
- Zdroje dat o intenzitě uhlíku: WattTime, ElectricityMaps, Ember Climate
- Time shifting: plánování dávkových úloh v oknech s nízkou intenzitou uhlíku
- Změna umístění: vyberte oblast cloudu s nejčistším energetickým mixem
- Integrace Pythonu s klientem Carbon Aware SDK
- Kubernetes + KEDA: automatické škálování s ohledem na uhlík beze změn v kódu aplikace
- Akce GitHubu: CI/CD potrubí s ohledem na uhlík s inteligentním plánováním
- Mezní vs průměrná intenzita uhlíku: jaký signál použít a proč
- Limity, kompromisy a skutečné případy použití s měřitelným snížením emisí
Green Software Engineering Series — Všechny články
| # | Titul | Podrobit |
|---|---|---|
| 1 | Principy zeleného softwarového inženýrství | 8 Principy GSF, SCI specifikace ISO/IEC 21031 |
| 2 | Měření emisí pomocí CodeCarbon | Sledování CO₂ v Pythonu, MLflow, dashboard |
| 3 | Climatiq API: Intenzita uhlíku v cloudových systémech | REST API, výpočet emisí cloudu a dodavatelský řetězec |
| 4 | Carbon Aware SDK (tento článek) | Time shifting, location shifting, Kubernetes, CI/CD |
| 5 | Rozsah 3 a potrubí ESG | Emise proti proudu/po proudu, datový kanál CSRD |
| 6 | Rozsah 1, 2, 3 modelování | Účetní rámce protokolu GHG, SBTi |
| 7 | AI uhlíková stopa | LLM školení, inference, energetická optimalizace |
| 8 | Udržitelné softwarové vzory | Návrh zeleného vzoru, efektivní architektury |
| 9 | ESG a CSRD pro software | Dodržování předpisů, povinné hlášení EU |
| 10 | GreenOps: Udržitelný provoz | FinOps+GreenOps, provozní metriky, kulturní posun |
Přesouvání poptávky: Princip výpočetní techniky využívající uhlík
Uhlíková intenzita kilowatthodiny elektřiny není pevná: mění se každou hodinu podle toho, kolik obnovitelné energie je k dispozici v síti. Za slunečného dne v Německu, uhlíková intenzita může klesnout pod i 100 g CO₂/kWh v hodinách elektrárny; noc, kdy je solární energie vypnutá a průmyslová poptávka klesá, stoupá výše 400 g CO₂/kWh. Stejná variabilita existuje ve vesmíru: Švédsko, krmení téměř výhradně z vodních a jaderných elektráren se pohybuje mezi 15 a 40 gCO₂/kWh, zatímco Polsko, stále závislý na uhlí, často přesahuje 700 gCO₂/kWh.
Il přesouvání poptávky využít tuto variabilitu přesunem flexibilních pracovních zátěží — dávkové zpracování, školení ML, zálohování, analýza dat, testování CI/CD — směrem k momentům a regionům, kde elektřina je čistší. Nejde o to vzdát se vypočítavosti: jde o to vybrat nejlepší čas spustit to.
Typy přesouvání poptávky
| Typ | Popis | Praktický příklad | Typické snížení CO₂ |
|---|---|---|---|
| Posun času | Přesuňte pracovní zátěž do časového okna s nízkou intenzitou uhlíku ve stejné oblasti | Spusťte trénink ML ve 2:00 místo ve 14:00 | 20–45 % |
| Posun polohy | Spusťte zátěž v cloudové oblasti s čistším mixem výkonu | Nasměrujte zakázku z us-východ-1 do eu-sever-1 (Stockholm) | 30–70 % |
| Formování poptávky | Omezte nabízené funkce, když je intenzita uhlíku vysoká | Deaktivujte rozlišení videa 4K během špičkových emisí | 10–25 % |
Je zřejmé, že ne všechny pracovní zátěže jsou pohyblivé: uživatelský požadavek HTTP musí být obsluhován okamžitě, finanční transakce nemůže čekat. Ale překvapivě vysoké procento zatížení podniková výpočetní technika je časově flexibilní: Školení ML modelu, ETL potrubí každou noc, generování zpráv, zálohování, bezpečnostní skenování, potrubí CI/CD. To jsou přesně ty ideální kandidáti pro plánování s ohledem na uhlík.
Carbon Aware SDK: Architektura a komponenty
Il Carbon Aware SDK je open source projekt Green Software Foundation,
uvolněna pod licencí MIT a v současné době ve stavu Absolventský projekt (nejvyšší úroveň
splatnosti v portfoliu GSF). Úložiště je na GitHubu pod
Green-Software-Foundation/carbon-aware-sdk a je vyvíjen hlavně v C# s a
ASP.NET Core WebAPI, ale odhaluje rozhraní, která lze použít v jakémkoli jazyce.
Architektura je modulární a točí se kolem klíčového konceptu: jediné rozhraní normalizované pro přístup k údajům o uhlíkové intenzitě od různých poskytovatelů, s výstupem vždy in gCO₂ekv/kWh bez ohledu na zdroj. To je zásadní, protože každý poskytovatel používá jiné jednotky, geografickou granularitu a metodiku výpočtu.
Součásti sady Carbon Aware SDK
| Komponent | Technologie | Použití | Kdy jej použít |
|---|---|---|---|
| WebAPI | ASP.NET Core (C#), Docker | REST API s Swagger/OpenAPI, nasaditelné jako mikroslužba | Integrace s jakýmkoli zásobníkem, architekturami mikroslužeb |
| CLI | dotnet nástroj, multiplatformní | Terminálový dotaz, skriptování bash/PowerShell | Deployment skripty, automatizace DevOps, rychlé testování |
| Knihovna SDK | Balíček NuGet (C#) | Přímá integrace do kódu aplikace .NET | .NET aplikace, které chtějí vestavěnou uhlíkovou logiku |
| Klient Python | openapi-generator, Python 3.8+ | Automaticky generovaný klient ze specifikace OpenAPI rozhraní WebAPI | ML potrubí, datové inženýrské skripty, Airflow DAG |
Typický tok je následující: roztočíte WebAPI jako kontejner Docker, nakonfigurován pomocí přihlašovacích údajů vašeho poskytovatele uhlíkové intenzity a jsou dotazovány koncové body REST z aplikace plánování. WebAPI se stará o normalizaci dat, správu hovorů poskytovatelům a vrátit optimální časová okna nebo regiony.
Zdroje dat: WattTime, ElectricityMaps a Ember Climate
Sada Carbon Aware SDK podporuje několik poskytovatelů dat o intenzitě uhlíku, z nichž každý má své vlastní funkce a různé geografické pokrytí. Výběr poskytovatele má důležité a metodologické důsledky (mezní vs. průměr) a praktické (pokrytí, náklady, frekvence aktualizací).
Porovnání poskytovatelů uhlíkové intenzity
| Poskytovatelé | Typ signálu | Krytí | Frekvence | Předpověď | Náklady |
|---|---|---|---|---|---|
| WattTime | Marginální (MOER) | Úplné USA, 50+ zemí | 5 minut | 24-72 hodin | Omezený bezplatný plán, komerční plány |
| Mapy elektřiny | Průměr (LCA) | 85+ zemí, celá EU | Hodinově/podhodinově | 24 hodin | Vývojářský bezplatný komerční plán |
| EmberClimate | Historický průměr | Globální (50+ zemí) | Denní/historické | Ne (pouze historické) | Otevřená data, zdarma |
| Statický JSON | Konfigurovatelné | Zvyk | Manuál | No | Zdarma (vývoj/testování) |
Marginální versus průměrná uhlíková intenzita: zásadní rozdíl
Volba mezi signálem okrajový e střední v tom je rozdíl je nejdůležitějším metodologickým přístupem CO2-aware computingu a má konkrétní dopady na výsledky dosažitelné.
Signál marginální (MOER — Marginal Operating Emissions Rate), poskytuje WattTime, odpovídá na otázku: „Kdybych právě teď zvýšil spotřebu o 1 kWh, která elektrárna byla uvedena do provozu, aby pokryla tuto dodatečnou poptávku?“. Odpověď je téměř vždy nejflexibilnější plynová elektrárna (tzv. „mezní generátor“), nikoli obnovitelné zdroje, které jsou již zapnuté na maximum. Tento signál je pro rozhodování nejdůležitější v reálném čase těch, kteří chtějí snížit emise způsobil z jeho přírůstkové spotřeby.
Signál průměr (LCA — Life Cycle Average), kterou poskytuje společnost ElectricityMaps, odpovídá místo toho na otázku: „Jaké jsou průměrné emise veškeré vyrobené elektřiny právě teď v síti?". V síti s 50 % solární energie a 50 % plynu je průměrný signál ~250 gCO₂/kWh, bez ohledu na to, co se děje na hranici. Tento signál je více vhodné pro ESG reporting a tržně založené Scope 2 účetnictví.
Důležité: Změna přístupu ElectricityMaps v roce 2025
V roce 2025 má ElectricityMaps okrajový signál je nespojitý z vašeho API s odvoláním na obavy ohledně ověřitelnosti údajů a souladu s předpisy EU a USA zakazující použití okrajových faktorů v účetnictví podle rozsahu 2. Mapy elektřiny nyní nabízí výhradně průměrné signály založené na metodologii LCA (Life Cycle Assessment). Pokud váš případ použití vyžaduje okrajový signál, WattTime zůstává jediným běžným poskytovatelem která to podporuje. Carbon Aware SDK řeší tento rozdíl transparentně prostřednictvím konfigurace poskytovatele.
Kdy použít který signál
| Use Case | Doporučený signál | Poskytovatelé | Proč |
|---|---|---|---|
| Dávkové úlohy s časovým posunem | Okrajový | WattTime | Maximalizuje skutečné snížení způsobených emisí |
| Přesun umístění ve více regionech | Střední nebo okrajové | ElectricityMaps nebo WattTime | Oba užitečné; stabilnější meziregionální průměr |
| Hlášení ESG / Rozsah 2 | Střední | Mapy elektřiny | Vyžadováno protokolem o skleníkových plynech a předpisy EU/USA |
| Vývoj a testování | Statický JSON | Místní soubor | Žádné náklady, deterministická data pro testování |
Nastavení a konfigurace sady Carbon Aware SDK
Nejrychlejší způsob, jak lokálně spustit Carbon Aware SDK, je pomocí Docker Compose. Podívejme se na kompletní konfiguraci s poskytovatelem ElectricityMaps (nejjednodušší konfigurovat díky bezplatnému API klíči pro vývojáře).
# docker-compose.yml - Carbon Aware SDK WebAPI
version: '3.8'
services:
carbon-aware-api:
image: ghcr.io/green-software-foundation/carbon-aware-sdk:latest
ports:
- "8080:80"
environment:
# Provider: ElectricityMaps (segnale medio LCA)
CarbonAwareVars__CarbonIntensityDataSource: "ElectricityMaps"
CarbonAwareVars__ElectricityMapsClient__APITokenHeader: "auth-token"
CarbonAwareVars__ElectricityMapsClient__APIToken: "${ELECTRICITY_MAPS_TOKEN}"
CarbonAwareVars__ElectricityMapsClient__BaseURL: "https://api.electricitymap.org/v3/"
# Oppure WattTime (segnale marginale MOER)
# CarbonAwareVars__CarbonIntensityDataSource: "WattTime"
# CarbonAwareVars__WattTimeClient__Username: "${WATTTIME_USER}"
# CarbonAwareVars__WattTimeClient__Password: "${WATTTIME_PASS}"
# CarbonAwareVars__WattTimeClient__BaseURL: "https://api2.watttime.org/v2/"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
interval: 30s
timeout: 10s
retries: 3
Pro vývojové a testovací prostředí můžete použít statický zdroj dat JSON, který nevyžaduje API klíč. Sada SDK obsahuje předem načtené ukázkové datové sady.
# appsettings.json - Configurazione con JSON statico (dev/test)
{
"CarbonAwareVars": {
"CarbonIntensityDataSource": "Json",
"JsonDataFileLocation": "./data/test-data.json",
"Proxy": {
"UseProxy": false
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Jakmile je kontejner spuštěn, dokumentace Swagger je k dispozici na adrese
http://localhost:8080/swagger. Zkontrolujeme, že vše funguje s jedním
zkušební volání do CLI:
# Installazione Carbon Aware CLI (dotnet tool)
dotnet tool install -g GSF.CarbonAware.Cli
# Verifica emissioni attuali in una location
carbon-aware emissions location --location "westeurope" \
--config ./appsettings.json
# Output esempio:
# Location: westeurope
# Time: 2025-09-15T14:30:00Z
# Rating: 180.5 gCO2eq/kWh
# Duration: PT1H
API Endpoints: The Heart of the Carbon Aware SDK
WebAPI odhaluje sadu koncových bodů REST zdokumentovaných pomocí OpenAPI/Swagger. Pojďme se podívat na ty hlavní s příklady skutečných volání a interpretací odpovědí.
GET /emise/bylocations — Aktuální emise podle umístění
Vrátí údaje o intenzitě uhlíku pro jedno nebo více míst v časovém intervalu. Používá se k porovnání aktuální uhlíkové intenzity mezi různými regiony (posun umístění).
# Confronto carbon intensity tra regioni cloud (location shifting)
GET /emissions/bylocations?locations=westeurope&locations=eastus&locations=northeurope
&time=2025-09-15T14:00:00Z
&toTime=2025-09-15T15:00:00Z
# Risposta JSON:
[
{
"location": "westeurope",
"timestamp": "2025-09-15T14:00:00Z",
"duration": 60,
"rating": 185.3
},
{
"location": "eastus",
"timestamp": "2025-09-15T14:00:00Z",
"duration": 60,
"rating": 312.7
},
{
"location": "northeurope",
"timestamp": "2025-09-15T14:00:00Z",
"duration": 60,
"rating": 32.1
}
]
# North Europe (Irlanda/Stoccolma) quasi 10x più verde di East US!
GET /emise/bylocation/best — Nejlepší umístění
Přímo vrací místo s nejnižší intenzitou uhlíku v rozsahu specifikováno. Ideální pro automatické směrování pracovní zátěže.
GET /emissions/bylocation/best?locations=westeurope&locations=eastus&locations=northeurope
&time=2025-09-15T14:00:00Z
&toTime=2025-09-15T15:00:00Z
# Risposta:
{
"location": "northeurope",
"timestamp": "2025-09-15T14:00:00Z",
"duration": 60,
"rating": 32.1
}
GET /emise/forecasts/current — Forecast Carbon Intensity
Toto je nejvýkonnější koncový bod pro posouvání času: vrací předpověď na příštích 24–72 hodin s optimálním časovým oknem, ve kterém bude zátěž spuštěna. Umožňuje určit dobu trvání úlohy, abyste našli nejlepší okno této délky.
GET /emissions/forecasts/current?locations=westeurope
&dataStartAt=2025-09-15T16:00:00Z
&dataEndAt=2025-09-16T16:00:00Z
&windowSize=60
# Risposta con finestra ottimale:
[
{
"generatedAt": "2025-09-15T14:30:00Z",
"location": "westeurope",
"dataStartAt": "2025-09-15T16:00:00Z",
"dataEndAt": "2025-09-16T16:00:00Z",
"windowSize": 60,
"optimalDataPoints": [
{
"location": "westeurope",
"timestamp": "2025-09-16T02:00:00Z",
"duration": 60,
"rating": 95.2
}
],
"forecastData": [
{ "timestamp": "2025-09-15T16:00:00Z", "rating": 195.8 },
{ "timestamp": "2025-09-15T17:00:00Z", "rating": 210.3 },
{ "timestamp": "2025-09-15T18:00:00Z", "rating": 230.1 },
{ "timestamp": "2025-09-15T22:00:00Z", "rating": 155.4 },
{ "timestamp": "2025-09-16T01:00:00Z", "rating": 105.7 },
{ "timestamp": "2025-09-16T02:00:00Z", "rating": 95.2 }, // OTTIMALE
{ "timestamp": "2025-09-16T03:00:00Z", "rating": 98.6 },
{ "timestamp": "2025-09-16T06:00:00Z", "rating": 120.3 }
]
}
]
# Il job da 1 ora dovrebbe partire alle 02:00 UTC (risparmio ~51% CO2 vs ora corrente)
POST /emise/forecasts/batch — Více předpovědí
Umožňuje odeslat dávku požadavků na prognózu pro naplánování více úloh současně, užitečné pro plánování složitých kanálů se závislostmi.
POST /emissions/forecasts/batch
Content-Type: application/json
[
{
"requestedAt": "2025-09-15T14:00:00Z",
"location": "westeurope",
"dataStartAt": "2025-09-15T20:00:00Z",
"dataEndAt": "2025-09-16T08:00:00Z",
"windowSize": 120
},
{
"requestedAt": "2025-09-15T14:00:00Z",
"location": "westeurope",
"dataStartAt": "2025-09-15T20:00:00Z",
"dataEndAt": "2025-09-16T08:00:00Z",
"windowSize": 30
}
]
Integrace Pythonu: CarbonAwareClient a Time Shifting
Pro kanály Pythonu – typicky datové inženýrství, školení ML nebo dávkovou analýzu – Sada Carbon Aware SDK nabízí klienta Pythonu automaticky generovaného specifikací OpenAPI. Podívejme se, jak vytvořit kompletní systém časového posunu pro trénink ML.
Instalace a konfigurace klienta Python
# requirements.txt
carbon-aware-sdk-client>=1.0.0 # Client auto-generato da OpenAPI
requests>=2.31.0
python-dateutil>=2.8.2
apscheduler>=3.10.4 # Scheduling job
pytz>=2023.3
# Installazione da PyPI (se disponibile) oppure da sorgente:
# pip install openapi-python-client
# openapi-python-client generate \
# --url http://localhost:8080/swagger/v1/swagger.json
Klient Carbon Aware v Pythonu
"""
carbon_aware_client.py
Client Python per il Carbon Aware SDK WebAPI
Implementa time shifting e location shifting
"""
import requests
from datetime import datetime, timedelta, timezone
from typing import Optional
import logging
logger = logging.getLogger(__name__)
class CarbonAwareClient:
"""
Client per il Carbon Aware SDK WebAPI.
Incapsula le chiamate REST e fornisce metodi ad alto livello
per time shifting e location shifting.
"""
def __init__(self, base_url: str = "http://localhost:8080"):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.headers.update({
"Content-Type": "application/json",
"Accept": "application/json"
})
def get_current_emissions(
self,
locations: list[str],
time: Optional[datetime] = None,
to_time: Optional[datetime] = None
) -> list[dict]:
"""
Recupera la carbon intensity corrente per una o più location.
Args:
locations: Lista di location name (es. ["westeurope", "eastus"])
time: Inizio intervallo (default: ora corrente)
to_time: Fine intervallo (default: time + 1 ora)
Returns:
Lista di dict con location, timestamp, rating (gCO2eq/kWh)
"""
now = datetime.now(timezone.utc)
time = time or now
to_time = to_time or (time + timedelta(hours=1))
params = {
"locations": locations,
"time": time.isoformat(),
"toTime": to_time.isoformat()
}
response = self.session.get(
f"{self.base_url}/emissions/bylocations",
params=params
)
response.raise_for_status()
return response.json()
def get_best_location(
self,
locations: list[str],
time: Optional[datetime] = None,
to_time: Optional[datetime] = None
) -> dict:
"""
Trova la location con la più bassa carbon intensity.
Returns:
Dict con location, timestamp, rating della migliore location
"""
now = datetime.now(timezone.utc)
time = time or now
to_time = to_time or (time + timedelta(hours=1))
params = {
"locations": locations,
"time": time.isoformat(),
"toTime": to_time.isoformat()
}
response = self.session.get(
f"{self.base_url}/emissions/bylocation/best",
params=params
)
response.raise_for_status()
return response.json()
def get_optimal_window(
self,
location: str,
window_size_minutes: int,
search_start: Optional[datetime] = None,
search_end: Optional[datetime] = None
) -> dict:
"""
Trova la finestra temporale ottimale (più bassa carbon intensity)
per eseguire un job di durata window_size_minutes nella location indicata.
Args:
location: Location name (es. "westeurope")
window_size_minutes: Durata del job in minuti
search_start: Inizio finestra di ricerca (default: ora corrente)
search_end: Fine finestra di ricerca (default: search_start + 24 ore)
Returns:
Dict con optimalDataPoints (timestamp ottimale) e forecastData
"""
now = datetime.now(timezone.utc)
search_start = search_start or now
search_end = search_end or (search_start + timedelta(hours=24))
params = {
"locations": [location],
"dataStartAt": search_start.isoformat(),
"dataEndAt": search_end.isoformat(),
"windowSize": window_size_minutes
}
response = self.session.get(
f"{self.base_url}/emissions/forecasts/current",
params=params
)
response.raise_for_status()
forecasts = response.json()
if not forecasts:
raise ValueError(f"Nessun forecast disponibile per {location}")
return forecasts[0]
def calculate_carbon_savings(
self,
current_rating: float,
optimal_rating: float,
duration_hours: float,
power_kw: float
) -> dict:
"""
Calcola il risparmio di CO2 del time shifting.
Args:
current_rating: Carbon intensity attuale (gCO2/kWh)
optimal_rating: Carbon intensity ottimale (gCO2/kWh)
duration_hours: Durata del job in ore
power_kw: Potenza media del job in kW
Returns:
Dict con emissioni attuali, ottimali e risparmio
"""
energy_kwh = duration_hours * power_kw
current_emissions_g = current_rating * energy_kwh
optimal_emissions_g = optimal_rating * energy_kwh
savings_g = current_emissions_g - optimal_emissions_g
savings_pct = (savings_g / current_emissions_g) * 100 if current_emissions_g > 0 else 0
return {
"energy_kwh": energy_kwh,
"current_emissions_gco2": round(current_emissions_g, 2),
"optimal_emissions_gco2": round(optimal_emissions_g, 2),
"savings_gco2": round(savings_g, 2),
"savings_percentage": round(savings_pct, 1)
}
Time Shifting: Plánovač pro trénink ML
"""
ml_training_scheduler.py
Scheduler carbon-aware per job di training ML.
Calcola la finestra ottimale nelle prossime 12 ore
e pianifica l'avvio del training.
"""
from datetime import datetime, timedelta, timezone
from apscheduler.schedulers.blocking import BlockingScheduler
from carbon_aware_client import CarbonAwareClient
import subprocess
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class MLTrainingScheduler:
"""
Scheduler carbon-aware per training ML.
Trova la finestra a più bassa carbon intensity
nelle prossime 12 ore e schedula il training.
"""
def __init__(
self,
location: str = "westeurope",
training_duration_minutes: int = 120,
max_wait_hours: int = 12,
carbon_aware_url: str = "http://localhost:8080"
):
self.location = location
self.training_duration_minutes = training_duration_minutes
self.max_wait_hours = max_wait_hours
self.client = CarbonAwareClient(base_url=carbon_aware_url)
self.scheduler = BlockingScheduler(timezone="UTC")
def find_optimal_start(self) -> datetime:
"""
Interroga il Carbon Aware SDK per trovare
il momento ottimale nelle prossime max_wait_hours.
"""
now = datetime.now(timezone.utc)
search_end = now + timedelta(hours=self.max_wait_hours)
logger.info(
f"Ricerca finestra ottimale in {self.location} "
f"per job da {self.training_duration_minutes} minuti..."
)
forecast = self.client.get_optimal_window(
location=self.location,
window_size_minutes=self.training_duration_minutes,
search_start=now,
search_end=search_end
)
optimal_points = forecast.get("optimalDataPoints", [])
if not optimal_points:
logger.warning("Nessuna finestra ottimale trovata, uso ora corrente")
return now
optimal_timestamp_str = optimal_points[0]["timestamp"]
optimal_timestamp = datetime.fromisoformat(
optimal_timestamp_str.replace("Z", "+00:00")
)
optimal_rating = optimal_points[0]["rating"]
# Recupera rating corrente per calcolo risparmio
current_data = self.client.get_current_emissions([self.location])
current_rating = current_data[0]["rating"] if current_data else 300.0
# Calcola risparmio
savings = self.client.calculate_carbon_savings(
current_rating=current_rating,
optimal_rating=optimal_rating,
duration_hours=self.training_duration_minutes / 60,
power_kw=150 # GPU server: ~150kW stima
)
wait_minutes = (optimal_timestamp - now).total_seconds() / 60
logger.info(
f"Finestra ottimale trovata:\n"
f" Inizio: {optimal_timestamp.strftime('%Y-%m-%d %H:%M UTC')}\n"
f" Carbon intensity: {optimal_rating:.1f} gCO2/kWh\n"
f" Carbon intensity attuale: {current_rating:.1f} gCO2/kWh\n"
f" Attesa: {wait_minutes:.0f} minuti\n"
f" Risparmio CO2: {savings['savings_gco2']:.0f}g ({savings['savings_percentage']}%)"
)
return optimal_timestamp
def run_training(self):
"""Esegue il job di training ML."""
logger.info("Avvio training ML carbon-optimized...")
result = subprocess.run(
["python", "train_model.py", "--config", "config.yaml"],
capture_output=True,
text=True
)
if result.returncode == 0:
logger.info("Training completato con successo")
else:
logger.error(f"Training fallito: {result.stderr}")
def schedule_and_run(self):
"""Trova la finestra ottimale e schedula il training."""
optimal_start = self.find_optimal_start()
now = datetime.now(timezone.utc)
if optimal_start <= now + timedelta(minutes=5):
# Avvio immediato se il momento ottimale è imminente
logger.info("Avvio immediato (finestra ottimale = ora)")
self.run_training()
else:
# Schedula l'avvio
self.scheduler.add_job(
self.run_training,
"date",
run_date=optimal_start,
id="ml_training"
)
logger.info(f"Training schedulato per {optimal_start}")
self.scheduler.start()
# Utilizzo:
if __name__ == "__main__":
scheduler = MLTrainingScheduler(
location="westeurope",
training_duration_minutes=120, # Job da 2 ore
max_wait_hours=12 # Aspetta massimo 12 ore
)
scheduler.schedule_and_run()
Posun místa: Směrování s ohledem na uhlík ve více regionech
Posun místa je často účinnější než posun času, protože ten rozdíl
uhlíková intenzita mezi oblastmi mraků může být mnohem větší než časová variace
v rámci jednoho regionu. Úloha provedená v
europe-north1 (Finsko, ~9 gCO₂/kWh) místo
asia-east1 (Tchaj-wan, ~550 gCO₂/kWh) snižuje emise o více než 98 %.
Průměrná uhlíková intenzita podle oblasti cloudu (2025)
| Poskytovatelé cloudu | Kraj | Umístění | Průměrná uhlíková intenzita (gCO₂/kWh) | Hlavní zdroj |
|---|---|---|---|---|
| Google Cloud | Evropa-sever 1 | Finsko | 9 | Vodní, jaderná |
| Blankyt | švédský centrální | Švédsko | 18 | Vodní, jaderná |
| Blankyt | severní evropa | Irsko | 280 | Vítr, plyn |
| AWS | eu-západ-1 | Irsko | 320 | Vítr, plyn |
| AWS | nás-východ-1 | Virginie | 380 | Plyn, jádro, uhlí |
| Google Cloud | Asie-východ 1 | Tchaj-wan | 545 | Svítiplyn |
| AWS | ap-jihovýchod-1 | Singapur | 408 | Zemní plyn |
Python: Automatické přesouvání umístění pro dávkové úlohy
"""
location_shifter.py
Location shifting carbon-aware per batch job multi-cloud.
Seleziona automaticamente la regione più verde
per eseguire un job Kubernetes.
"""
from carbon_aware_client import CarbonAwareClient
from datetime import datetime, timezone
import subprocess
import json
import logging
logger = logging.getLogger(__name__)
# Mapping location Carbon Aware SDK -> region cloud reale
LOCATION_TO_CLOUD_REGION = {
"northeurope": {
"aws": "eu-west-1",
"azure": "northeurope",
"gcp": "europe-west1"
},
"swedencentral": {
"azure": "swedencentral",
"gcp": "europe-north1"
},
"westeurope": {
"aws": "eu-central-1",
"azure": "westeurope",
"gcp": "europe-west4"
},
"eastus": {
"aws": "us-east-1",
"azure": "eastus",
"gcp": "us-east1"
}
}
CANDIDATE_LOCATIONS = ["swedencentral", "northeurope", "westeurope", "eastus"]
TARGET_CLOUD = "azure" # Cloud provider di destinazione
class LocationShifter:
def __init__(self, carbon_aware_url: str = "http://localhost:8080"):
self.client = CarbonAwareClient(base_url=carbon_aware_url)
def select_greenest_region(self) -> tuple[str, str, float]:
"""
Seleziona la regione cloud con la più bassa carbon intensity
tra le candidate.
Returns:
Tuple (location_name, cloud_region, carbon_intensity_rating)
"""
best = self.client.get_best_location(
locations=CANDIDATE_LOCATIONS
)
best_location = best["location"]
best_rating = best["rating"]
# Mappa a regione cloud reale
cloud_regions = LOCATION_TO_CLOUD_REGION.get(best_location, {})
cloud_region = cloud_regions.get(TARGET_CLOUD, "westeurope")
logger.info(
f"Regione selezionata: {best_location} -> {TARGET_CLOUD}:{cloud_region}\n"
f"Carbon intensity: {best_rating:.1f} gCO2/kWh"
)
# Log emissioni di tutte le candidate per confronto
all_emissions = self.client.get_current_emissions(CANDIDATE_LOCATIONS)
logger.info("Confronto regioni:")
for em in sorted(all_emissions, key=lambda x: x["rating"]):
marker = " <- SELEZIONATA" if em["location"] == best_location else ""
logger.info(f" {em['location']:20s} {em['rating']:6.1f} gCO2/kWh{marker}")
return best_location, cloud_region, best_rating
def deploy_job_to_region(self, cloud_region: str, job_config: dict):
"""
Deploya un batch job Kubernetes nella regione selezionata.
Usa kubectl con il context della regione target.
"""
# Sostituisce la regione nel manifest Kubernetes
manifest = job_config.copy()
manifest["metadata"]["annotations"]["target-region"] = cloud_region
manifest_json = json.dumps(manifest)
logger.info(f"Deploy job in regione {cloud_region}...")
result = subprocess.run(
["kubectl", "apply", "-f", "-", "--context", f"aks-{cloud_region}"],
input=manifest_json,
capture_output=True,
text=True
)
if result.returncode == 0:
logger.info(f"Job deployato con successo in {cloud_region}")
else:
raise RuntimeError(f"Deploy fallito: {result.stderr}")
def run_carbon_aware_job(self, job_config: dict):
"""Workflow completo: seleziona regione e deploya."""
location, cloud_region, rating = self.select_greenest_region()
logger.info(f"Avvio job carbon-aware in {cloud_region} ({rating:.1f} gCO2/kWh)")
self.deploy_job_to_region(cloud_region, job_config)
# Utilizzo
if __name__ == "__main__":
shifter = LocationShifter()
# Configurazione job Kubernetes
batch_job = {
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": {
"name": "data-processing-carbon-aware",
"annotations": {
"green-software.io/carbon-aware": "true",
"target-region": "" # Verrà popolato dal location shifter
}
},
"spec": {
"template": {
"spec": {
"containers": [{
"name": "processor",
"image": "myapp/data-processor:latest",
"resources": {
"requests": {"cpu": "2", "memory": "4Gi"},
"limits": {"cpu": "4", "memory": "8Gi"}
}
}],
"restartPolicy": "Never"
}
}
}
}
shifter.run_carbon_aware_job(batch_job)
Kubernetes a KEDA: Carbon-Aware Autoscaling
Pro produkční prostředí Kubernetes společnost Microsoft vydala Carbon Aware operátor KEDA (open source na Azure GitHub), která integruje Carbon Aware SDK přímo do škálovací vrstvy KEDA. Princip je jednoduchý: když je intenzita uhlíku nízká (dostupná čistá energie), KEDA může škálovat až do maximálního počtu replik; když je vysoká, maximální počet replikací se automaticky sníží.
Architektura se skládá ze tří prvků: Exportér Kubernetes Carbon Intensity (získejte data ze sady Carbon Aware SDK a tam vystaví jako ConfigMap v clusteru), the Carbon Aware operátor KEDA (přečte ConfigMap a aktualizuje limity škálování KEDA) a vlastní zdroj CarbonAwareKedaScaler který definuje prahové hodnoty měřítka.
Instalace nástroje Carbon Intensity Exporter
# Installa il Carbon Intensity Exporter con Helm
helm repo add azure-carbon https://azure.github.io/carbon-aware-keda-operator
helm repo update
# Crea il secret con le credenziali WattTime
kubectl create secret generic watttime-credentials \
--from-literal=username=MY_WATTTIME_USER \
--from-literal=password=MY_WATTTIME_PASS \
--namespace kube-system
# Installa l'exporter
helm install carbon-intensity-exporter azure-carbon/carbon-intensity-exporter \
--namespace kube-system \
--set carbonDataProvider=WattTime \
--set watttime.username=MY_WATTTIME_USER \
--set watttime.password=MY_WATTTIME_PASS \
--set location=westus \
--set forecastIntervalHours=12
ConfigMap vygenerovaný exportérem
apiVersion: v1
kind: ConfigMap
metadata:
name: carbon-intensity
namespace: kube-system
data:
# Recuperata ogni 12 ore dall'exporter
lastUpdated: "2025-09-15T14:00:00Z"
forecastDateTime: "2025-09-15T14:00:00Z"
message: "Carbon intensity data for westus"
# Array JSON con forecast delle prossime 24 ore
# Format: ISO timestamp + gCO2/kWh
forecastData: |
[
{ "timestamp": "2025-09-15T14:00:00Z", "intensity": 312.5 },
{ "timestamp": "2025-09-15T15:00:00Z", "intensity": 298.3 },
{ "timestamp": "2025-09-15T16:00:00Z", "intensity": 285.1 },
{ "timestamp": "2025-09-15T22:00:00Z", "intensity": 195.7 },
{ "timestamp": "2025-09-16T02:00:00Z", "intensity": 145.2 },
{ "timestamp": "2025-09-16T03:00:00Z", "intensity": 138.9 }
]
# Intensità corrente
currentIntensity: "312.5"
currentIntensityUnit: "gCO2/kWh"
CarbonAwareKedaScaler: Vlastní definice zdroje
apiVersion: carbonaware.azure.com/v1alpha1
kind: CarbonAwareKedaScaler
metadata:
name: batch-processor-carbon-scaler
namespace: default
spec:
# Riferimento al KEDA ScaledJob da controllare
kedaTargetRef:
apiVersion: keda.sh/v1alpha1
kind: ScaledJob
name: batch-processor-scaledjob
# Sorgente dati carbon intensity (ConfigMap dell'exporter)
carbonIntensityForecastDataSource:
localConfigMap:
name: carbon-intensity
namespace: kube-system
key: forecastData
mockCarbonForecast: false
# Soglie: definiscono maxReplicas in base all'intensità carbonica
# Ordinate per intensità crescente
# - Se intensità <= 150: max 20 repliche (energia molto pulita)
# - Se intensità <= 300: max 10 repliche (energia moderatamente pulita)
# - Se intensità <= 500: max 4 repliche (energia poco pulita)
# - Se intensità > 500: max 1 replica (energia sporca)
maxReplicasByCarbonIntensity:
- carbonIntensityThreshold: 150
maxReplicas: 20
- carbonIntensityThreshold: 300
maxReplicas: 10
- carbonIntensityThreshold: 500
maxReplicas: 4
ScaledJob KEDA pro dávkové zpracování
apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
name: batch-processor-scaledjob
namespace: default
labels:
app: batch-processor
green-software.io/carbon-aware: "true"
spec:
# Sorgente trigger: RabbitMQ, Kafka, Azure Queue, ecc.
jobTargetRef:
parallelism: 1
completions: 1
activeDeadlineSeconds: 3600
backoffLimit: 2
template:
metadata:
labels:
app: batch-processor
spec:
containers:
- name: processor
image: myapp/batch-processor:v2.1.0
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "2"
memory: "2Gi"
env:
- name: BATCH_SIZE
value: "1000"
- name: OUTPUT_BUCKET
value: "gs://my-processed-data"
restartPolicy: Never
# Trigger: coda Azure Service Bus
triggers:
- type: azure-servicebus
metadata:
queueName: batch-jobs
namespace: my-servicebus-namespace
messageCount: "50"
# Scaling: min 0, max viene controllato dal CarbonAwareKedaScaler
pollingInterval: 60 # Controlla la coda ogni 60 secondi
successfulJobsHistoryLimit: 5
failedJobsHistoryLimit: 3
minReplicaCount: 0
maxReplicaCount: 20 # Overridato dal CarbonAwareKedaScaler
Jak funguje Carbon Aware KEDA Operator v praxi
Každou hodinu čte Carbon Aware KEDA Operator aktualizovanou ConfigMap z exportéra a aktualizuje
pole maxReplicaCount KEDA ScaledJob na základě odpovídajícího prahu
na aktuální uhlíkovou intenzitu. Pokud je ve frontě 200 pracovních míst a uhlíková náročnost je
312 gCO₂/kWh (práh 300-500), KEDA může škálovat až pro maximálně 10 pracovníků
místo 20. Jakmile intenzita klesne pod 150 (např. ve 2:00 při silném větru),
limit se automaticky zvýší na 20 a úlohy se zpracují rychleji.
Nejsou nutné žádné změny v kódu aplikace.
Kubernetes CronJob Carbon-Aware s Init Containerem
Pro periodicky naplánované úlohy lze použít jiný vzor: a init kontejner který se před zahájením úlohy dotazuje na sadu Carbon Aware SDK a rozhodne, zda provést nebo odložit. Tento přístup nevyžaduje KEDA a funguje s standardní Kubernetes CronJobs.
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-etl-pipeline
namespace: default
annotations:
green-software.io/carbon-aware: "true"
green-software.io/max-intensity: "200"
spec:
# Schedulato per le 00:00 UTC ogni notte
# L'init container deciderà se eseguire o saltare
schedule: "0 0 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 7
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
initContainers:
# Init container che controlla la carbon intensity
- name: carbon-aware-check
image: curlimages/curl:8.5.0
env:
- name: CARBON_AWARE_API
value: "http://carbon-aware-api.monitoring.svc.cluster.local:8080"
- name: LOCATION
value: "westeurope"
- name: MAX_INTENSITY
value: "200"
command:
- sh
- -c
- |
set -e
echo "Controllo carbon intensity per ${LOCATION}..."
# Interroga il Carbon Aware SDK
RESPONSE=$(curl -sf "${CARBON_AWARE_API}/emissions/bylocations?locations=${LOCATION}")
INTENSITY=$(echo "$RESPONSE" | grep -o '"rating":[0-9.]*' | head -1 | cut -d: -f2)
echo "Carbon intensity corrente: ${INTENSITY} gCO2/kWh (massimo: ${MAX_INTENSITY})"
if [ $(echo "${INTENSITY} > ${MAX_INTENSITY}" | bc -l) -eq 1 ]; then
echo "ATTENZIONE: Intensità carbonica troppo alta (${INTENSITY} > ${MAX_INTENSITY})"
echo "Il job verrà rimandato al prossimo ciclo di scheduling"
exit 1 # Fallisce l'init container, il job non parte
fi
echo "OK: Intensità carbonica accettabile. Avvio job ETL..."
exit 0
containers:
- name: etl-pipeline
image: myapp/etl-pipeline:v1.5.0
command: ["python", "run_etl.py"]
env:
- name: PIPELINE_DATE
value: "$(date +%Y-%m-%d)"
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "4"
memory: "8Gi"
restartPolicy: Never
Akce GitHubu: Carbon-Aware CI/CD Pipeline
Testovací a sestavovací potrubí CI/CD patří k nejsnazším pracovním zátěžím, které umožňují zohledňovat uhlík, protože mnoho z nich je skutečně časově flexibilních: sada testů integrace může počkat 2–4 hodiny, aniž by to ovlivnilo pracovní postup vývojářů, zejména u potrubí v noci nebo podle plánu.
Studie z roku 2024 odhadla uhlíkovou stopu akcí GitHub pro celý ekosystém je to v pořadí 450–1000 MTCO₂ekv./rok. S plánováním s ohledem na uhlík i jen ty nejtěžší pracovní postupy (sestavení s kompilací, kompletní testovací sady, školení modelů), snížení by mohlo být významné.
Workflow Akce GitHubu s plánováním Carbon-Aware
# .github/workflows/carbon-aware-build.yml
# Pipeline CI/CD carbon-aware con scheduling intelligente
name: Carbon-Aware Build and Test
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
# Scheduled: ogni 6 ore per trovare la finestra migliore
schedule:
- cron: '0 */6 * * *'
workflow_dispatch:
inputs:
force_run:
description: 'Forza esecuzione indipendentemente dalla carbon intensity'
required: false
default: 'false'
env:
CARBON_AWARE_API: ${{ secrets.CARBON_AWARE_API_URL }}
MAX_INTENSITY: "250"
LOCATION: "westeurope"
jobs:
# Job 1: Controlla carbon intensity e decide se procedere
carbon-check:
name: Carbon Intensity Check
runs-on: ubuntu-latest
outputs:
should_run: ${{ steps.check.outputs.should_run }}
intensity: ${{ steps.check.outputs.intensity }}
optimal_time: ${{ steps.check.outputs.optimal_time }}
steps:
- name: Check Carbon Intensity
id: check
run: |
# Per PR e push diretti: esegui sempre (developer experience)
if [[ "${{ github.event_name }}" == "push" || \
"${{ github.event_name }}" == "pull_request" || \
"${{ github.event.inputs.force_run }}" == "true" ]]; then
echo "Event: ${{ github.event_name }} - Esecuzione forzata"
echo "should_run=true" >> $GITHUB_OUTPUT
echo "intensity=0" >> $GITHUB_OUTPUT
exit 0
fi
# Per scheduled e manual: controlla carbon intensity
RESPONSE=$(curl -sf \
"${CARBON_AWARE_API}/emissions/bylocations?locations=${LOCATION}" \
--max-time 30) || {
echo "WARN: Carbon Aware API non raggiungibile, esecuzione forzata"
echo "should_run=true" >> $GITHUB_OUTPUT
echo "intensity=-1" >> $GITHUB_OUTPUT
exit 0
}
INTENSITY=$(echo "$RESPONSE" | python3 -c "
import json, sys
data = json.load(sys.stdin)
print(data[0]['rating'] if data else 999)
")
echo "Carbon intensity corrente: ${INTENSITY} gCO2/kWh"
echo "Soglia massima: ${MAX_INTENSITY} gCO2/kWh"
echo "intensity=${INTENSITY}" >> $GITHUB_OUTPUT
if python3 -c "exit(0 if float('$INTENSITY') <= float('$MAX_INTENSITY') else 1)"; then
echo "Intensità accettabile - Avvio build"
echo "should_run=true" >> $GITHUB_OUTPUT
else
echo "Intensità troppo alta - Skip build"
echo "should_run=false" >> $GITHUB_OUTPUT
# Recupera finestra ottimale per info
FORECAST=$(curl -sf \
"${CARBON_AWARE_API}/emissions/forecasts/current?locations=${LOCATION}&windowSize=60")
OPTIMAL=$(echo "$FORECAST" | python3 -c "
import json, sys
data = json.load(sys.stdin)
pts = data[0].get('optimalDataPoints', []) if data else []
print(pts[0]['timestamp'] if pts else 'N/A')
")
echo "optimal_time=$OPTIMAL" >> $GITHUB_OUTPUT
echo "Finestra ottimale: $OPTIMAL"
fi
- name: Summary Carbon Check
run: |
echo "### Carbon Intensity Check" >> $GITHUB_STEP_SUMMARY
echo "| Parametro | Valore |" >> $GITHUB_STEP_SUMMARY
echo "|-----------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Location | ${LOCATION} |" >> $GITHUB_STEP_SUMMARY
echo "| Intensity | ${{ steps.check.outputs.intensity }} gCO2/kWh |" >> $GITHUB_STEP_SUMMARY
echo "| Max Threshold | ${MAX_INTENSITY} gCO2/kWh |" >> $GITHUB_STEP_SUMMARY
echo "| Will Run | ${{ steps.check.outputs.should_run }} |" >> $GITHUB_STEP_SUMMARY
# Job 2: Build e test (condizionale alla carbon intensity)
build-and-test:
name: Build and Test
runs-on: ubuntu-latest
needs: carbon-check
if: needs.carbon-check.outputs.should_run == 'true'
steps:
- uses: actions/checkout@v4
- name: Carbon Intensity Badge
run: |
echo "Esecuzione con carbon intensity: ${{ needs.carbon-check.outputs.intensity }} gCO2/kWh"
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Test
run: npm test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
Případová studie: Školení ML s 35% snížením CO₂
Předkládáme realistickou případovou studii založenou na vzorech zdokumentovaných v akademické literatuře (Springer Nature, 2024) a z podnikových implementací s Carbon Aware SDK.
Scénář: Výukový kanál modelu doporučení elektronického obchodu
| Parametr | Hodnota |
|---|---|
| Agentura | Elektronický obchod střední velikosti (500 tisíc aktivních zákazníků) |
| Pracovní zátěže | Týdenní školení modelu doporučení |
| Délka zaměstnání | 3-4 hodiny (závisí na velikosti datové sady) |
| Železářské zboží | 4x NVIDIA A100 (cloud VM), ~300W celkem |
| Spotřeba energie na jeden běh | ~1,2 kWh |
| Oblaková oblast | Azure westeurope (Amsterdam) |
| Frekvence | Jednou týdně (v neděli večer) |
| Časová flexibilita | 12 hodin (od neděle 20:00 do pondělí 8:00) |
Výsledky před a po přijetí sady Carbon Aware SDK
Srovnání emisí: před vs. po (na roční bázi)
| Metrický | Před (pevný rozvrh neděle 22:00) | Po (časový posun s ohledem na uhlík) | Variace |
|---|---|---|---|
| Průměrná uhlíková intenzita v době provedení | 265 g CO₂/kWh | 172 g CO₂/kWh | -35 % |
| Emise na jeden běh | 318 g CO2 | 206 g CO2 | -112 g (-35 %) |
| Roční emise (52 jízd) | 16,5 kg CO₂ | 10,7 kg CO₂ | -5,8 kg (-35 %) |
| Průměrný časový posun | 0 hodin | 6,2 hodiny | N/A |
| Úlohy provedené v rámci 12hodinového okna | 100 % | 100 % | Beze změny |
| Výpočetní náklady | Beze změny | Beze změny | 0% |
Výsledek -35 % emisí CO₂ bylo získáno jednoduše posunutí doby provádění v rámci již dostupného 12hodinového okna. Žádné změny školicího kódu, žádné dodatečné náklady, žádné omezení funkčnosti. V tom je síla výpočetní techniky s ohledem na uhlík: přínosy pro životní prostředí něco stojí provoz v blízkosti nuly pro časově flexibilní pracovní zátěž.
Případová studie 2: Carbon-Aware Multi-Region Deployment
Druhý scénář se týká posun umístění pro potrubí zpracování dat které jsou spouštěny v týdenních dávkách na globálně distribuovaném souboru dat.
Porovnání změn polohy: Emise podle regionu (4 hodiny práce, 500 W)
| Kraj | Průměrná intenzita uhlíku | Emise na běh (gCO₂) | vs. Nejlepší |
|---|---|---|---|
| Asie-východ1 (Tchaj-wan) | 545 g CO₂/kWh | 1090 g CO₂ | +11850 % (nejhorší případ) |
| us-východ-1 (Virginie) | 380 g CO₂/kWh | 760 g CO2 | +8160 % |
| eu-západ-1 (Irsko) | 280 g CO₂/kWh | 560 g CO2 | +5933 % |
| střední Švédsko (Azure) | 18 g CO₂/kWh | 36 g CO2 | +289 % |
| evropa-sever1 (Finsko) | 9 g CO₂/kWh | 9,2 g CO2 | NEJLEPŠÍ (-99 % oproti Tchaj-wanu) |
Umístění se posouvá směrem k europe-north1 v místě asia-east1
snižuje emise přes 99 % pro úplně stejnou pracovní zátěž.
Je zřejmé, že proveditelnost závisí na tom, kde se data nacházejí (závažnost dat), na požadavcích
předpisy o latenci a pobytu dat (GDPR atd.). Ale pro pracovní vytížení
analýzy přenosných datových sad, to je nejvýkonnější dostupný nástroj.
Porovnání s API národních provozovatelů sítí
Kromě poskytovatelů nativně podporovaných sadou Carbon Aware SDK existují veřejná rozhraní API národní provozovatelé sítí, kteří poskytují údaje o uhlíkové náročnosti nebo energetickém mixu v reálném čase. Ty jsou užitečné pro ty, kteří chtějí hyper-lokalizovaná data nebo na ně nechtějí být závislí od komerčních poskytovatelů.
Operátoři veřejné sítě API pro uhlíkovou intenzitu
| Operátor | Obec | API | Data jsou k dispozici | Poznámky |
|---|---|---|---|---|
| Terna | Itálie | transparentnost.terna.it | RT energetický mix, předpověď D+1 | Zdarma, REST JSON |
| RTE | Francie | data.rte-france.com | Energetický mix, emise CO₂, ceny | OAuth2, zdarma |
| CAISO | Kalifornie (USA) | caiso.com/awe | Obnovitelný pct, uhlíková intenzita | Zdarma, XML/JSON |
| Národní síť ESO | UK | carbonintensity.org.uk | Britská regionální uhlíková intenzita | Zdarma, REST JSON, předpověď |
| ENTSO-E | Evropa | transparentnost.entsoe.eu | Energetický mix 40+ zemí EU | Registrace, SFTP/API |
Integrace s Carbon Intensity UK API (National Grid ESO)
Britský National Grid ESO nabízí zvláště dobře zdokumentované bezplatné veřejné API, s regionálními údaji, prognózami a energetickým mixem podle oblastí. Je to skvělý výchozí bod pro ty, kteří chtějí experimentovat bez nákladů.
"""
uk_carbon_intensity.py
Integrazione con API National Grid ESO (UK) per carbon intensity.
Alternativa gratuita per workload in regioni UK.
"""
import requests
from datetime import datetime, timezone
class UKCarbonIntensityClient:
"""
Client per l'API pubblica National Grid ESO.
Documentazione: https://carbon-intensity.github.io/api-definitions/
"""
BASE_URL = "https://api.carbonintensity.org.uk"
def get_current_intensity(self) -> dict:
"""Intensità carbonica attuale per UK nazionale."""
response = requests.get(f"{self.BASE_URL}/intensity")
response.raise_for_status()
return response.json()["data"][0]
def get_regional_intensity(self, region_id: int) -> dict:
"""
Intensità per regione UK specifica.
Region IDs: 1=North Scotland, 6=Yorkshire, 13=South East, ecc.
"""
response = requests.get(f"{self.BASE_URL}/regional/regionid/{region_id}")
response.raise_for_status()
return response.json()["data"][0]
def get_forecast_48h(self) -> list[dict]:
"""Forecast 48 ore per UK nazionale."""
response = requests.get(f"{self.BASE_URL}/intensity/date")
response.raise_for_status()
return response.json()["data"]
def find_optimal_window(self, duration_hours: int = 2) -> dict:
"""
Trova la finestra a più bassa carbon intensity
nelle prossime 48 ore.
"""
forecast = self.get_forecast_48h()
# Calcola media su finestra scorrevole
best_window_start = None
best_avg_intensity = float("inf")
slots_per_window = duration_hours * 2 # Dati ogni 30 min
for i in range(len(forecast) - slots_per_window + 1):
window = forecast[i : i + slots_per_window]
intensities = [
slot["intensity"]["actual"] or slot["intensity"]["forecast"]
for slot in window
if (slot["intensity"]["actual"] or slot["intensity"]["forecast"]) is not None
]
if not intensities:
continue
avg_intensity = sum(intensities) / len(intensities)
if avg_intensity < best_avg_intensity:
best_avg_intensity = avg_intensity
best_window_start = window[0]["from"]
return {
"optimal_start": best_window_start,
"avg_intensity_gco2_kwh": round(best_avg_intensity, 1),
"duration_hours": duration_hours
}
# Utilizzo:
client = UKCarbonIntensityClient()
# Intensità corrente
current = client.get_current_intensity()
print(f"Carbon intensity UK ora: {current['intensity']['actual']} gCO2/kWh")
print(f"Indice: {current['intensity']['index']}") # very low / low / moderate / high / very high
# Finestra ottimale per job da 2 ore
optimal = client.find_optimal_window(duration_hours=2)
print(f"Finestra ottimale: {optimal['optimal_start']}")
print(f"Intensità media: {optimal['avg_intensity_gco2_kwh']} gCO2/kWh")
Limity a kompromisy: Latence versus udržitelnost
Výpočetní technika s ohledem na uhlík není řešením bez kompromisů. Je to zásadní porozumět praktickým limitům a kompromisům, aby nevytvářel více problémů, než řeší.
Limity, které je třeba zvážit před přijetím
-
Gravitace dat: Přemístění počítače je snadné, ale vaše data ano
být příliš objemné na přenos. Úloha, která přistupuje k 10 TB dat
us-east-1stěží má cenu to provozovateu-north-1pokud při převodu vzniká více CO₂, než jsou dosažené úspory. - Přesnost předpovědi: Předpovědi intenzity uhlíku na 24–72 hodin mají odchylku, která se zvyšuje s časovým horizontem. Povětrnostní podmínky (vítr, slunce) je obtížné předpovědět déle než 6-12 hodin s vysokou přesností.
- Latence versus udržitelnost: Časový posun zavádí inherentní latenci ve výsledcích. Pokud tým datové vědy čeká na výsledky školení, zpoždění o 8 hodin má skutečné výrobní náklady.
- Marginální versus průměrný paradox: Jak zdůraznil nedávný výzkum, optimalizace pro okrajový signál a průměrný signál může vést k opačným rozhodnutím. Výběr špatného signálu pro váš případ použití může dokonce zvýšit emise.
- Efekt odrazu: Pokud výpočetní technika s ohledem na uhlík podněcuje spotřebu více výpočtů během „zelených“ hodin, může být čistý efekt nulový nebo záporný (Jevonsův paradox aplikovaný na software).
- Geografické pokrytí: WattTime a ElectricityMaps mají pokrytí vynikající v USA a EU, ale omezené v mnoha rozvíjejících se zemích, kde jsou datová centra rychle rostou.
Matice proveditelnosti podle typu pracovní zátěže
Použitelnost Carbon-Aware Computing podle typu pracovní zátěže
| Typ pracovní zátěže | Posun času | Posun polohy | Motivace |
|---|---|---|---|
| Školení modelů ML | Optimální | Optimální | Vysoká flexibilita, vysoká energetická náročnost |
| Dávkový ETL potrubí | Optimální | Dobrý | Plánovač již existuje, závisí na datové lokalitě |
| Zálohování a replikace dat | Optimální | Omezený | Časově flexibilní, ale již naplánováno zálohování pro více regionů |
| Neblokující potrubí CI/CD | Dobrý | Dobrý | Záleží na prahu čekání, který tým akceptuje |
| Generování zprávy | Optimální | Omezený | Vysoká časová flexibilita, závisí na latenci SLA |
| Zobrazování API (v reálném čase) | Nelze použít | Omezený | Latence SLA neumožňuje dočasné posunutí |
| Transakční databáze | Nelze použít | Omezený | Konzistence a latence brání pohybu |
| Streamování v reálném čase (Kafka) | Nelze použít | Nelze použít | Kontinuální, citlivá na latenci, stavová |
Nejlepší postupy pro přijetí sady Carbon Aware SDK
Kontrolní seznam implementace výpočetní techniky s ohledem na uhlík
- Identifikujte časově flexibilní pracovní vytížení za prvé: katalogové dávkové zakázky s jejich přijatelným oknem flexibility. Začněte s nejvýkonnějšími úkoly spotřeba energie a maximální časová flexibilita.
- Vyberte si správného poskytovatele pro váš případ použití: WattTime pro optimalizaci skutečných mezních emisí; ElectricityMaps pro tržně založené ESG a hlášení rozsahu 2.
- Definujte jasné SLA: Určuje maximální přijatelné čekací okno pro každý typ práce. Školení ML může počkat 12 hodin; výkonnou zprávu musí být připraveno před 9:00.
- Implementujte elegantní nouzová řešení: Pokud není sada Carbon Aware SDK dostupná nebo prognóza není k dispozici, spusťte úlohu normálně. Nikdy neblokujte výroba pro optimalizaci životního prostředí.
- Změřte a nahlaste: Integrujte metriky uhlíkové intenzity do řídicích panelů provozní. Ukažte nahromaděné úspory, abyste motivovali tým a prokázali návratnost investic ESG.
- Kombinujte posun času a místa: maximalizovat úspory, najít optimální kombinaci momentu a oblasti, neoptimalizovat dva rozměry samostatně.
- Buďte opatrní při přenosu dat: Zohledňuje přenosové emise údaje ve výpočtu celkového posunu polohy. Někdy nejlepší "zelený" kraj není to výhodné kvůli nákladům na přenos dat.
- Nasaďte sadu Carbon Aware SDK jako postranní vozík nebo sdílenou službu: ne každý tým si musí nakonfigurovat vlastní nasazení. Centralizovaná instance s ukládání do mezipaměti snižuje náklady a zjednodušuje správu.
TypeScript: Nástroj Carbon-Aware pro Next.js a Node.js
// carbon-aware.utils.ts
// Utility TypeScript per integrazione Carbon Aware SDK
// Usabile in Next.js (SSR/cron), Node.js, Deno
interface EmissionsData {
location: string;
timestamp: string;
duration: number;
rating: number;
}
interface OptimalWindow {
optimalTimestamp: string;
rating: number;
currentRating: number;
savingsPercentage: number;
}
export class CarbonAwareUtils {
private readonly baseUrl: string;
constructor(baseUrl: string = process.env.CARBON_AWARE_API_URL ?? 'http://localhost:8080') {
this.baseUrl = baseUrl;
}
/**
* Controlla se la carbon intensity corrente è sotto la soglia.
* Usabile come guard per job schedulati.
*/
async isCarbonIntensityAcceptable(
location: string,
maxIntensityGco2: number
): Promise<{ acceptable: boolean; current: number }> {
try {
const url = new URL(`${this.baseUrl}/emissions/bylocations`);
url.searchParams.append('locations', location);
const response = await fetch(url.toString(), {
signal: AbortSignal.timeout(10_000)
});
if (!response.ok) {
// Fail open: se l'API è down, procedi comunque
console.warn(`Carbon Aware API error: ${response.status}. Proceeding anyway.`);
return { acceptable: true, current: -1 };
}
const data: EmissionsData[] = await response.json();
const current = data[0]?.rating ?? 0;
return {
acceptable: current <= maxIntensityGco2,
current
};
} catch (error) {
// Timeout o rete non disponibile: fail open
console.warn('Carbon Aware API unreachable. Proceeding with job.', error);
return { acceptable: true, current: -1 };
}
}
/**
* Trova la migliore regione cloud per eseguire un job ora.
*/
async getBestRegion(locations: string[]): Promise<EmissionsData> {
const url = new URL(`${this.baseUrl}/emissions/bylocation/best`);
locations.forEach(loc => url.searchParams.append('locations', loc));
const response = await fetch(url.toString());
if (!response.ok) {
throw new Error(`Carbon Aware API error: ${response.status}`);
}
return response.json() as Promise<EmissionsData>;
}
/**
* Trova la finestra temporale ottimale per le prossime N ore.
*/
async getOptimalWindow(
location: string,
jobDurationMinutes: number,
searchHours: number = 24
): Promise<OptimalWindow | null> {
const now = new Date();
const searchEnd = new Date(now.getTime() + searchHours * 60 * 60 * 1000);
const url = new URL(`${this.baseUrl}/emissions/forecasts/current`);
url.searchParams.append('locations', location);
url.searchParams.append('dataStartAt', now.toISOString());
url.searchParams.append('dataEndAt', searchEnd.toISOString());
url.searchParams.append('windowSize', jobDurationMinutes.toString());
const response = await fetch(url.toString());
if (!response.ok) return null;
const forecasts = await response.json() as Array<{
optimalDataPoints: Array<{ timestamp: string; rating: number }>;
forecastData: Array<{ timestamp: string; rating: number }>;
}>;
const forecast = forecasts[0];
if (!forecast?.optimalDataPoints?.length) return null;
const optimal = forecast.optimalDataPoints[0];
const currentRating = forecast.forecastData[0]?.rating ?? 0;
const savingsPercentage = currentRating > 0
? Math.round(((currentRating - optimal.rating) / currentRating) * 100)
: 0;
return {
optimalTimestamp: optimal.timestamp,
rating: optimal.rating,
currentRating,
savingsPercentage
};
}
}
// Decorator per funzioni che supportano carbon-aware execution
export function carbonAware(
location: string,
maxIntensity: number = 300
) {
return function (
_target: object,
_propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args: unknown[]) {
const client = new CarbonAwareUtils();
const { acceptable, current } = await client.isCarbonIntensityAcceptable(location, maxIntensity);
if (!acceptable) {
console.log(`Job skipped: carbon intensity ${current} gCO2/kWh > threshold ${maxIntensity}`);
return null;
}
console.log(`Job starting: carbon intensity ${current} gCO2/kWh (threshold: ${maxIntensity})`);
return originalMethod.apply(this, args);
};
return descriptor;
};
}
Závěry: Carbon-Aware Computing jako provozní standard
Carbon Aware SDK představuje dnes nejvyspělejší a nejlépe podporovaný nástroj pro implementovat přesouvání poptávky v podnikových softwarových systémech. S API Standardní REST, podpora hlavních poskytovatelů dat (WattTime, ElectricityMaps) a připravené integrace pro Kubernetes/KEDA a GitHub Actions, výrazně snižuje technická překážka pro přijetí zeleného softwarového inženýrství.
Výsledky jsou konkrétní a zdokumentované: 20-45% snížení emisí pro pracovní zátěž dávka s časovým posunem, až 99 % s přesunem polohy do evropských regionů poháněných vodní a jaderné. A klíčové je, že tyto úspory mají a provozní náklady blízké nule: nevyžadují jiný hardware, nesnižují funkce aplikace, nezvyšují náklady na cloud.
Směr je jasný: hlavní poskytovatelé cloudu integrují metriky uhlíkové intenzity v jejich konzolách (AWS Customer Carbon Footprint Tool, Google Cloud Carbon Footprint, Azure Emissions Impact Dashboard). Výpočetní technika s ohledem na uhlík, dnes rozlišovací prvek pokročilé, se v nadcházejících letech stane standardním požadavkem pro organizace podléhají předpisům ESG a CSRD. Začít nyní znamená získat dovednosti a infrastrukturu předem.
Další kroky
-
Prozkoumat oficiální úložiště su
github.com/Green-Software-Foundation/carbon-aware-sdka spusťte Docker kontejner lokálně s konfigurací JSON k provedení prvních experimentů bez API klíče. - Přečtěte si předchozí článek ze série: Climatiq API: Carbon Intensity nei Cloudové systémy, abyste pochopili, jak měřit emise před jejich optimalizací.
- Pokračujte s Rozsah 3 a potrubí ESG integrovat uhlíková data intenzita podávání zpráv o podnikové udržitelnosti vyžadovaná CSRD.
- Pokud pracujete s modely ML, zapátrejte hlouběji AI uhlíková stopa kde podívejme se, jak optimalizovat celý cyklus školení + inference pomocí technik kombinace plánování s ohledem na uhlík a účinnosti modelu.
- Přihlaste se k odběru Green Software Foundation jako bezplatný podporovatel pro přístup ke komunitě, technickým webinářům a certifikačnímu programu „Zelený softwarový praktik“.
Zdroje a reference
- Carbon Aware SDK — Green Software Foundation:
carbon-aware-sdk.greensoftware.foundation - Úložiště GitHub:
github.com/Green-Software-Foundation/carbon-aware-sdk - Operátor KEDA Azure Carbon Aware:
github.com/Azure/carbon-aware-keda-operator - Dokumentace rozhraní WattTime API:
docs.watttime.org - ElectricityMaps API:
portal.electricitymaps.com/developer-hub/api - UK Carbon Intensity API:
carbonintensity.org.uk - Springer 2024: „Strategie časového posunu pro uhlíkově efektivní trénink LLM“
- MDPI Sustainability 2025: „Carbon-Aware Spatio-Temporal Workload Shifting in Edge-Cloud“
- Green Software Foundation SCI Specifikace: ISO/IEC 21031:2024







