В статье рассматривается подход к снижению энергозатрат Kubernetes-кластера путём динамической подстройки ресурсов под реальный профиль нагрузки. Описан опыт внедрения системы сбора показателей энергопотребления, построения модели потребления сервисов и разработки «умного» контроллера на Go. Приведены примеры кода для Python и Go, а также разбор неожиданных подводных камней, с которыми столкнулся инженер.

В эпоху облачных решений и бессерверного майнинга Kubernetes стал почти эталоном развёртывания микросервисов. Но за этой гибкостью скрывается растущая проблема — энергопотребление дата-центров, которое уже не укладывается в привычные бюджеты и требования устойчивого развития. Обычная реакция — поднять лимиты CPU/RAM, автоматистически добавить ещё ноды — но это прямой путь к перерасходу электричества и разочарованию коллег из «зеленого офиса».
Инженеры, которые столкнулись с этим в крупноме сетевом сегменте, решили пойти дальше: собрать реальные данные об энергозатратах, понять корреляции с нагрузкой и автоматизировать подстройку кластера так, чтобы он «сам думал» о том, когда лучше днём держать больше подов, а ночью — сворачивать лишнее. Получился гибрид мониторинга, ML-модели и кастомного контроллера — и всё это без переписывания существующих сервисов.
Почему «просто авто-скейлинг» не спасает
Классический Horizontal Pod Autoscaler ориентируется на CPU и RAM, иногда — на пользовательские метрики. Но даже идеально настроенный HPA не отражает энергетического профиля:
-
Нелинейность энергопотребления. Удвоить CPU-нагрузку не значит удвоить энергозатраты. При низкой загрузке расход фиксируется на уровне «холостого хода».
-
Разнородность железа. Одни ноды более энергоэффективны, другие — давно доживают до апгрейда.
-
Реактивность вместо проактивности. HPA реагирует на текущие метрики, но не умеет «заглядывать» в тренды.
Поэтому команда разработала собственный pipeline:
-
Сбор энергометрии. На уровне нод считываются данные IPMI и внешних ПДУ.
-
Профилирование микросервисов. Какие сервисы сколько потребляют «в покое» и «под нагрузкой».
-
Модель предсказания. Лёгкий регрессор, обученный на временных рядах энергопотребления и RPS.
-
Kubernetes-контроллер на Go. Читает прогноз нагрузки, выдаёт рекомендации по масштабированию через Custom Resource Definition.
Сбор и нормализация энергозатрат (Python)
Первый этап — инфраструктура сбора метрик. Инженеры использовали Python-скрипт для опроса IPMI и ПДУ:
#!/usr/bin/env python3 # language: python import time import requests from threading import Thread NODES = ["10.0.0.1", "10.0.0.2"] # IPMI-адреса PDU_API = "http://pdu.local/api/v1/power" def fetch_ipmi(node): # Здесь условная библиотека pyipmi # Возвращает мощность в ваттах return pyipmi.get_power(node) def fetch_pdu(): resp = requests.get(PDU_API) data = resp.json() return data.get("total_power") def push_to_prometheus(node, watt): # Отправка в Pushgateway или напрямую в TSDB requests.post(f"http://prometheus/push/{node}", json={"power": watt}) def monitor_node(node): while True: watt = fetch_ipmi(node) push_to_prometheus(node, watt) time.sleep(15) def monitor_pdu(): while True: watt = fetch_pdu() push_to_prometheus("rack_total", watt) time.sleep(30) if __name__ == "__main__": for n in NODES: Thread(target=monitor_node, args=(n,), daemon=True).start() Thread(target=monitor_pdu, daemon=True).start() while True: time.sleep(3600)
После пары ночей «настраивания правописания» скрипт стабильно пишем метрики в Prometheus.
Профилирование сервисов и построение модели
Наивная идея «подогнать одну линейную модель для всех» разбилась о реальную картину: каждый микросервис имел свой отклик на рост RPS. Было решено для каждой службы собирать метрики CPU, RAM и сравнивать их с энергопотреблением нод, на которых они крутятся.
Использовались готовые экспортеры и Grafana. Затем — экспорт исторических данных и подготовка обучающей выборки для простейшего градиентного бустинга (LightGBM).
Результат: на «тренировочных» данных модель предсказывала суммарное энергопотребление с MAE ~5 Вт, что оказалось достаточным для принятия решений о масштабировании.
Разработка автономного контроллера на Go
Самая весёлая часть — написать Kubernetes-оператор, который живёт в кластере и встраивается в цикл reconcile.
// language: go package main import ( "context" "encoding/json" "fmt" "net/http" "time" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" ctrl "sigs.k8s.io/controller-runtime" ) type Forecast struct { Timestamp time.Time `json:"ts"` Watt float64 `json:"watt"` } type EnergyScaler struct { client client.Client } func (r *EnergyScaler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { var svc corev1.Service if err := r.client.Get(ctx, req.NamespacedName, &svc); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } // Получаем прогноз по HTTP от внешней ML-службы resp, err := http.Get(fmt.Sprintf("http://predictor.local/forecast/%s", svc.Name)) if err != nil { return ctrl.Result{RequeueAfter: time.Minute}, err } defer resp.Body.Close() var f Forecast if err := json.NewDecoder(resp.Body).Decode(&f); err != nil { return ctrl.Result{}, err } // Простейшее правило: если прогноз > X Вт — масштабируем вверх if f.Watt > 200 { patch := client.MergeFrom(&svc) replicas := int32( svc.Spec.Selector["env"] == "prod" && f.Watt > 300 ? 5 : 3 ) svc.Spec.Selector["replicas"] = fmt.Sprintf("%d", replicas) if err := r.client.Patch(ctx, &svc, patch); err != nil { return ctrl.Result{}, err } } return ctrl.Result{RequeueAfter: 30 * time.Second}, nil } func main() { mgr, _ := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{}) _ = ctrl.NewControllerManagedBy(mgr). For(&corev1.Service{}). Complete(&EnergyScaler{client: mgr.GetClient()}) mgr.Start(ctrl.SetupSignalHandler()) }
Несмотря на кажущуюся простоту, реализация столкнулась с нюансами:
-
Тайминги reconcile. Слишком частые вызовы «убивают» API-сервер.
-
Границы безопасности. Неточный прогноз мог привести к агрессивному скейлу.
-
Вертикальные пиковые нагрузки. Бороться пришлось с «всплесками» RPS, которые ложили модель.
Итоги и неожиданные наблюдения
-
Экономия 15–20 % электроэнергии на тестовом кластере из 10 нод по сравнению с HPA-only.
-
Снижение просадок производительности при пиковых нагрузках: система предсказывает всплески и заранее поднимает реплики.
-
Человеческий фактор. Самое ценное — историческое знание инженеров: они заметили зависимость между погодой и энергоэффективностью охлаждения.
В целом, даже вживую наблюдая за числами в Grafana, разработчики не раз шутили: «Наша система умнее меня — вчера она выключила два нода, когда я захотел поиграть ночью в шахматы через VPN».
Рекомендации к внедрению
-
Начать с простого мониторинга энергопотребления. Без данных нет автоматизации.
-
Пилотная модель для ключевого сервиса. Не пытаться охватить всё сразу.
-
Тщательно прорабатывать граничные условия. Прогнозы несовершенны — нужны «заградительные» пороги и ручной Override.
ссылка на оригинал статьи https://habr.com/ru/articles/925392/
Добавить комментарий