Spot Management — preemptible-ноды без ложных гарантий
Oopps.ai помогает перенести подходящие stateless workloads на более дешёвые preemptible-ноды. Эта функция снижает стоимость, но сама по себе не гарантирует непрерывную доступность приложения.
По умолчанию Oopps сохраняет одну фактически Ready on-demand
ноду. Для явно отмеченного stateless Deployment можно дополнительно держать
одну service-visible warm replica. В строгом isolated live-E2E тесте
продуктовая Spot-конверсия прошла без ошибок и таймаутов в двух
независимых probes. Это не гарантирует бесшовность любого приложения:
свой SLO подтверждайте external и internal probes в собственном fault-test.
Обзор
Preemptible (spot) ноды в Yandex Cloud обычно дешевле on-demand, но провайдер может остановить их в любой момент. Spot Management анализирует workloads на каждой on-demand node group, показывает причины несовместимости и по подтверждению создаёт preemptible-клон группы.
Это не разовая замена VM и не список доступных инстансов. После подтверждения
безопасной node group Oopps создаёт её preemptible-клон и копирует в него
действующие правила autoscaling. После переноса исходная on-demand группа
по умолчанию сохраняется с минимальным или фиксированным размером 1,
а product apply ждёт физическую Ready ноду. Размер резерва можно
изменить в настройках; значение 0 означает явный холодный резерв.
Как устроен резерв в одном кластере
Один Kubernetes-кластер
├── workers-spot 1…N preemptible-нод, обслуживают основную нагрузку
└── workers 1 on-demand нода по умолчанию
└── api-oopps-fallback 1 opt-in service-visible replica
- Capacity и replica настраиваются отдельно. Одна on-demand нода сохраняется по умолчанию. Warm replica создаётся только при connector-level toggle и annotation на конкретном Deployment.
- Несколько Spot-нод относятся к одной парной on-demand группе. Граница такой пары — node group внутри одного Kubernetes-кластера. Одну Kubernetes-ноду нельзя разделить между несколькими кластерами.
-
Тёплый резерв оплачивается как обычная VM. Уменьшение
fallback_min_sizeдо 0 экономит больше, но возвращает холодный старт и не позволяет включить warm replica. - Warm replica участвует в Service. Она сохраняет pod-labels исходного Deployment, жёстко закреплена за on-demand группой и синхронизируется с последующими rollout исходного workload.
Почему «+1 on-demand нода» не равна полному резерву
Monitor при длительном отсутствии всех Ready Spot-нод пытается один раз увеличить размер парной on-demand группы на заданный шаг (по умолчанию на одну ноду сверх тёплого минимума). Если одновременно потеряно несколько Spot-нод, ёмкости может не хватить по CPU, памяти, зоне или ограничениям scheduler. Monitor также не обещает автоматическое возвращение на Spot после восстановления.
Анализ кандидатов
Для каждой on-demand node group платформа проверяет все workloads на нодах:
Критерии spot-safe workload
- Только Deployments — StatefulSet и другие контроллеры блокируют рекомендацию; DaemonSet-поды при проверке игнорируются.
- replicas > 1 — Deployment должен иметь избыточность. Это фильтр кандидатов, а не доказательство, что реплики фактически размещены на разных нодах или зонах.
-
Не system namespaces —
kube-system,kube-public,kube-node-leaseисключены. -
Нет local PV —
hostPathи local Persistent Volumes привязаны к конкретной ноде, данные будут потеряны.
Если все проверяемые workloads в node group spot-safe — группа получает рекомендацию. Если хотя бы один workload unsafe — вся группа пропускается. Непосредственно перед cloud mutation live-preflight повторяет проверку и останавливает устаревшую или опасную рекомендацию.
Unsafe Reasons
Каждая причина, по которой workload не подходит для spot, отображается в UI:
| Причина | Описание |
|---|---|
StatefulSet |
Workload с состоянием, привязан к конкретным PV |
Single replica |
Только 1 реплика, нет избыточности |
Local PV |
Использует hostPath или local Persistent Volume |
System namespace |
Workload в kube-system или другом системном namespace |
Что происходит после «Включить Spot»
Шаг 1 — Fetch
Получение текущей конфигурации on-demand node group из Yandex MKS API.
Шаг 2 — Live-preflight
Повторная проверка фактических source nodes, stateless workloads и ограничений warm fallback. На этом шаге облачные ресурсы ещё не меняются.
Шаг 3 — Тёплый резерв до миграции
Для opt-in workload Oopps сначала создаёт отдельную on-demand replica и ждёт,
пока она станет Ready и ready endpoint в EndpointSlice
каждого подходящего Service. Реплика входит в тот же PDB selector, поэтому
Deployment с двумя replicas и minAvailable: 2 получает безопасный
budget для одной миграции. Без этого подтверждения drain не начинается.
Шаг 4 — Create
Создание preemptible клона node group с schedulingPolicy.preemptible = true.
Имя новой группы: {old_name}-spot. Все остальные параметры
(тип VM, scalePolicy, labels, taints) копируются из оригинала. Поэтому
autoscaling продолжает работать по тем же правилам, но на spot-нодах.
Шаг 5 — Wait
Ожидание статуса RUNNING у preemptible группы.
Таймаут — 600 секунд.
Шаг 6 — Drain
Ноды on-demand группы обрабатываются строго по одной, а внутри ноды Pod
выселяются по одной: cordon, evict через Kubernetes Eviction API с уважением
PodDisruptionBudget, ожидание ухода старой Pod и готовности replacement.
Тёплая replica исключается из drain. Только затем платформа переходит к следующей Pod.
Шаг 7 — Сохранение резерва
Исходная on-demand node group не удаляется: её минимальный (или fixed) размер
по умолчанию устанавливается в 1. Oopps ждёт физического схождения до одной
Ready ноды, подтверждает её соответствие переносимому
nodeSelector workload и делает её schedulable. Затем Oopps повторно
проверяет, что уже созданная тёплая replica всё ещё Ready и присутствует
в EndpointSlice каждого подходящего Service; одного статуса
Available недостаточно.
Что сейчас делает Health Monitor
Backend периодически проверяет состояние preemptible-групп. Это наблюдение и реактивная попытка восстановления, а не механизм высокой доступности.
Проверка каждые 60 секунд
Для каждой preemptible node group платформа подсчитывает количество нод
в статусе NotReady.
Нормальный preemption
Если часть нод NotReady, платформа логирует состояние и ждёт.
Fallback рассматривается только когда ожидаемая ёмкость Spot больше нуля,
а Ready-нод в группе нет вообще.
Реализованная попытка fallback
Если ожидаемая preemptible-ёмкость больше нуля и Ready-нод нет (все
NotReady или ноды отсутствуют) непрерывно более 5 минут,
monitor пытается увеличить парную on-demand группу на один настроенный шаг.
Пятиминутный интервал отсчитывается по сохранённому состоянию мониторинга, поэтому
перезапуск сервиса не сбрасывает защиту. Для одной непрерывной аварии fallback
выполняется не более одного раза и записывается как
action_kind = spot_fallback. Наличие записи говорит о выполненной
операции scale up, но не подтверждает, что все поды уже Ready.
Ready-нода и Available replica подтверждают резерв ёмкости, но не ноль ошибок конкретного Service: на результат влияют endpoint propagation, readiness, connection draining и клиентские таймауты. Для критичной нагрузки выполните fault-test с непрерывными probes без retry и зафиксируйте собственный SLO.
Как подготовить workloads
- Оставьте системные компоненты, stateful-сервисы и критичные одиночные экземпляры на on-demand нодах. Автоматическая конвертация группы не создаёт такой смешанный baseline за вас.
-
Для stateless Deployment задайте минимум две реплики и проверьте
topologySpreadConstraintsили pod anti-affinity. Одногоreplicas: 2недостаточно, если обе реплики попали на одну ноду. - Настройте корректные CPU/memory requests, readiness probe и время старта. Иначе scheduler не сможет предсказуемо разместить поды после прерывания.
-
Добавьте
PodDisruptionBudget, но помните: PDB помогает при добровольном drain и не может запретить облаку внезапно остановить VM. -
Проведите собственный fault-test: остановите тестовую Spot-ёмкость, измерьте
ошибки и время восстановления, проверьте
Pending-поды и события. Не переносите критичную нагрузку только по отметке «Можно включить».
Экономика и выбор режима
| Схема | Стоимость | Что ожидать |
|---|---|---|
| Spot + warm capacity (по умолчанию) | Одна обычная VM оплачивается постоянно | Первая on-demand ёмкость уже Ready; opt-in replica может сразу участвовать в Service |
Spot + cold reserve (fallback_min_size: 0) |
Максимальная экономия в штатном режиме | Холодное восстановление; warm replica недоступна |
| Только on-demand | Выше стоимость | Подходит, если прерывания и холодный старт неприемлемы |
Включите spot_management.warm_fallback.enabled в настройках и
добавьте исходному Deployment annotation
oopps.ai/spot-warm-fallback: enabled. Нужны минимум две replicas,
matching PDB и отсутствие PVC/hostPath, init containers, host namespaces,
жёсткого nodeName, affinity или привязки к конкретной node group.
Переносимый nodeSelector допускается, только если ему соответствует
retained on-demand capacity. Нарушение блокирует apply до изменений.
В isolated YC live-E2E на stateless Deployment с PDB minAvailable: 2
Oopps выполнил product-controlled Spot-конверсию: сохранил тёплую on-demand
replica в Service, перенёс primary Pod'ы и оставил ready on-demand capacity.
В marker-bounded SLO-окне external probe дал 220/220, internal Service-DNS —
145/145; retries, ошибки и таймауты отсутствовали. Это evidence для данной
миграции, а не публичная гарантия на полную физическую потерю Spot capacity или
произвольную конфигурацию workload.
Что дальше?
- Instance Type — подбор оптимального типа VM для node groups.
- Hibernation — расписание сна для dev/staging кластеров.
- Proactive Autoscaling — двухслойный автоскейлер на p95.
- Bin-Packing — консолидация нагрузки и освобождение нод.