Carbon Aware SDK: mutarea sarcinilor de lucru în timp și spațiu
Sectorul TIC consumă aproximativ 460-700 TWh de energie electrică pe an în centrele de date globale, o cifră care se va dubla până în 2030, determinată de explozia volumului de lucru AI. Dar nu toate astea energia este aceeași: un kilowatt oră produs la un moment în care rețeaua este alimentată în proporție de 90%. Energia regenerabilă provoacă o zecime din emisiile de CO₂ în comparație cu cea produsă în timpul cererii de vârf acoperite de centrale pe gaz. Aceeași logică se aplică și în spațiu: rulați o lucrare într-o regiune cloud europeană cu un mix de energie curată poate fi de patru ori mai verde decât o regiune alimentată în principal din cărbune.
Acest concept — rulați software-ul Când e Unde electricitatea este mai curat — se numește schimbarea cererii o calculul conștient de carbon, și este unul dintre principiile fundamentale ale ingineriei software ecologice. Acolo Green Software Foundation a construit un set de instrumente open source de referință în jurul acestui principiu: the SDK Carbon Aware, disponibil pe GitHub sub licența MIT și utilizat în producție de companii precum UBS, Vestas și Microsoft însuși.
În acest articol vom vedea cum funcționează SDK-ul Carbon Aware din interior: arhitectură, surse de date, API REST, integrare cu Python, Kubernetes și GitHub Actions. Vom construi exemple concrete de schimbarea timpului (mută un loc de muncă 6-8 ore când intensitatea carbonului scade) e schimbarea locației (direcționați sarcinile de lucru către cea mai verde regiune de nor în timp real).
Ce vei învăța
- Cum funcționează SDK-ul Carbon Aware: arhitectură WebAPI, CLI și bibliotecă client
- Surse de date privind intensitatea carbonului: WattTime, ElectricityMaps, Ember Climate
- Schimbarea timpului: programarea sarcinilor de lot în ferestre cu intensitate scăzută de carbon
- Schimbarea locației: alegeți regiunea de nor cu cel mai curat mix de energie
- Integrarea Python cu clientul SDK Carbon Aware
- Kubernetes + KEDA: autoscaling conștient de carbon fără modificări ale codului aplicației
- Acțiuni GitHub: conductă CI/CD conștientă de carbon cu programare inteligentă
- Intensitatea de carbon marginală vs medie: ce semnal să folosiți și de ce
- Limite, compromisuri și cazuri reale de utilizare cu reducere măsurabilă a emisiilor
Seria Green Software Engineering — Toate articolele
| # | Titlu | Subiect |
|---|---|---|
| 1 | Principiile Green Software Engineering | 8 principii GSF, specificația SCI ISO/IEC 21031 |
| 2 | Măsurarea emisiilor cu CodeCarbon | Urmărirea CO₂ în Python, MLflow, tablou de bord |
| 3 | Climatiq API: Intensitatea carbonului în sistemele cloud | API REST, calculul emisiilor în cloud și lanțul de aprovizionare |
| 4 | Carbon Aware SDK (acest articol) | Schimbarea timpului, schimbarea locației, Kubernetes, CI/CD |
| 5 | Scope 3 și ESG Pipeline | Emisii în amonte/aval, conductă de date CSRD |
| 6 | Modelare Scope 1, 2, 3 | Cadre de contabilitate GHG Protocol, SBTi |
| 7 | Amprenta de carbon AI | Formare LLM, inferență, optimizare energetică |
| 8 | Modele software durabile | Design model verde, arhitecturi eficiente |
| 9 | ESG și CSRD pentru software | Conformitatea cu reglementările, raportarea obligatorie UE |
| 10 | GreenOps: Operațiuni durabile | FinOps+GreenOps, metrici operaționale, schimbare de cultură |
Schimbarea cererii: principiul din spatele calculului bazat pe carbon
Intensitatea carbonului unui kilowatt-oră de electricitate nu este fixă: variază în fiecare oră bazat pe cât de multă energie regenerabilă este disponibilă în rețea. Într-o zi însorită în Germania, intensitatea carbonului poate scădea sub i 100 gCO₂/kWh în orele centrale electrice; noaptea, cu energia solară oprită și cererea industrială în scădere, se ridică deasupra 400 gCO₂/kWh. Aceeași variabilitate există și în spațiu: Suedia, alimentată aproape în totalitate din hidroelectric și nuclear, variază între 15 și 40 gCO₂/kWh, în timp ce Polonia, încă dependent de cărbune, depășește adesea 700 gCO₂/kWh.
Il schimbarea cererii exploatează această variabilitate prin mutarea sarcinilor de lucru flexibile — procesare lot, instruire ML, backup, analiza datelor, testare CI/CD — către momentele și regiunile în care electricitatea este mai curată. Nu este vorba despre renunțarea la calcul: este vorba alege cel mai bun moment să-l ruleze.
Tipuri de schimbare a cererii
| Tip | Descriere | Exemplu practic | Reducere tipică de CO₂ |
|---|---|---|---|
| Schimbarea timpului | Mutați volumul de muncă într-o fereastră de timp cu intensitate scăzută de carbon în aceeași regiune | Rulați antrenamentul ML la 2:00 am în loc de 2:00 pm | 20-45% |
| Schimbarea locației | Rulați sarcina de lucru într-o regiune de nor cu un amestec de putere mai curat | Rutați locul de muncă de la us-east-1 la eu-north-1 (Stockholm) | 30-70% |
| Modelarea cererii | Reduceți caracteristicile oferite atunci când intensitatea carbonului este ridicată | Dezactivați rezoluția video 4K în timpul emisiilor de vârf | 10-25% |
Evident, nu toate sarcinile de lucru sunt mobile: o solicitare HTTP de utilizator trebuie servită imediat, o tranzacție financiară nu poate aștepta. Dar un procent surprinzător de mare de încărcări computerul de întreprindere este flexibil în timp: Training model ML, pipeline ETL seara, generare de rapoarte, backup, scanări de securitate, conductă CI/CD. Acestea sunt tocmai candidații ideali pentru programarea conștientă de carbon.
Carbon Aware SDK: Arhitectură și componente
Il SDK Carbon Aware este un proiect open source al Green Software Foundation,
lansat sub licența MIT și în prezent în stat Proiect absolvent (cel mai înalt nivel
de maturitate în portofoliul GSF). Depozitul este pe GitHub sub
Green-Software-Foundation/carbon-aware-sdk și este dezvoltat în principal în C# cu a
ASP.NET Core WebAPI, dar expune interfețe care pot fi utilizate de orice limbă.
Arhitectura este modulară și se învârte în jurul unui concept cheie: o singură interfață normalizat pentru a accesa date privind intensitatea carbonului de la diferiți furnizori, cu producție întotdeauna în gCO₂eq/kWh indiferent de sursa. Acest lucru este crucial pentru că fiecare furnizor folosește unități diferite, granularitate geografică și metodologii de calcul.
Componentele SDK-ului Carbon Aware
| Componentă | Tehnologie | Utilizare | Când să-l folosești |
|---|---|---|---|
| WebAPI | ASP.NET Core (C#), Docker | API REST cu Swagger/OpenAPI, implementabil ca microserviciu | Integrare cu orice stivă, arhitecturi de microservicii |
| CLI | instrument dotnet, multiplatformă | Interogare la terminal, scripting bash/PowerShell | Scripturi de implementare, automatizare DevOps, testare rapidă |
| Biblioteca SDK | Pachetul NuGet (C#) | Integrare directă în aplicația cod .NET | Aplicații .NET care doresc o logică încorporată în funcție de carbon |
| Client Python | openapi-generator, Python 3.8+ | Client generat automat din specificația OpenAPI a WebAPI | Conductă ML, scripturi de inginerie a datelor, Airflow DAG |
Fluxul tipic este următorul: rotiți WebAPI ca container Docker, configurat cu acreditările furnizorului dvs. de intensitate a carbonului, iar punctele finale sunt interogate REST din aplicația de programare. WebAPI se ocupă de normalizarea datelor, de gestionarea apelurilor furnizorilor și returnează ferestrele de timp sau regiunile optime.
Surse de date: WattTime, ElectricityMaps și Ember Climate
SDK-ul Carbon Aware acceptă mai mulți furnizori de date privind intensitatea carbonului, fiecare cu propriile caracteristici și acoperire geografică diferită. Alegerea furnizorului are implicații importante și metodologice (marginal vs mediu) și practic (acoperire, cost, frecvență de actualizare).
Comparația furnizorilor de intensitate a carbonului
| Furnizorii | Tip de semnal | Acoperire | Frecvenţă | Prognoza | Cost |
|---|---|---|---|---|---|
| WattTime | Marginal (MOER) | Întregul SUA, peste 50 de țări | 5 minute | 24-72 ore | Plan gratuit limitat, planuri comerciale |
| Electricity Maps | Medie (LCA) | 85+ țări, UE completă | Orar/suborar | 24 de ore | Dezvoltator gratuit, plan comercial |
| EmberClimate | Medie istorică | Global (+ 50 de țări) | Zilnic/istoric | Nu (doar istoric) | Date deschise, gratuit |
| JSON static | Configurabil | Personalizat | Manual | No | Gratuit (dezvoltare/testare) |
Intensitatea marginală vs medie a carbonului: o distincție crucială
Alegerea între semnal marginal e mediu asta e diferenta cea mai importantă abordare metodologică a calculului conștient de carbon și are implicații concrete asupra rezultatelor obținută.
Semnalul marginal (MOER — Rata de emisii de operare marginală), oferit de WattTime, raspunde la intrebare: „Dacă chiar acum mi-am mărit consumul cu 1 kWh, care centrală a intrat în funcțiune pentru a acoperi această cerere suplimentară?”. Răspunsul este aproape întotdeauna cea mai flexibilă centrală pe gaz (așa-numitul „generator marginal”), nu sursele regenerabile care sunt deja pornite la maxim. Acest semnal este cel mai relevant pentru decizii în timp real a celor care doresc să reducă emisiile cauzat din consumul său incremental.
Semnalul medie (LCA — Media ciclului de viață), oferit de ElectricityMaps, răspunde in schimb la intrebarea: „Care sunt emisiile medii ale întregii energie electrică produsă în rețea chiar acum?". Într-o rețea cu 50% solar și 50% gaz, semnalul mediu este ~250 gCO₂/kWh, indiferent de ce se întâmplă la margine. Acest semnal este mai mult potrivit pentru raportarea ESG și contabilitatea Scope 2 bazată pe piață.
Important: Schimbarea abordării ElectricityMaps în 2025
În 2025, ElectricityMaps are semnalul marginal este discontinuu din API-ul dvs invocând preocupări cu privire la verificarea datelor și alinierea la reglementările UE și SUA interzicând utilizarea factorilor marginali în contabilitatea Scope 2. Electricity Maps acum oferă exclusiv semnale medii bazate pe metodologia LCA (Life Cycle Assessment). Dacă cazul dvs. de utilizare necesită semnal marginal, WattTime rămâne singurul furnizor mainstream care o sustine. SDK-ul Carbon Aware gestionează această diferență în mod transparent prin intermediul configurarea furnizorului.
Când să utilizați ce semnal
| Caz de utilizare | Semnal recomandat | Furnizorii | De ce |
|---|---|---|---|
| Locuri de muncă cu schimbare de timp | Marginal | WattTime | Maximizează reducerea reală a emisiilor cauzate |
| Schimbarea locației în mai multe regiuni | Mediu sau marginal | ElectricityMaps sau WattTime | Ambele utile; medie inter-regională mai stabilă |
| Raportare ESG / Scope 2 | Mediu | Electricity Maps | Obligat de Protocolul GHG și reglementările UE/SUA |
| Dezvoltare și testare | JSON static | Fișier local | Fără costuri, date deterministe pentru testare |
Configurarea și configurarea SDK-ului Carbon Aware
Cel mai rapid mod de a lansa local SDK Carbon Aware este cu Docker Compose. Să vedem configurația completă cu ElectricityMaps ca furnizor (cel mai ușor de configurați datorită cheii API gratuite pentru dezvoltatori).
# 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
Pentru mediul de dezvoltare și testare, puteți utiliza sursa de date JSON statică care nu necesită cheie API. SDK-ul include seturi de date mostre preîncărcate.
# 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"
}
}
}
Odată ce containerul este pornit, documentația Swagger este disponibilă la
http://localhost:8080/swagger. Să verificăm dacă totul funcționează cu unul
apel de testare la 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
Puncte finale API: inima SDK-ului Carbon Aware
WebAPI-ul expune un set de puncte finale REST documentate cu OpenAPI/Swagger. Să le vedem pe cele principale cu exemple de apeluri reale și interpretarea răspunsurilor.
GET /emissions/bylocations — Emisii curente în funcție de locație
Returnează date privind intensitatea carbonului pentru una sau mai multe locații într-un interval de timp. Folosit pentru a compara intensitatea actuală a carbonului între diferite regiuni (schimbarea locației).
# 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 /emissions/bylocation/best — Cea mai bună locație
Acesta returnează direct locația cu cea mai scăzută intensitate de carbon din gamă specificat. Ideal pentru rutarea automată a sarcinilor de lucru.
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 /emissions/forecasts/current — Forecast Carbon Intensity
Acesta este cel mai puternic punct final pentru schimbarea timpului: se întoarce prognoza pentru următoarele 24-72 de ore cu fereastra de timp optimă în care să ruleze volumul de lucru. Vă permite să specificați durata lucrării pentru a găsi cea mai bună fereastră de acea lungime.
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 /emissions/forecasts/batch — Prognoze multiple
Vă permite să trimiteți un lot de solicitări de prognoză pentru a programa mai multe lucrări în același timp, util pentru programarea conductelor complexe cu dependențe.
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
}
]
Integrare Python: CarbonAwareClient și Time Shifting
Pentru pipeline Python - de obicei inginerie de date, antrenament ML sau analiză în loturi - SDK-ul Carbon Aware oferă un client Python generat automat de specificația OpenAPI. Să vedem cum să construim un sistem complet de schimbare a timpului pentru un job de formare ML.
Instalarea și configurarea clientului 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
Client Carbon Aware în Python
"""
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: Scheduler pentru ML Training
"""
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()
Schimbarea locației: rutare cu respectarea carbonului în mai multe regiuni
Schimbarea locației este adesea mai eficientă decât schimbarea timpului, deoarece diferența
intensitatea carbonului între regiunile norilor poate fi mult mai mare decât variația temporală
în cadrul unei singure regiuni. O lucrare executată în
europe-north1 (Finlanda, ~9 gCO₂/kWh) în loc de
asia-east1 (Taiwan, ~550 gCO₂/kWh) reduce emisiile cu peste 98%.
Intensitatea medie a carbonului în funcție de regiunea cloud (2025)
| Furnizori de cloud | Regiune | Locaţie | Intensitatea medie a carbonului (gCO₂/kWh) | Sursa principală |
|---|---|---|---|---|
| Google Cloud | europa-nord1 | Finlanda | 9 | Hidroelectric, nuclear |
| Azur | Suediacentrala | Suedia | 18 | Hidroelectric, nuclear |
| Azur | nordul europei | Irlanda | 280 | Vânt, gaz |
| AWS | eu-vest-1 | Irlanda | 320 | Vânt, gaz |
| AWS | noi-est-1 | Virginia | 380 | Gaz, nuclear, cărbune |
| Google Cloud | asia-est1 | Taiwan | 545 | Cărbune, gaz |
| AWS | ap-sud-est-1 | Singapore | 408 | Gaz natural |
Python: Schimbarea automată a locației pentru joburi în lot
"""
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 și KEDA: Carbon-Aware Autoscaling
Pentru mediile de producție Kubernetes, Microsoft a lansat Operator KEDA Carbon Aware (sursă deschisă pe Azure GitHub), care integrează SDK-ul Carbon Aware direct în stratul de scalare KEDA. Principiul este simplu: când intensitatea carbonului este scăzută (energie curată disponibilă), KEDA poate scala până la numărul maxim de replici; când este mare, numărul maxim a replicărilor este redusă automat.
Arhitectura este alcătuită din trei elemente: cel Exportator de intensitate a carbonului Kubernetes (obțineți datele din SDK-ul Carbon Aware și acolo expune ca ConfigMap în cluster), the Operator KEDA Carbon Aware (citește ConfigMap și actualizează limitele de scalare KEDA) și resursa personalizată CarbonAwareKedaScaler care defineşte pragurile de scalare.
Instalare 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 generat de exportator
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: definiție personalizată a resurselor
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 pentru procesarea în loturi
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
Cum funcționează în practică operatorul KEDA Carbon Aware
În fiecare oră, operatorul KEDA Carbon Aware citește ConfigMap actualizat de la exportator și actualizează
câmpul maxReplicaCount a KEDA ScaledJob pe baza pragului corespunzător
la intensitatea actuală a carbonului. Dacă sunt 200 de locuri de muncă în coadă și intensitatea carbonului este
312 gCO₂/kWh (pragul 300-500), KEDA poate scala până la maximum 10 lucrători
în loc de 20. De îndată ce intensitatea scade sub 150 (de exemplu, la 2:00 am cu vânt puternic),
limita crește automat la 20 și lucrările sunt procesate mai rapid.
Nu sunt necesare modificări ale codului aplicației.
Kubernetes CronJob Carbon-Aware cu Init Container
Pentru lucrările programate periodic, se poate folosi un model diferit: a container init care interogează SDK-ul Carbon Aware înainte de a începe lucrarea și decide dacă execută sau amână. Această abordare nu necesită KEDA și funcționează cu standardele 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
Acțiuni GitHub: Pipeline CI/CD Carbon-Aware
Testarea CI/CD și construirea conductelor sunt printre cele mai ușoare sarcini de lucru pentru a face conștient de carbon, deoarece multe dintre ele sunt de fapt flexibile în timp: o suită de teste de integrare poate aștepta 2-4 ore fără a afecta fluxul de lucru al dezvoltatorului, în special pentru conducte noapte sau programat.
Un studiu din 2024 a estimat amprenta de carbon a GitHub Actions pentru întregul ecosistem este în ordinea lui 450-1000 MTCO₂eq/an. Cu o programare conștientă de carbon chiar și doar cele mai grele fluxuri de lucru (construire cu compilare, suite de testare complete, instruire de modele), reducerea ar putea fi semnificativă.
Flux de lucru Acțiuni GitHub cu programare 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
Studiu de caz: Antrenament ML cu o reducere de 35% CO₂
Prezentăm un studiu de caz realist bazat pe modele documentate în literatura academică (Springer Nature, 2024) și din implementările întreprinderilor cu SDK-ul Carbon Aware.
Scenariu: Pipeline de instruire pentru modelul de recomandare pentru comerțul electronic
| Parametru | Valoare |
|---|---|
| Agenţie | Comerț electronic de dimensiuni medii (500.000 de clienți activi) |
| Sarcini de lucru | Instruire săptămânală a modelului de recomandare |
| Durata jobului | 3-4 ore (depinde de dimensiunea setului de date) |
| Hardware | 4x NVIDIA A100 (VM în cloud), ~300W total |
| Consum de energie pe cursă | ~1,2 kWh |
| Regiunea de nori | Azure vest Europa (Amsterdam) |
| Frecvenţă | O dată pe săptămână (duminică seara) |
| Flexibilitate temporală | 12 ore (de duminică 20:00 până luni 8:00) |
Rezultate înainte și după adoptarea SDK-ului Carbon Aware
Comparația emisiilor: înainte vs după (pe o bază anuală)
| Metric | Înainte (programare fixă duminică, ora 22:00) | După (decalare a timpului conștient de carbon) | Variaţie |
|---|---|---|---|
| Intensitatea medie a carbonului la momentul executării | 265 gCO₂/kWh | 172 gCO₂/kWh | -35% |
| Emisii pe o singură cursă | 318 gCO2 | 206 gCO2 | -112g (-35%) |
| Emisii anuale (52 de rulări) | 16,5 kg CO₂ | 10,7 kg CO₂ | -5,8 kg (-35%) |
| Schimbare medie de timp | 0 ore | 6,2 ore | N / A |
| Lucrări executate în intervalul de 12 ore | 100% | 100% | Neschimbat |
| Costul de calcul | Neschimbat | Neschimbat | 0% |
Rezultatul lui -35% emisii de CO₂ a fost obținut simplu mutarea timpului de execuție într-o fereastră de 12 ore deja disponibilă. Fără modificări ale codului de antrenament, fără costuri suplimentare, fără reducere a funcționalității. Acesta este punctul forte al calculului conștient de carbon: beneficiile de mediu au un cost funcționează aproape de zero pentru sarcini de lucru flexibile în timp.
Studiu de caz 2: Desfășurare în mai multe regiuni în funcție de carbon
Un al doilea scenariu se referă la schimbarea locației pentru conductele de prelucrare a datelor care sunt rulate în loturi săptămânale pe un set de date distribuit la nivel global.
Comparație privind schimbarea locației: emisii în funcție de regiune (4 ore de muncă, 500 W)
| Regiune | Intensitatea medie a carbonului | Emisii pe run (gCO₂) | vs. Cel mai bun |
|---|---|---|---|
| asia-est1 (Taiwan) | 545 gCO₂/kWh | 1090 gCO₂ | +11850% (cel mai rău caz) |
| us-east-1 (Virginia) | 380 gCO₂/kWh | 760 gCO₂ | +8160% |
| eu-west-1 (Irlanda) | 280 gCO₂/kWh | 560 gCO₂ | +5933% |
| swedencentral (Azure) | 18 gCO₂/kWh | 36 gCO₂ | +289% |
| europa-nord1 (Finlanda) | 9 gCO₂/kWh | 9,2 gC02 | CEL MAI BUN (-99% față de Taiwan) |
Locația se schimbă spre europe-north1 în locul de asia-east1
reduce emisiile peste 99% pentru exact aceeași sarcină de muncă.
Evident, fezabilitatea depinde de locul în care se află datele (gravitatea datelor), de cerințe
reglementări privind latența și rezidența datelor (GDPR etc.). Dar pentru sarcini de lucru
analiză pe seturi de date transferabile, aceasta este cea mai puternică pârghie disponibilă.
Comparație cu API-urile Operatorilor Naționali de Rețea
În plus față de furnizorii suportați nativ de Carbon Aware SDK, există API-uri publice ale operatorii naționali de rețea care furnizează date privind intensitatea carbonului sau mixul energetic în timp real. Acestea sunt utile pentru cei care doresc date hiper-localizate sau nu vor să depindă de acestea de la furnizorii comerciali.
Operatori publici de rețea API pentru intensitatea carbonului
| Operator | Sat | API-uri | Date disponibile | Note |
|---|---|---|---|---|
| Terna | Italia | transparenta.terna.it | Mix energetic RT, prognoză D+1 | Gratuit, REST JSON |
| RTE | Franţa | data.rte-france.com | Mix energetic, emisii de CO₂, prețuri | OAuth2, gratuit |
| CAISO | California (SUA) | caiso.com/awe | PCt din surse regenerabile, intensitatea carbonului | Gratuit, XML/JSON |
| National Grid ESO | UK | carbonintensity.org.uk | Intensitatea regională de carbon din Marea Britanie | Gratuit, REST JSON, prognoză |
| ENTSO-E | Europa | transparenta.entsoe.eu | Mix energetic peste 40 de țări din UE | Înregistrare, SFTP/API |
Integrare cu Carbon Intensity UK API (National Grid ESO)
National Grid ESO din Marea Britanie oferă un API public gratuit deosebit de bine documentat, cu date regionale, prognoze și mix energetic pe zonă. Este un punct de plecare grozav pentru cei care doresc să experimenteze fără costuri.
"""
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")
Limite și compromisuri: latență vs sustenabilitate
Calculul conștient de carbon nu este o soluție fără compromisuri. Este fundamental înțelegeți limitele practice și compromisurile pentru a nu crea mai multe probleme decât rezolvă.
Limite de luat în considerare înainte de adopție
-
Gravitatea datelor: Mutarea computerului este ușor, dar datele dvs. s-ar putea
fi prea voluminos pentru a fi transferat. O lucrare care accesează 10 TB de date
us-east-1abia merită să-l rulezieu-north-1dacă transferul emite mai mult CO₂ decât economiile realizate. - Precizia prognozei: Prognoza intensității carbonului pentru 24-72 de ore au o marjă de eroare care crește odată cu orizontul de timp. Conditiile meteo (vânt, soare) sunt greu de prezis peste 6-12 ore cu mare precizie.
- Latență vs sustenabilitate: Deplasarea timpului introduce o latență inerentă în rezultate. Dacă o echipă de știință a datelor așteaptă rezultatele unui job de formare, amânarea lui cu 8 ore are un cost real de producție.
- Paradoxul marginal vs mediu: După cum a subliniat cercetările recente, optimizarea pentru semnalul marginal și semnalul mediu poate duce la decizii opuse. Alegerea semnalului greșit pentru cazul dvs. de utilizare poate chiar crește emisiile.
- Efect de rebound: Dacă calculul conștient de carbon stimulează consumul mai multe calcule în timpul orelor „verzi”, efectul net ar putea fi zero sau negativ (Paradoxul Jevons aplicat software-ului).
- Acoperire geografică: WattTime și ElectricityMaps au acoperire excelent în SUA și UE, dar limitat în multe țări emergente în care centrele de date ele cresc rapid.
Matricea de fezabilitate în funcție de tipul sarcinii de lucru
Aplicabilitatea calculului Carbon-Aware în funcție de tipul de sarcină de lucru
| Tipul sarcinii de lucru | Schimbarea timpului | Schimbarea locației | Motivația |
|---|---|---|---|
| Antrenarea modelelor ML | Optimal | Optimal | Flexibilitate ridicată, intensitate energetică ridicată |
| Conductă ETL în loturi | Optimal | Bun | Scheduler deja prezent, depinde de localitatea datelor |
| Copiere de rezervă și replicare a datelor | Optimal | Limitat | Flexibil în timp, dar backup pentru mai multe regiuni deja programat |
| Conducte CI/CD neblocante | Bun | Bun | Depinde de pragul de așteptare acceptat de echipă |
| Generarea rapoartelor | Optimal | Limitat | Flexibilitate temporală ridicată, depinde de latența SLA |
| Difuzare API (în timp real) | Nu se aplică | Limitat | Latența SLA nu permite deplasarea temporală |
| Baza de date tranzacțională | Nu se aplică | Limitat | Consistența și latența împiedică mișcarea |
| Streaming în timp real (Kafka) | Nu se aplică | Nu se aplică | Continuu, sensibil la latență, cu stare |
Cele mai bune practici pentru adoptarea SDK-ului Carbon Aware
Lista de verificare a implementării calculului Carbon-Aware
- Identificați sarcinile de lucru flexibile în timp în primul rând: joburi de lot de catalog cu fereastra lor de flexibilitate acceptabilă. Începeți cu cele mai performante locuri de muncă consum de energie și flexibilitate maximă a timpului.
- Alegeți furnizorul potrivit pentru cazul dvs. de utilizare: WattTime pentru optimizare a emisiilor marginale reale; Electricity Maps pentru raportarea ESG bazată pe piață și Scope 2.
- Definiți SLA-uri clare: Specifică fereastra maximă de așteptare acceptabilă pentru fiecare tip de muncă. Un antrenament ML poate aștepta 12 ore; un raport executiv trebuie să fie gata înainte de ora 9.
- Implementați alternative grațioase: Dacă SDK-ul Carbon Aware nu este accesibil sau prognoza nu este disponibilă, rulați jobul în mod normal. Nu blocați niciodată producție pentru optimizarea mediului.
- Măsurați și raportați: Integrați valorile intensității carbonului în tablouri de bord operațională. Prezentați economiile acumulate pentru a motiva echipa și pentru a demonstra rentabilitatea investiției ESG.
- Combinați schimbarea timpului și a locației: pentru a maximiza economiile, găsiți combinația optimă de moment și regiune, nu optimizați cele două dimensiuni separat.
- Fii atent la transferul de date: Ia în considerare emisiile de transfer date în calculul deplasării totale a locației. Uneori, cea mai bună regiune „verde”. nu este convenabil din cauza costului transportului de date.
- Implementați SDK-ul Carbon Aware ca sidecar sau serviciu partajat: nu fiecare echipă trebuie să-și configureze propria desfășurare. O instanță centralizată cu memorarea în cache reduce costurile și simplifică gestionarea.
TypeScript: utilitar Carbon-Aware pentru Next.js și 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;
};
}
Concluzii: Carbon-Aware Computing ca standard operațional
SDK-ul Carbon Aware reprezintă astăzi cel mai matur și cel mai bine suportat instrument pentru implementează schimbarea cererii în sistemele software de întreprindere. Cu un API REST standard, suport pentru principalii furnizori de date (WattTime, ElectricityMaps) și integrări gata pentru Kubernetes/KEDA și GitHub Actions, reduce semnificativ barieră tehnică în calea adoptării ingineriei software ecologice.
Rezultatele sunt concrete și documentate: reducerea cu 20-45% a emisiilor pentru sarcinile de lucru lot cu schimbare de timp, până la 99% cu schimbarea locației în regiunile europene alimentate de hidroelectrice si nucleare. Iar punctul cheie este că aceste economii au un cost de operare aproape de zero: nu necesită hardware diferit, nu se reduc caracteristicile aplicației, nu crește costul cloud-ului.
Direcția este clară: furnizorii importanți de cloud integrează valori intensitatea carbonului în consolele lor (AWS Customer Carbon Footprint Tool, Google Cloud Carbon Footprint, Azure Emissions Impact Dashboard). Calculul conștient de carbon, astăzi un factor de diferențiere avansat, va deveni o cerință standard pentru organizații în următorii ani supuse reglementărilor ESG și CSRD. A începe acum înseamnă a dobândi competențe și infrastructură în avans.
Următorii pași
-
Explorați depozitul oficial su
github.com/Green-Software-Foundation/carbon-aware-sdkși începe Container Docker local cu configurația JSON pentru a face primele experimente fără cheie API. - Citiți articolul anterior din serie: Climatiq API: Carbon Intensity nei Sisteme cloud, pentru a înțelege cum să măsurați emisiile înainte de a le optimiza.
- Continuați cu Scope 3 și ESG Pipeline pentru a integra datele de carbon intensitatea raportării de sustenabilitate corporativă cerută de CSRD.
- Dacă lucrați cu modele ML, săpați mai adânc Amprenta de carbon AI unde să vedem cum să optimizăm întregul ciclu de antrenament + inferență prin tehnici combinații de programare conștientă de carbon și eficiență a modelului.
- Înscrieți-vă pentru Green Software Foundation ca suporter liber pentru accesați comunitatea, webinarii tehnice și programul de certificare „Practician de software verde”.
Resurse și referințe
- Carbon Aware SDK — Green Software Foundation:
carbon-aware-sdk.greensoftware.foundation - Depozitul GitHub:
github.com/Green-Software-Foundation/carbon-aware-sdk - Operator KEDA Azure Carbon Aware:
github.com/Azure/carbon-aware-keda-operator - Documentația API-ului WattTime:
docs.watttime.org - API ElectricityMaps:
portal.electricitymaps.com/developer-hub/api - API-ul pentru intensitatea carbonului din Marea Britanie:
carbonintensity.org.uk - Springer 2024: „Strategii de schimbare a timpului pentru formarea LLM eficientă din punct de vedere al carbonului”
- Sustenabilitate MDPI 2025: „Deplasarea sarcinii de lucru spațio-temporale în funcție de carbon în Edge-Cloud”
- Specificația Green Software Foundation SCI: ISO/IEC 21031:2024







