Interruptible‑инстансы: чекпоинты и устойчивость
Задача страницы. Практический гид, как безопасно и эффективно работать на interruptible‑инстансах (прерываемых узлах): чекпоинты и автосейвы, идемпотентные пайплайны, «чанкование» задач, lease/heartbeat‑механики, быстрый рестарт с NVMe‑кэша и расчёт экономики. Подходит для офлайн‑батчей (ETL/RAPIDS, рендер, ASR‑обработка, обучение/пересчёт моделей, симуляции), а также для «полуонлайна» с допусками по SLA.
TL;DR
- Разделяйте пулы: On‑Demand (жёсткое SLA) vs Interruptible (массовые батчи/офлайн). См. https://cloudcompute.ru/solutions/throughput-vs-latency/
- Чанк ≤ 120 с + идемпотентность: каждый чанк перерабатывается без побочных эффектов; финализация — атомарная.
- Два уровня чекпоинтов: быстрый локальный (NVMe) каждые N секунд/шагов, и «надёжный» — в объектное хранилище (реже).
- Lease + heartbeat: «кто держит чанк» с TTL; по таймауту работа автоматически переотдаётся другому воркеру.
- WAL/манифест: журнал прогресса и manifest.json с версионированием моделей/данных/порогов.
- Грациозное завершение: ловим сигнал остановки, за ≤ 5–30 с пишем быстрый чекпоинт и отдаём lease.
- Экономика: выгодно, если Discount_interruptible > Overhead(checkpoint + потери); расчёт — ниже.
- Наблюдаемость: checkpoint_age, resume_count, lost_work_sec, chunk_fail_rate, commit_lag, HBM/NVMe.
Сценарии (когда это нужно)
- ETL/ML‑подготовка: RAPIDS/Spark RAPIDS, CTAS/партиции, агрегации и джойны. См. https://cloudcompute.ru/solutions/rapids/, https://cloudcompute.ru/solutions/spark-rapids/
- Обработка медиа: видео/аудио/рендер — сегменты 30–120 с, конвертация/детекция/аннотации.
- Обучение/пересчёт моделей: LoRA/QLoRA/классические ML, периодические эмбеддинги/индексы. См. https://cloudcompute.ru/solutions/recsys/
- Симуляции/датасеты: Isaac Sim headless рендер/аннотации по шартам. См. https://cloudcompute.ru/solutions/isaac-sim/
- Граф/эмбеддинги: офлайн‑инференс узлов/индексы ANN. См. https://cloudcompute.ru/solutions/graph-ml/
Архитектуры/пайплайны (паттерны) A) Map‑Reduce с идемпотентной финализацией
Jobs manifest (chunks.json)
└─> Scheduler (lease TTL, backoff)
└─> Worker:
├─ 1) Pull chunk → local NVMe
├─ 2) Compute (≤120 s)
├─ 3) Write WAL + local checkpoint
├─ 4) Commit atomically → object storage
└─ 5) Ack lease → done
↳ On failure: lease timeout → reassign
Ключи: атомарная финализация через «временный путь → rename/commit‑маркер», read‑after‑write валидация, повторный запуск безопасен.
B) Потоковая обработка с быстрым автосейвом
Stream → Micro-batch (5–30 s) → GPU stage → WAL + partial state (NVMe) → Flush/commit (перекат окна)
Ключи: фиксированные «водяные знаки» времени, запрет «вечных» трансакций, компенсационные записи для отмены.
C) Обучение/инференс (elastic)
Data shards → Trainer/Inferencer (elastic world size)
├─ Periodic checkpoints:
│ - fast: weights/optimizer to NVMe (часто)
│ - durable: to object store (реже)
└─ Resume:
- manifest.yaml (step/seed/versions)
- deterministic dataloader (reseed)
См. распределёнку: https://cloudcompute.ru/solutions/multi-gpu/
Профили и ориентиры (инженерные)
Оценка устойчивости и параметров чекпоинтинга по профилям GPU. Цифры — ориентиры для «средних» задач (обработка батчей/рендер/инференс), NVMe‑кэш, U≈0.7.
| **Профиль** | **Память** | **Реком‑интервал fast‑чекпоинта** | **Типичный объём fast‑ckpt** | **Параллельных чанков/GPU\*** |
| 24 ГБ (Compact) | 24 ГБ | 30–60 с | 50–300 МБ | 2–4 |
| 48 ГБ (Balanced) | 48 ГБ | 30–90 с | 100–600 МБ | 4–8 |
| 80 ГБ (HQ) | 80 ГБ | 60–120 с | 200–1200 МБ | 8–16 |
* «Параллельных чанков» — микробатчей/потоков в пределах VRAM/I/O. Тюнинг — https://cloudcompute.ru/solutions/performance-tuning/
Конфиги и скелеты кода
Docker Compose: воркер с autosave/lease/WAL
version: "3.9"
x-env: &env
CHUNK_MAX_SEC: "120"
CHECKPOINT_LOCAL: "/nvme/ckpt"
CHECKPOINT_REMOTE: "/obj/ckpt"
WAL_DIR: "/nvme/wal"
LEASE_DIR: "/obj/leases"
JOBS_MANIFEST: "/obj/jobs/chunks.json"
AUTOSAVE_SEC: "45"
GRACEFUL_TIMEOUT_SEC: "20"
PRECISION: "fp16" # bf16|fp16|fp32
services:
intr-worker:
image: cloudcompute/interruptible-worker:latest
environment: *env
deploy:
resources:
reservations:
devices: [{capabilities: ["gpu"]}]
volumes:
- /nvme/cache:/nvme
- /mnt/object:/obj
command: ["python","worker.py","--manifest","/obj/jobs/chunks.json"]
Kubernetes: preStop + увеличение grace‑тайма
apiVersion: apps/v1
kind: Deployment
metadata: { name: intr-worker }
spec:
replicas: 3
selector: { matchLabels: { app: intr-worker } }
template:
metadata: { labels: { app: intr-worker } }
spec:
terminationGracePeriodSeconds: 30
containers:
- name: worker
image: cloudcompute/interruptible-worker:latest
resources: { limits: { nvidia.com/gpu: 1, cpu: "4", memory: "24Gi" } }
lifecycle:
preStop:
exec: { command: ["/bin/sh","-c","kill -TERM 1; sleep 5"] }
env:
- { name: AUTOSAVE_SEC, value: "45" }
- { name: CHUNK_MAX_SEC, value: "120" }
YAML: манифест джобы и записей WAL
job:
id: "job_2025_08_29T12_00Z"
dataset: "s3://bucket/ds/v1/" # или /obj/...
chunks:
- { id: "000001", from_s: 0, to_s: 120, status: "todo" }
- { id: "000002", from_s: 120, to_s: 240, status: "todo" }
policy:
retry_max: 3
backoff_sec: [10, 30, 60]
lease_ttl_sec: 180
atomic_commit: true
wal_record:
job: "job_2025_08_29T12_00Z"
chunk: "000001"
step: "compute"
ts: "2025-08-29T12:03:00Z"
meta: { gpu_mem_peak_mb: 16384, out_bytes: 104857600 }
Python: воркер с lease/heartbeat и атомарным commit
import os, json, time, signal, uuid, shutil, tempfile, pathlib
LEASE_TTL = 180
GRACE = int(os.getenv("GRACEFUL_TIMEOUT_SEC", "20"))
_should_exit = False
def _graceful(*_): # ловим SIGTERM/SIGINT
global _should_exit; _should_exit = True
for s in (signal.SIGTERM, signal.SIGINT): signal.signal(s, _graceful)
def lease_take(lease_dir, chunk_id, holder):
p = pathlib.Path(lease_dir, f"{chunk_id}.json")
now = time.time()
try:
if p.exists():
lease = json.loads(p.read_text())
if now - lease["ts"] < LEASE_TTL: return False
p.write_text(json.dumps({"holder": holder, "ts": now}))
return True
except Exception:
return False
def lease_heartbeat(lease_dir, chunk_id, holder):
p = pathlib.Path(lease_dir, f"{chunk_id}.json")
p.write_text(json.dumps({"holder": holder, "ts": time.time()}))
def atomic_commit(tmp_dir, final_dir):
os.makedirs(final_dir, exist_ok=True)
staging = tempfile.mkdtemp(prefix="commit_", dir=final_dir)
for name in os.listdir(tmp_dir):
shutil.move(os.path.join(tmp_dir, name), staging)
os.replace(staging, os.path.join(final_dir, pathlib.Path(staging).name))
pathlib.Path(final_dir, "DONE").touch()
def process_chunk(chunk, ckpt_dir) -> bool:
# 1) compute → периодический autosave в ckpt_dir
# 2) собрать результаты в tmp_out, затем atomic_commit
return True
def main(manifest, lease_dir, ckpt_local, out_remote):
holder = f"{uuid.uuid4()}"
chunks = json.loads(open(manifest).read())["chunks"]
for ch in chunks:
if not lease_take(lease_dir, ch["id"], holder): continue
start = time.time()
last_hb = start
while not _should_exit:
ok = process_chunk(ch, ckpt_local)
if not ok: break
if time.time() - last_hb > 10:
lease_heartbeat(lease_dir, ch["id"], holder); last_hb = time.time()
break
if _should_exit:
# быстрый чекпоинт перед выходом
pathlib.Path(ckpt_local, "quick.ckpt").touch()
break
# COMMIT
atomic_commit(tmp_dir=os.path.join(ckpt_local, "out", ch["id"]),
final_dir=os.path.join(out_remote, ch["id"]))
``` ## **Наблюдаемость/метрики/алерты**
**Perf/Latency:**
- chunk\_time\_seconds (p50/p95), lost\_work\_seconds (оценка потерянной работы при прерывании).
- resume\_time\_seconds, checkpoint\_write\_seconds, checkpoint\_size\_bytes.
- queue\_wait\_seconds, throughput\_chunks\_per\_min, gpu\_utilization, gpu\_mem\_peak\_bytes, nvme\_{read,write}\_mb\_s.
**Reliability:**
- lease\_expired\_total, resume\_count\_total, retry\_total{reason=oom|preempt|io|data}, chunk\_fail\_rate.
- checkpoint\_age\_seconds, manifest\_drift (версионные несовпадения моделей/данных).
**Data/Commit:**
- commit\_lag\_seconds, exactly\_once\_conflicts\_total, wal\_gap\_total.
**Алерты (примеры):**
- chunk\_time\_p95 > SLO — уменьшить размер чанка/батча, оптимизировать I/O, распараллелить.
- lost\_work\_seconds\_per\_hour > budget — чаще fast‑чекпоинт, T\_opt↓ (см. Экономика), увеличить lease\_ttl.
- resume\_count ↑ — частые прерывания/нестабильность; проверить кворум узлов, перенести часть джоб в On‑Demand.
- exactly\_once\_conflicts\_total > 0 — усилить атомарность коммита, уникальные ключи, дедуп‑правила.
Детали мониторинга:
[ https://cloudcompute.ru/solutions/monitoring-logging/](https://cloudcompute.ru/solutions/monitoring-logging/) • <https://cloudcompute.ru/solutions/llm-inference/observability/>
## **Экономика и формулы**
Обозначения: c\_gpu — цена/час, U — целевая загрузка; λ — интенсивность прерываний (1/час); C — время записи fast‑чекпоинта; T — интервал между fast‑чекпоинтами; t\_base — чистое время вычислений.
- **Потери из‑за прерываний (ожид.):
При равномерном распределении моментов прерываний **средняя потеря работы на один обрыв ≈ T/2**.
Тогда Overhead\_interrupt ≈ λ × (T/2).
- **Оверхед на чекпоинты:** Overhead\_ckpt ≈ C / T (доля времени).
- **Оптимальный интервал fast‑чекпоинта (оценка):
Минимизируя Overhead\_total = C/T + λT/2, получаем T\_opt ≈ sqrt(2C/λ).
- **Итоговое время:
T\_total ≈ t\_base × (1 + Overhead\_ckpt + Overhead\_interrupt).
- **Стоимость:
Cost\_total ≈ c\_gpu × T\_total / U.
- **Брейк‑ивен interruptible vs on‑demand:
Пусть Discount = (Cost\_on\_demand - Cost\_interruptible) / Cost\_on\_demand.
Использовать interruptible выгодно, если Discount > Overhead\_total.
Планирование бюджета: <https://cloudcompute.ru/solutions/cost-planner/> • компромиссы: <https://cloudcompute.ru/solutions/throughput-vs-latency/>
**Безопасность и политики**
- **PII/секреты:** только через секрет‑хранилища/переменные окружения; шифрование «в канале/на диске».
- **WAL/манифесты:** подписывать хэшем/версией; хранить в объектном хранилище с версионированием.
- **Idempotency by design:** финальные объекты — **неперезаписываемые**; только «append + commit‑маркер».
- **Разделение доступов:** разные бакеты/префиксы на «сырьё», промежуточные и финальные артефакты.
Подробнее: <https://cloudcompute.ru/solutions/security/> • <https://cloudcompute.ru/solutions/storage-data/>
**Траблшутинг**
<table><tbody><tr><td>**Симптом**
</td><td>**Возможная причина**
</td><td>**Решение**
</td></tr><tr><td>Повторная обработка одних и тех же чанков
</td><td>Нет атомарной финализации/уникальных ключей
</td><td>«tmp → rename + DONE», дедуп по job\_id+chunk\_id+hash
</td></tr><tr><td>Потеря прогресса при остановке
</td><td>Нет быстрого чекпоинта/ловли сигналов
</td><td>Обработчики SIGTERM/SIGINT, fast‑ckpt ≤ 5–20 с, WAL на NVMe
</td></tr><tr><td>OOM/разбухание VRAM
</td><td>Большой батч/слишком много параллелизма
</td><td>FP16/BF16, уменьшить батч/параллелизм, spill на NVMe
</td></tr><tr><td>«Залипшие» лизы
</td><td>Некорректный TTL/часовой дрейф
</td><td>heartbeat каждые 10–20 с, TTL с запасом, синхронизация времени
</td></tr><tr><td>Конфликт версий артефактов
</td><td>Миграция модели/схемы без манифеста
</td><td>Версионирование (model\_v, schema\_v), миграционные скрипты
</td></tr><tr><td>Commit медленный
</td><td>Много мелких файлов/узкое NVMe
</td><td>Паковать в шард ≥ 128 МБ, параллельный аплоад, уменьшить файловую фрагментацию
</td></tr><tr><td>Частые ретраи из‑за данных
</td><td>«Грязные» записи/сломанные партиции
</td><td>Предвалидация входа, quarantine‑папка, отчёты по браку
</td></tr><tr><td>Низкая утилизация GPU
</td><td>Узкое CPU/I/O, слабая конвейеризация
</td><td>Параллельная подготовка чанков, prefetch, закрепление потоков
</td></tr></tbody></table>
См. также: <https://cloudcompute.ru/solutions/performance-tuning/> • <https://cloudcompute.ru/solutions/monitoring-logging/>
**Как запустить в cloudcompute.ru**
1. Откройте **Шаблоны запусков**: <https://cloudcompute.ru/solutions/templates/> — выберите **Interruptible Worker** или профильный темплейт (RAPIDS/рендер/ASR/инференс).
2. Разделите нагрузки: **On‑Demand** (интерактив/стриминг) и **Interruptible** (батчи/обучение).
3. Смонтируйте диски: /nvme (кэш/ckpt/WAL) и /mnt/object (надёжные артефакты).
4. Задайте параметры: CHUNK\_MAX\_SEC, AUTOSAVE\_SEC, LEASE\_TTL, атомарный коммит, backoff‑политики.
5. В продакшне: дашборды (checkpoint/lease/resume/throughput/HBM/NVMe), алерты, канареечные обновления воркеров и схем, тесты на перезапуск.
**Чек‑лист перед продом**
- Чанки ≤ 120 с, идемпотентность, атомарная финализация.
- Быстрый локальный чекпоинт (NVMe) и периодический durable‑чекпоинт (объектное хранилище).
- Lease/heartbeat с TTL; корректный graceful‑шутдаун (SIGTERM→fast‑ckpt).
- WAL/manifest с версиями данных/моделей; детерминированные сиды.
- Дашборды/алерты: lost\_work, resume\_count, checkpoint\_age, chunk\_fail\_rate, HBM/NVMe.
- Рассчитан T\_opt ≈ sqrt(2C/λ) и бюджет оверхедов; подтверждён брейк‑ивен по стоимости.
- Пулы **On‑Demand/Interruptible** разведены; политика ретраев/кворум/автоскейл настроены.
- Нагрузочный прогон ≥ 30 мин с искусственными прерываниями.
**Навигация**
- Хаб «Решения»: <https://cloudcompute.ru/solutions/>
- Шаблоны запусков: <https://cloudcompute.ru/solutions/templates/>
- Планирование стоимости: <https://cloudcompute.ru/solutions/cost-planner/>
- Throughput vs Latency: <https://cloudcompute.ru/solutions/throughput-vs-latency/>
- Производительность и тюнинг: <https://cloudcompute.ru/solutions/performance-tuning/>
- Multi‑GPU/распределёнка: <https://cloudcompute.ru/solutions/multi-gpu/>
- Хранилища и данные: <https://cloudcompute.ru/solutions/storage-data/>
- Безопасность: <https://cloudcompute.ru/solutions/security/>
- Мониторинг и логи: <https://cloudcompute.ru/solutions/monitoring-logging/>
- Наблюдаемость инференса/ETL: <https://cloudcompute.ru/solutions/llm-inference/observability/>
- Triton Inference Server (сервинг): <https://cloudcompute.ru/solutions/triton-inference-server/>
- Gradio + FastAPI (эндпойнты/демо): <https://cloudcompute.ru/solutions/gradio-fastapi/>
- CI/CD контейнеров: <https://cloudcompute.ru/solutions/containers-ci-cd/>
Готовы запустить?
Запустить GPU-сервер