Когда обучение прерывается: сначала сохраняем факты
Потеря SSH-соединения, ошибка CUDA или падение скорости не всегда означают неисправный GPU. Причина может быть в приложении, драйвере, сети, диске или жизненном цикле арендуемого инстанса. Задача диагностики — отделить эти варианты и сохранить данные до перезапуска.
Разберём проверки при старте GPU-хоста, небольшой тест PyTorch и план восстановления. Это не рейтинг надёжности провайдеров и не обещание найти любую неисправность за пять минут.
Типичные симптомы и возможные причины
- SSH недоступен: проверьте состояние инстанса, сетевые правила, маршрут и консоль провайдера. Статус Running сам по себе не подтверждает работоспособность SSH.
- CUDA out of memory: сопоставьте ошибку с размером модели, batch size, контекстом и другими процессами. Свободная память после сбоя не доказывает, что её хватало в момент выделения.
- Driver version is insufficient: проверьте совместимость драйвера и CUDA-библиотек окружения.
- Illegal memory access: сохраните воспроизводимый пример и версию библиотек; ошибка не доказывает аппаратный дефект.
- Процесс D: ожидает непрерываемую операцию, часто I/O. Сигнал может исполниться только после завершения ожидания. Процесс Z уже завершён и ждёт, пока родитель получит его статус; убивать сам zombie бессмысленно.
- Падение скорости или I/O ошибки: сравните частоты, температуру, загрузку, свободное место и логи с исправным запуском.
Не начинайте с принудительного reboot. Сначала определите, можно ли безопасно сохранить чекпоинт, какие тома переживут перезапуск и какие процессы работают на узле.
Как интерпретировать аппаратные показатели
Повышение температуры, ошибки ECC и Xid-сообщения требуют внимания, но конкретное действие зависит от модели GPU, типа ошибки и документации. Не всякий ненулевой накопительный счётчик означает новый отказ. Сравнивайте время события, динамику и влияние на задачу; передавайте эти данные поддержке. Значение N/A может означать, что метрика не поддерживается в этой среде.
Чек-лист при старте GPU-инстанса
Сначала выполните read-only проверки. Нагрузочные тесты запускайте отдельно от полезной задачи, только на выделенных вам ресурсах и с учётом правил провайдера.
1 Быстрая проверка здоровья GPU
# Список устройств и доступная информация
nvidia-smi -L
nvidia-smi
nvidia-smi -q
# Поддерживаемые поля можно проверить через --help-query-gpu
nvidia-smi --query-gpu=index,name,uuid,driver_version,memory.total,memory.used,temperature.gpu --format=csv
# Если CUDA Toolkit установлен: версия компилятора, а не драйвера
command -v nvcc >/dev/null && nvcc --version
nvidia-smi показывает поддерживаемую драйвером версию CUDA, а не наличие соответствующего Toolkit. Углублённые тесты памяти выполняются отдельными средствами, например NVIDIA DCGM на поддерживаемом оборудовании; сначала согласуйте их с оператором и остановите конкурирующую нагрузку. Не сбрасывайте ECC-счётчики и GPU до сбора диагностики.2 Проверка системных ресурсов и диска
free -h
df -h
df -i
top -b -n 1
# Необязательная запись 64 МиБ: выполните в тестовом каталоге
# с достаточным свободным местом, не на рабочем наборе данных.
(
set -eu
f=$(mktemp ./gpu-io-test.XXXXXX)
trap 'rm -f -- "$f"' EXIT
dd if=/dev/zero of="$f" bs=1M count=64 conv=fdatasync
)
3 Создание эталонного теста производительности
Запусти маленький, но показательный скрипт. Он должен:
- Загрузить небольшой тензор в память GPU.
- Выполнить матричное умножение (операция, сильно нагружающая вычислительные блоки).
- Измерить время выполнения.
- Сохранить результат. При следующем запуске на другом инстансе (или после падения) ты сможешь сравнить производительность.
import argparse
import json
import statistics
import time
import torch
parser = argparse.ArgumentParser()
parser.add_argument("--device", type=int, default=0)
parser.add_argument("--size", type=int, default=2048)
args = parser.parse_args()
if not torch.cuda.is_available():
raise SystemExit("CUDA недоступна в данном окружении")
if not 0 <= args.device < torch.cuda.device_count():
raise SystemExit("Неверный индекс GPU")
if not 128 <= args.size <= 4096:
raise SystemExit("Для этого smoke-test размер должен быть от 128 до 4096")
device = torch.device(f"cuda:{args.device}")
torch.cuda.set_device(device)
torch.set_float32_matmul_precision("highest")
props = torch.cuda.get_device_properties(device)
size, iterations = args.size, 20
with torch.inference_mode():
x = torch.randn(size, size, device=device, dtype=torch.float32)
y = torch.randn(size, size, device=device, dtype=torch.float32)
out = torch.empty_like(x)
for _ in range(5):
torch.mm(x, y, out=out)
torch.cuda.synchronize(device)
samples = []
for _ in range(5):
start = time.perf_counter()
for _ in range(iterations):
torch.mm(x, y, out=out)
torch.cuda.synchronize(device)
samples.append(time.perf_counter() - start)
seconds = statistics.median(samples)
record = {
"time_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"gpu_index": args.device, "gpu_name": props.name,
"torch": str(torch.__version__), "torch_cuda": torch.version.cuda,
"dtype": "float32", "matmul_precision": "highest",
"size": size, "iterations": iterations,
"median_seconds": seconds,
"estimated_tflops": 2 * size ** 3 * iterations / seconds / 1e12,
}
print(json.dumps(record, ensure_ascii=False))
with open("gpu_benchmark.jsonl", "a", encoding="utf-8") as f:
f.write(json.dumps(record, ensure_ascii=False) + "\n")
Запуск: python benchmark_gpu.py --device 0 --size 2048. Это короткий тест матричного умножения, а не проверка всей памяти и не прогноз скорости LLM. Он не гарантирует отсутствие OOM. Сравнивайте одинаковые GPU или MIG-профили, размер, precision, версии и условия нагрузки. Значение ниже прежнего — повод исследовать причину, но не доказательство неисправности. Для каждого GPU запускайте отдельный тест и сохраняйте его UUID из nvidia-smi рядом с результатом.
RunPod, Vast.ai и другие провайдеры: что сравнивать
Сравнивайте конкретное предложение, регион и режим аренды. Общая оценка бренда не заменяет технические условия выбранного узла и проверку своей нагрузки.
| Критерий | Что проверить до запуска |
|---|---|
| GPU и окружение | Модель, VRAM, MIG/разделение ресурсов, драйвер, допустимые образы и привилегии. |
| Жизненный цикл | Возможность прерывания, правила остановки и удаления инстанса, доступность замены. |
| Хранение | Какие тома эфемерные, какие переживают stop/delete, где находятся резервные копии и как их восстановить. |
| Сеть и данные | Доступные механизмы изоляции, открытые порты, регион, договорные условия обработки данных. TLS не скрывает открытые данные от процесса, который их обрабатывает. |
| Поддержка и SLA | Официальные каналы, обязательства по ответу и порядок компенсации. Срок или возврат не гарантируются опытом другого клиента. |
| Бюджет | Вычисления, простой, диски, трафик, копии, сопровождение и возможный повтор задачи. |
Например, RunPod и Vast.ai отдельно описывают виды хранения. Перед остановкой прочитайте правила именно своего типа тома: постоянный том не равен независимой резервной копии.
Для дальнейшего сравнения можно посмотреть материал о специализированных провайдерах и условиях аренды GPU. Актуальную стоимость и наличие проверяйте у самого провайдера.
Когда проблема случилась: план сохранения и восстановления
- Сохраните факты: время UTC, ID узла, версии, код ошибки и доступные логи. Удалите токены, персональные данные и содержимое промптов перед передачей поддержке.
- Проверьте сохранность: завершите запись чекпоинта, если приложение ещё отвечает. Снапшот активного диска может быть только crash-consistent; он не гарантирует целостный чекпоинт или сохранение RAM.
- Подготовьте копию: завершённые чекпоинты отправляйте в отдельное хранилище. Используйте отдельные версии/имена, проверяйте размер и контрольную сумму; не заменяйте последнюю рабочую копию незавершённой загрузкой.
- Перезапускайте по плану: сначала проверьте, какие данные переживут stop/delete. Восстановите проверенный образ и последний целый чекпоинт на доступном ресурсе.
- Обратитесь в поддержку: опишите симптомы и приложите очищенную диагностику. Компенсация возможна только в рамках применимых условий, а причина сбоя требует проверки.
Для внешних чекпоинтов можно сравнить S3-совместимое хранилище Timeweb Cloud (партнёрская ссылка). Проверьте регион, стоимость хранения и передачи, права доступа и совместимость клиента. Настройте закрытый бакет и отдельный доступ с минимальными правами; сам выбор S3 ещё не создаёт политику резервирования.
Для возобновления обучения обычно нужны не только веса, но и состояние оптимизатора, шаг/эпоха и другие состояния, от которых зависит конкретный pipeline. Периодически проверяйте восстановление на отдельном тесте. Интервал сохранения выбирайте по допустимой потере работы и затратам на запись.
Метрики и очищенные логи стоит хранить вне сбойного узла. Контролируйте доставку: если агент мониторинга перестал отправлять данные, отсутствие ошибок в панели не означает исправность.
Воспроизводимое окружение для восстановления
Создай себе "золотой образ" – Docker-контейнер или системный снапшот с идеально настроенным окружением для твоих задач. В нём должны быть:
- Нужная версия Python и всех пакетов (зафиксированная в requirements.txt).
- Проверенные версии CUDA, cuDNN, PyTorch/TensorFlow.
- Предустановленные утилиты диагностики (htop, nvtop, iotop).
- Настроенный крон для периодического запуска health-чекеров.
Сохраните проверенный образ в доступном вам registry, зафиксируйте digest и документируйте запуск. Не встраивайте секреты, данные клиентов и приватные ключи в слои образа. Образ приложения не содержит драйвер хоста и не заменяет совместимость CUDA; проверку нового узла всё равно нужно выполнить.
План восстановления считается полезным после проверки: новый узел запускается, данные доступны, чекпоинт читается и работа продолжается с ожидаемого шага. Наличие свободного GPU на замену заранее не гарантировано, поэтому учитывайте это в допустимом времени простоя.
Начните с тестового чекпоинта и одного восстановления. Это даст больше уверенности, чем неподтверждённый рейтинг надёжности провайдеров.