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.

Сценарии (когда это нужно)

Архитектуры/пайплайны (паттерны) 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 &gt; SLO — уменьшить размер чанка/батча, оптимизировать I/O, распараллелить.
- lost\_work\_seconds\_per\_hour &gt; budget — чаще fast‑чекпоинт, T\_opt↓ (см. Экономика), увеличить lease\_ttl.
- resume\_count ↑ — частые прерывания/нестабильность; проверить кворум узлов, перенести часть джоб в On‑Demand.
- exactly\_once\_conflicts\_total &gt; 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 &gt; 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-сервер