В 2024 году была опубликована интересная статья, в которой описано, как за 54 дня обучения Llama 3 405B кластер Meta из 16 384 карт H100 пережил 419 внезапных прерываний, примерно по одному каждые три часа. 58,7% из них пришлись на GPU и их память. Процессоры за то же время отказали дважды. Свежее по такому масштабу никто ничего не публиковал, но тренд на Kubernetes с тех пор только усилился: по опросу CNCF за 2025 год, 82% пользователей контейнеров гоняют кубер в проде и 66% компаний с genAI-моделями держат на нем инференс.

Сам Kubernetes до сих пор как будто бы уверен, что отказы лечатся рестартами. Для stateless-сервиса это правда, а вот для пода с GPU нет: если контейнер упадет, то kubelet поднимет его заново на том же мертвом устройстве, и все пойдет по кругу.

Я Стас Погоржельский, технологический евангелист VK Cloud. Про то, как раздавать GPU через DRA, на Хабре уже писали, про отказоустойчивость кластеров в целом тоже. Эта статья про то, о чем обе умалчивают: что происходит после того, как выданное устройство умерло. Мы посмотрим на это наглядно: соберем отказный стенд, отберем у пода GPU и посмотрим глазами kubectl, кто и когда об этом узнает. А потом разберем механизмы 1.36, с которыми из этой ситуации впервые можно выйти без вечного Pending и без убийства ноды целиком.

Чем устройство отличается от CPU

Процессор и память в кубере взаимозаменяемы: планировщику все равно, на каких именно ядрах крутится под, а сбойное ядро операционная система изолирует сама. А вот с устройством все не так: GPU выдается поду персонально, у него есть серийник, состояние, температура и своя прошивка. Отказ устройства не равен отказу ноды: нода жива, kubelet отвечает, остальные карты работают. И не равен отказу приложения: код не виноват, что у карты отвалилась память.

Официальный блог Kubernetes делит отказы вокруг подов с устройствами на четыре режима: сбой инфраструктуры самого кубера, отказ устройства, падение кода контейнера и деградация устройства, когда карта формально жива, но считает медленнее или теряет пропускную способность NVLink. На каждый режим нужна своя реакция, но по умолчанию кубер реагирует только одним способом — просто перезапускает контейнер.

Схема 1. Четыре режима отказа вокруг пода с устройством и реакция кубера
Схема 1. Четыре режима отказа вокруг пода с устройством и реакция кубера

В том же отчете про Llama 3 авторы прямо пишут: «a single GPU failure may require a restart of the entire job», то есть синхронная природа обучения делает большой кластер менее отказоустойчивым, чем куда более крупные CPU-парки. Обвязка тоже отказывает: HBM3-память дала 17,2% внезапных прерываний, NVLink входит в 30,1% GPU-отказов. И это считается не аварией, а нормой эксплуатации: команда NVIDIA GeForce NOW называла на KubeCon NA 2024 цифру в 19 запросов на remediation на 1000 нод в день.

Может показаться, что это все касается лишь гигантов с десятками тысяч карт, а на кластере с восемью GPU можно жить по-старому. Но на практике все наоборот: у гигантов есть свои прослойки автоматизации над кубером, а маленький кластер живет на голых примитивах, и одна умершая карта в нем заметнее, чем сотня карт у Meta.

Как кластер выдает устройства: device plugin против DRA

Устройство попадает в под двумя разными способами, и оба так или иначе ослепляют кубер, мешая увидеть отказ.

Классический device plugin работает как счетчик. Плагин вендора регистрирует в kubelet ресурс nvidia.com/gpu: 8, планировщик вычитает единицы при аллокации. Здоровье устройств плагин NVIDIA отслеживает через события NVML: поймал критичный Xid, пометил карту unhealthy и убрал ее из allocatable ноды. Xid — это код ошибки, который драйвер NVIDIA пишет в kernel log. Эти коды стабильны между версиями драйвера, но одна и та же ошибка может иметь несколько первопричин, от бага приложения до умершей PCIe-шины.

Если счетчик allocatable уменьшится с 8 до 7, то это будет все, что узнал кластер. Официальный блог Kubernetes пишет об этом честно: device plugin сообщает об отказе только изменением количества доступных устройств. Вопросов, какая именно карта умерла, какой под на ней сидит, надо ли его куда-то девать, старая модель даже не ставит.

DRA, динамическое выделение ресурсов, ставшее stable в 1.34, перекраивает модель. Устройство описывается объектами API как том в PVC:

  • DeviceClass задает тип;

  • ResourceClaim фиксирует заявку на устройство;

  • ResourceClaimTemplate создает заявки для каждого пода;

  • ResourceSlice публикует инвентарь устройств ноды с атрибутами, по которым можно фильтровать через CEL-выражения. 

ResourceClaim живет в том же namespace, что и под, и, если заявки нет, под не запланируется вовсе.

Минимальная связка выглядит так:

apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
  name: gpu-claim-template
  namespace: inference
spec:
  spec:
    devices:
      requests:
        - name: gpu
          exactly:
            deviceClassName: gpu.nvidia.com
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: llm-worker
  namespace: inference
spec:
  replicas: 1
  selector:
    matchLabels:
      app: llm-worker
  template:
    metadata:
      labels:
        app: llm-worker
    spec:
      containers:
        - name: worker
          image: registry.example.com/llm-worker:1.4
          resources:
            claims:
              - name: gpu
      resourceClaims:
        - name: gpu
          resourceClaimTemplateName: gpu-claim-template

Под здесь намеренно завернут в Deployment, а не задеплоен голым: у голого пода нет владельца, и после вытеснения его никто не пересоздаст. Это ружье выстрелит в третьем акте стенда.

Аллокация в нашем примере одноразовая. Планировщик подбирает устройство под клейм в момент планирования, кладет результат в статус клейма, и с этого момента связка «под, клейм, устройство» зафиксирована. Механизма для того, чтобы переаллоцировать клейм на другую карту, не трогая под, в кубере нет, и дальше мы увидим, во что это превратится при отказе.

Схема 2. Поток аллокации устройства через DRA и где терялся сигнал об отказе
Схема 2. Поток аллокации устройства через DRA и где терялся сигнал об отказе

Раздача GPU через DRA подробно разобрана в статье RUVDS, пересказывать ее не буду. Для нас важнее срез готовности: что из DRA-стека доступно в 1.36, вышедшем 22 апреля 2026 года.

Механизм

KEP

Статус в 1.36

Зачем 

при отказах

DRA, базовая модель

4381

stable (с 1.34)

объектная модель вместо счетчика

Prioritized list (firstAvailable)

4816

stable

запасное устройство вместо Pending

Device health в статусе пода

4680

beta

под узнает, что его карта умерла

Device taints and tolerations

5055

beta

увести нагрузку с больной карты

Partitionable devices (MIG)

4815

beta

делить карту на изолированные куски

Device binding conditions

5007

beta

не биндить под к неготовой карте

Статус устройства в ResourceClaim

4817

beta (с 1.33)

второй канал диагностики

Workload ResourceClaims (PodGroup)

5729

alpha

заявки на группу подов, gang-нагрузки

Таблица собрана по анонсам релиза и DRA-обзору команды 1.36. Заявленный приоритет апстрима на ближайшие релизы: миграция пользователей с device plugin на DRA.

Стенд: отбираем GPU у живого пода

Теория теорией, но в начале статьи я обещал показать отказ на практике. Стенд собран на Managed Kubernetes в VK Cloud: кластер 1.36, GPU-нода с двумя vGPU (вторая карта понадобится в третьем акте, когда под отправится на здоровое устройство), NVIDIA GPU Operator, поверх него DRA-драйвер. Бета‑механика работает на control plane, и одного фичегейта здесь недостаточно: для health нужен гейт ResourceHealthStatus, а для таинтов, помимо гейта DeviceTaintRules, придется включить еще и runtime‑config resource.k8s.io/v1beta2 на apiserver. Поэтому понадобится кластер, где такая настройка доступна. Полные манифесты и скрипты лежат в репозитории-харнесе, ссылка в конце статьи. Там же в README расписана полная конфигурация моего стенда.

Разворачиваем манифест выше и убеждаемся, что под получил устройство и выполняет инференс. Дальше устройство нужно сломать, но честный аппаратный отказ по расписанию не устроишь, поэтому имитируем его. Самый грубый способ сделать это на стенде — это горячее изъятие устройства с PCIe-шины.

# 01: находим адрес GPU на ноде
lspci -d 10de: | head -1
# 3b:00.0 3D controller: NVIDIA Corporation ...

# 02: смотрим, что драйвер писал про карту до отказа
dmesg | grep -i xid   # пусто: карта здорова

# 03: имитируем отказ: изымаем устройство с шины
echo 1 | sudo tee /sys/bus/pci/devices/0000:3b:00.0/remove

# 04: наблюдаем реакцию драйвера
dmesg | tail -20
# NVRM: Xid (PCI:0000:3b:00): 79, GPU has fallen off the bus.

Видим Xid 79, карта отвалилась от шины. 

У этого метода есть два нюанса:

  • Горячее изъятие имитирует режим «устройство исчезло», деградацию (четвертый режим из классификации выше) так не воспроизвести, для нее нужен инструментарий вроде DCGM. 

  • Конкретный Xid зависит от драйвера и окружения. Запись в sysfs remove вежливая, ядро сначала зовет remove-колбэк драйвера, поэтому возможен и тихий teardown без ошибки. Гораздо надежнее fallen off the bus воспроизводится гашением линка на вышестоящем PCIe-мосту через setpci, а в vGPU-госте часть Xid видна только на хосте. Для нашей задачи достаточно любого исхода, при котором устройство исчезло. 

Команды диагностики из траблшутинг-доки NVIDIA работают как заявлено: dmesg | grep -i Xid показывает событие, логи nvidia-device-plugin-daemonset фиксируют, что плагин пометил устройство unhealthy. 

Важно учесть, что под получил карту через DRA, но на стенде параллельно живет и классический device plugin, его счетчик nvidia.com/gpu удобен как независимый свидетель отказа. Смотрим на произошедшее глазами кластера:

kubectl get node gpu-node-1 -o jsonpath='{.status.allocatable.nvidia\.com/gpu}'
# 1    # было 2: отказавшая карта исчезла из счетчика

kubectl get pod -n inference -l app=llm-worker
# NAME                         READY   STATUS    RESTARTS      AGE
# llm-worker-6f7c9d5b8-x2m4v   0/1     Running   4 (38s ago)   22m

kubectl get events -n inference --field-selector involvedObject.name=llm-worker-6f7c9d5b8-x2m4v
# BackOff  Back-off restarting failed container worker in pod llm-worker-6f7c9d5b8-x2m4v

Вот и весь спектакль. Нода честно сообщила, что устройство пропало, а под этого не узнал: он падает, kubelet его рестартует, контейнер снова не находит карту, падает, и счетчик RESTARTS тикает. Обратите внимание: рестарт-петля не удаляет под, поэтому Deployment тоже молчит, пересоздавать ему нечего. Планировщик ни при чем: под уже привязан к ноде и к устройству, перепланировать его никто не собирается. Это ровно то поведение, которое описывает апстрим: кубер держит под на устройстве, даже если устройство официально признано нездоровым.

Тайминги стенда сведены в таблицу. Числа усредненные и иллюстративные, это порядок величины, не точный замер:

Событие

Время от отказа

Xid 79 в dmesg

секунды

Device plugin: карта unhealthy, allocatable уменьшился

10–30 секунд

Первый рестарт контейнера

~1 минута

Реакция планировщика (перепланирование)

не наступает

Возврат пода к работе

не наступает

Почему под не восстанавливается как обычный

Давайте разбираться, почему кубер вообще так себя ведет. Все дело в дизайне — сигналы об отказах устройств и жизненный цикл пода живут в разных вселенных. Официальный блог формулирует это так:

«Kubernetes не устанавливает корреляцию между сбоями устройств и сбоями контейнеров и не предлагает никаких мер по их устранению, кроме перезапуска контейнера при подключении к тому же устройству».
официальный блог Kubernetes, Navigating Failures in Pods With Devices, июль 2025

Кубер не связывает отказ устройства с падением контейнера и не предлагает ничего, кроме рестарта на том же устройстве. И дальше все только хуже: для пода с restartPolicy: Always не существует descheduling — механизма, чтобы снять под с ноды и отдать планировщику заново, в кубере нет. Нет и встроенного контроллера, который удалял бы поды, застрявшие в CrashLoopBackOff. Рестарт-петля на мертвой карте будет крутиться, пока ее не разорвет кто-то извне.

Экосистема, естественно, наросла костылями. Апстрим честно перечисляет три DIY-паттерна и пределы каждого.

Первый: контроллер здоровья ноды. Следит за allocatable, и, если емкость не восстановилась за таймаут, убивает ноду целиком. Работает, но грубо: root cause неизвестна, контроллер не знает, какие поды сидели на умершей карте, а какие на здоровых, и нода гибнет вместе со всеми. Отдельная прелесть в том, что отказавшая карта могла вообще никем не использоваться.

Второй: pod failure policy. Под завершается специальным кодом выхода при потере устройства, Job это ловит и сразу валит джобу целиком, не сжигая лимит ретраев на мертвой карте:

apiVersion: batch/v1
kind: Job
metadata:
  name: training-run
spec:
  backoffLimit: 3
  podFailurePolicy:
    rules:
      - action: FailJob
        onExitCodes:
          containerName: worker
          operator: In
          values: [42]   # код выхода при потере GPU, задает приложение
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: worker
          image: registry.example.com/train:2.1

Но механизм работает только для Job с restartPolicy: Never и не работает с инференс-сервисом с Always.

Третий: свой pod watcher. Контроллер слушает Pod Resources API kubelet, сопоставляет умершие устройства с подами и убивает их принудительно, чтобы контроллер выше по стеку пересоздал под уже на здоровой карте. Самый точный вариант, но и самый дорогой, ведь код надо написать, катить и поддерживать на каждом кластере.

Уничтожение пода руками тоже не панацея, а лишь обмен одной проблемы на другую. Когда мы удаляем застрявший под, Deployment создает новый, ResourceClaimTemplate штампует свежий клейм, и планировщик начинает искать под него устройство. Если умершая карта была единственной свободной, искать нечего, и под зависает в Pending. Формально это прогресс: Pending хотя бы виден в мониторинге и не сжигает рестарты. Но для нагрузки это тот же простой, только с другим статусом в kubectl. Отсюда и формулировка задачи этой статьи: нужен remediation без бесконечного Pending.

Все паттерны объединяет то, что реагировать нужно быстро и автоматически. Meta в исследовании надежности своих ML-кластеров — 11 месяцев наблюдений, 4 миллиона джобов и больше 150 миллионов GPU-часов A100 — проверяет здоровье нод каждые пять минут, а из их модели следует, что на гипотетических 100 000 GPU чекпойнт и рестарт должны укладываться примерно в две минуты, иначе эффективное время обучения падает ниже 0,9. То есть ручной разбор инцидента рассматривать вообще не стоит.

Что дает свежий кубер: health в статусе пода и таинты на устройства

Апстрим проблему признал и с 1.31 методично закрывает. К 1.36 из альф выросли два механизма, которые вместе превращают DIY-паттерны выше в штатные механизмы кластера.

Под узнаёт, что его карта умерла

KEP-4680 добавляет в статус пода поле allocatedResourcesStatus со здоровьем каждого выделенного устройства. Для device plugin фича появилась альфой в 1.31, для DRA в 1.34, в 1.36 доросла до беты, stable в плане на 1.37.

Под капотом DRA-драйвер стримит здоровье устройств в kubelet через gRPC-сервис DRAResourceHealth (API-группа dra-health/v1alpha1, серверный стрим NodeWatchResources), статусы Healthy, Unhealthy и Unknown оседают в кэше kubelet, и кэш переживает его рестарт. Повторяем отказ на стенде с включенным фичегейтом ResourceHealthStatus и смотрим:

kubectl get pod llm-worker-6f7c9d5b8-x2m4v -n inference -o yaml | grep -A7 allocatedResourcesStatus
#   allocatedResourcesStatus:
#     - name: claim:gpu
#       resources:
#         - resourceID: gpu.nvidia.com/gpu-node-1/gpu-3b00
#           health: Unhealthy

Разница со стендом из прошлой секции видна сразу. Там нам приходилось собирать проблему по крупицам: dmesg на ноде, логи плагина, счетчик ноды. Здесь причина написана прямо в статусе пода, и ее видит любой контроллер, дашборд или инженер с kubectl. Джон-Пол Сассин из Google, представлявший фичу в блоге кубера, формулирует смысл одной фразой: «Этот явный статус ясно показывает, что проблема связана с аппаратным обеспечением, а не с приложением». Дежурного в три часа ночи это избавляет от часа копания в логах — он сразу увидит, что проблема в железе.

В 1.36 бета умеет и в человекочитаемые сообщения вида «GPU temperature exceeds threshold» или «NVLink connection lost». Таймаут для статуса Unknown тоже уже настраивается, его задает драйвер отдельно для каждого устройства, по умолчанию 30 секунд, это один из критериев выхода фичи в бету. А вот сохранение health для уже завершившихся подов апстрим рассматривал и закрыл как известное ограничение, так что диагностика мертвого пода остается за логами. Отдельно не путайте этот канал со статусом устройства в самом ResourceClaim: KEP-4817 добавил драйверам возможность публиковать данные об аллоцированном устройстве в ResourceClaim.status.devices еще бетой в 1.33. Клейм помогает понять, что выделено и в каком оно состоянии, для алертинга нужен второй.

Диагноз в статусе решает половину задачи: теперь понятно, что случилось. Осталось понять, что делать.

Таинт на устройство вместо cordon на ноду

KEP‑5055 про device taints and tolerations переносит на устройства ту же модель, которую все привыкли видеть на нодах: те же таинты, те же tolerations, только примененные к отдельным девайсам. В Kubernetes она стартовала в альфа-версии 1.33, перешла в бету в 1.36, а stable запланирован на 1.37 согласно официальному плану развития KEP‑5055. 

Таинт на устройство ставит либо сам DRA-драйвер, поймавший деградацию, либо администратор, объектом DeviceTaintRule:

apiVersion: resource.k8s.io/v1beta2   # группа для 1.36: v1beta2; в других версиях сверьтесь с kubectl api-resources
kind: DeviceTaintRule
metadata:
  name: gpu-3b00-degraded
spec:
  deviceSelector:
    driver: gpu.nvidia.com
    pool: gpu-node-1
    device: gpu-3b00
  taint:
    key: device.example.com/health
    value: degraded
    effect: NoExecute

Напомню про включение: сама группа resource.k8s.io/v1beta2 и фичегейт DeviceTaintRules в 1.36 по умолчанию выключены. Без них kubectl apply выдаст «no matches for kind DeviceTaintRule», и это первые грабли нашего стенда.

Устройства выбираются по имени в формате <драйвер>/<пул>/<устройство> или CEL-выражением, так что одним правилом можно накрыть, например, все карты одной ревизии прошивки. Таинт работает в обе стороны. Новые поды на затейнченное устройство не планируются, а уже запущенные вытесняются автоматически: eviction-контроллер проставляет поду DeletionTimestamp, по аналогии с taint-eviction для нод, и дальше под пересоздает владелец, Deployment или Job, уже с новым клеймом на здоровой карте. Вот зачем стендовый под завернут в Deployment: у голого пода эта стадия закончилась бы на вытеснении.

От DIY-паттерна с убийством ноды это отличается гранулярностью. Из восьми карт ноды в обслуживание уходит одна, остальные семь продолжают работать. Cordon и drain для этого больше не нужны.

И симметричный механизм — toleration: нагрузка может явно объявить, что готова работать на деградировавшем устройстве. 

apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
  name: gpu-claim-tolerant
spec:
  spec:
    devices:
      requests:
        - name: gpu
          exactly:
            deviceClassName: gpu.nvidia.com
            tolerations:
              - key: device.example.com/health
                operator: Exists
                effect: NoExecute

Сценарий из KEP такой: дать пользователям решить, продолжать ли работу в деградированном режиме, пока устройство нездорово, или предпочесть перепланирование. Для батч-обучения с чекпойнтами каждые десять минут вытеснение почти бесплатно, а вот для инференса с прогретым KV-кэшем на десятки гигабайт бывает дешевле дотерпеть на медленной карте до конца сессии, чем греть кэш заново.

Повторяем стенд в третий раз: отказ, health Unhealthy в статусе пода, DeviceTaintRule (в проде его поставит контроллер по алерту), вытеснение, и Deployment пересоздает под уже с новым клеймом на второй карте. Полный лог прогона пишет скрипт наблюдения харнеса в watch.log, здесь только сводка таймингов. Числа, как и раньше, иллюстративные, свои снимете харнесом:

Событие

Время от отказа

health: Unhealthy в статусе пода

10–30 секунд

Таинт на устройство (контроллером по алерту)

~1 минута

Вытеснение пода, DeletionTimestamp

1–2 минуты

Новый под Running на второй карте

2–4 минуты (на стенде вторая vGPU свободна; в проде зависит от емкости)

Последняя строка целиком опирается на наличие свободной емкости, и это подводит к финальному вопросу: что происходит, если свободного устройства нет? 

Правила remediation без вечного Pending

Вытеснить под с мертвой карты — это еще полдела. Если в кластере нет свободного устройства, под повиснет в Pending, и для SLA это может быть хуже деградировавшей карты. Так что давайте сведем правила, которые вырисовываются из механики выше, в чек-лист.

Мониторить health в статусе пода, а не только ноды. Поле allocatedResourcesStatus появилось именно для машинной обработки: алерт на health != Healthy у критичных подов ловит отказ за секунды, без парсинга dmesg. Канал в ResourceClaim держите как вторую линию для диагностики драйвера.

Таинт на устройство вместо drain ноды. Правило гранулярности: выводите из строя ровно то, что сломалось. DeviceTaintRule с CEL-селектором заодно закрывает сценарий планового обслуживания, когда надо мягко освободить карты под замену прошивки.

Toleration — это осознанное решение владельца нагрузки. Не навешивайте его по умолчанию на все клеймы, иначе получите инференс, который неделями тихо работает на деградировавших устройствах. Это должен быть явный выбор на уровне каждой конкретной workload: батч‑задачам — вытеснение, долгоживущим сессиям — готовность терпеть деградацию. 

Запасное устройство выбирается через prioritized list. Механизм firstAvailable в ResourceClaim, который со стабильным статусом появился в 1.36, позволяет задать фолбэк: сначала просим A100, а если их нет, соглашаемся на L40S. В результате под получает карту классом ниже вместо того, чтобы застревать в Pending (документация по DRA). Для gang‑нагрузок, где группа подов бессмысленна по частям, имеет смысл смотреть на Workload ResourceClaims (альфа в 1.36): они позволяют подавать заявку на всю PodGroup целиком и заодно снимают ограничение в 256 подов на один клейм (там же в документации по DRA)

Дедлайн на перепланирование лучше задавать явно. Если за N минут под так и не нашел устройство, дальше уже нужна эскалация — например, автоскейлер поднимает новую GPU-ноду или нагрузка переезжает в другую зону. Штатного таймера для этого в Kubernetes нет, но алерт на возраст Pending-пода с клеймом собирается без особой экзотики. Сам N стоит выбирать не для красоты, а от экономики нагрузки: для инференса с SLA это обычно минуты, а для ночного батча допустим и час. Верхнюю границу тут неплохо задает практика больших кластеров: в работе Meta бюджет на восстановление при таких масштабах измеряется минутами, и с ростом кластера этот бюджет только сокращается (Meta, arXiv).

У этой схемы, впрочем, есть и честные ограничения: в Kubernetes 1.36 часть нужных механизмов все еще не добралась до апстрима. В официальном разборе device failures среди открытых пунктов прямо перечислены health устройств в самом ResourceSlice, интеграция отказов устройств в pod failure policy, node-local retry без полного цикла перепланирования и descheduling для подов с restartPolicy: Always (Kubernetes blog). Поэтому рестарт-петлю на живой ноде с уже мертвой картой по-прежнему обычно разрывает только вытеснение — либо инициированное отдельно, либо через taints.

Отдельная история — облачные сценарии с vGPU. Один физический GPU там режется на несколько виртуальных устройств, поэтому отказ железа может одновременно задеть несколько подов, в том числе у разных тенантов; надежной публичной статистики по blast radius таких сбоев найти не получается, так что здесь лучше не притворяться точными цифрами. Практический вывод при этом довольно прямой: health-статусы виртуальных устройств, сидящих на одном физическом GPU, стоит считать коррелированными, а значит, remediation-контроллеру полезно уметь выставлять taint сразу группе таких устройств, а не по одному. Для этого как раз подходит CEL-селектор в DeviceTaintRule.

Что дальше

Отказ GPU перестал быть редкой экзотикой: на 16 384 картах у Meta он случается примерно раз в три часа, и по мере того как инференс переезжает из экспериментов в прод, та же статистика, только в меньшем масштабе, приходит и в обычные кластеры. Kubernetes отвечает на это сменой модели: устройство из безликой единицы счетчика превращается в объект с именем, атрибутами, статусом здоровья и taint. То, что раньше приходилось собирать вручную, — pod watcher, контроллеры здоровья, скрипты drain — к 1.36 стало частью API, а к 1.37 должно окончательно закрепиться в stable.

Практический вывод довольно простой. Если у вас есть поды с GPU и restartPolicy: Always, то сценарий с рестарт‑петлей на мертвой карте у вас уже существует — просто, возможно, еще ни разу не выстрелил. Проверить это на своем кластере можно за полчаса: харнес с манифестами и скриптами стенда лежит в репозитории. А в Managed Kubernetes в VK Cloud уже доступны кластеры 1.36 с vGPU, так что стенд из статьи можно поднять там практически без изменений.

За 54 дня обучения Llama 3 инженеры Meta вмешивались в инциденты вручную всего три раза. Все остальное закрывала автоматика. Теперь примитивы для такой автоматики есть прямо в Kubernetes, и вопрос уже не в том, случатся ли у вас отказы GPU. Вопрос в том, кто будет их обрабатывать: kubelet, гоняя поды по кругу, или контроллер, работающий по заранее описанным правилам.

Комментарии (0)