Надёжные вычисления на GPU для масштабных задач обучения и инференса
- Обучение одной модели может неделями идти на сотнях GPU; отказ одного участника способен замедлить или остановить всю задачу.
- Агент Kubernetes каждые 30 секунд проверяет GPU и сетевые адаптеры, включая ошибки памяти, соединения и температуру.
- При устойчивой неисправности Karpenter выводит машину из эксплуатации и заменяет её; критические меры принимаются за несколько минут.
Почему это важно: Надёжность влияет на доступность сервисов и помогает не тратить деньги на простаивающие GPU.
[не удалось получить полный текст]
Авторы: Nick Rutigliano, Argha C, Samartha Chandrashekar
Как мы выявляем неисправности GPU и сетевой инфраструктуры, автоматически восстанавливаем работоспособность машин и помогаем длительным задачам пережить потерю оборудования.
Откройте Netflix — и главная страница сформируется специально для вас: машинное обучение определяет, какие подборки появятся, в каком порядке, какие фильмы и сериалы войдут в них и какие изображения будут сопровождать каждый из них. Модели обучаются на данных о том, как смотрят контент сотни миллионов пользователей. Персонализация — одна из самых востребованных наших разработок, и пользователи ощущают её каждый раз, открывая приложение. Как и всё большая часть Netflix, она работает на GPU: на них обучаются модели, и всё чаще они же обслуживают запросы в реальном времени.
Чтобы всё это работало стабильно, GPU должны оставаться исправными, а поддерживать их в таком состоянии сложнее, чем кажется. Одна крупная задача обучения может выполняться неделями на сотнях GPU, распределённых по множеству машин. Она запускается как единая группа: все процессы синхронно выполняют шаги и обмениваются данными после каждого из них. Поэтому задача продвигается со скоростью самого медленного участника и полностью останавливается, если один из них выходит из строя. На протяжении всего запуска должны работать все GPU, высокоскоростные сетевые адаптеры и соединения между устройствами. При масштабах, характерных для современного машинного обучения, отдельные GPU и сетевые каналы могут отказать прямо во время выполнения задачи. Поэтому платформа должна быть готова к таким ситуациям и справляться с ними без участия человека.
В этой статье рассказывается, как мы поддерживаем исправность GPU-инфраструктуры для обучения и инференса. Мы объясним, как выявляем неисправное оборудование до запуска задач и во время их выполнения, автоматически восстанавливаем работоспособность проблемной машины и помогаем длительной задаче пережить потерю машины. Эти задачи обучения выполняются на нашей современной вычислительной платформе, построенной на Kubernetes.
Почему надёжность GPU так важна
Netflix стремится развлекать весь мир, и машинное обучение уже много лет помогает нам в этом: от рекомендаций, которые помогают пользователям найти, что посмотреть, до систем, участвующих в создании и продвижении контента. По мере развития генеративного ИИ мы внедряем его в обеих сферах бизнеса: для пользователей — чтобы улучшать рекомендации и создавать более качественные рекламные материалы, а для повествования — чтобы предоставлять творческие инструменты нашим партнёрам по производству контента.
Всё это работает на GPU — как обучение, так и инференс, —, а вычислительные мощности GPU относятся к самым быстрорастущим и дорогостоящим составляющим нашей инфраструктуры. Поэтому надёжность важна для бизнеса по двум причинам. — -
- Доступность: если во время обучения теряется машина или сервису инференса не удаётся получить исправные мощности, задерживается работа, от которой зависят пользователи и создатели контента.
- Экономическая эффективность: GPU, простаивающий из-за медленного соседа, или целая машина, выведенная из пула из-за неисправности одного устройства, — это оплаченные, но неиспользуемые ресурсы. Чем выше надёжность и утилизация, тем больше полезной работы выполняет каждый GPU, а значит, на ту же работу можно потратить меньше средств.
По мере того как всё больше функций ИИ становятся доступны пользователям, доля инференса в нагрузке на GPU растёт. Поэтому надёжность и эффективность, важные для многонедельного обучения, всё больше значат и для постоянно работающих сервисов.
Почему одних базовых проверок работоспособности недостаточно
Мы начинаем с базовых проверок, предоставляемых облачным провайдером. Провайдеры проверяют состояние виртуальных машин: системная проверка оценивает доступность питания, сети и оборудования хоста; проверка экземпляра подтверждает, что операционная система запущена и доступна; также проверяется подключённое хранилище. Когда провайдер планирует вывести хост из эксплуатации или перезагрузить его, он отправляет уведомление о плановом обслуживании, а экземпляр, перешедший в неисправное состояние, может быть восстановлен автоматически. Мы используем все эти механизмы, а уведомления о плановом обслуживании поступают в одну из наших собственных проверок. Они обнаруживают выключенную, недоступную или предназначенную для обслуживания машину и на этом уровне останавливаются. Они подтверждают, что машина запущена и доступна, но не проверяют, достаточно ли исправны каждый GPU, его встроенная память, соединения между устройствами и сетевая инфраструктура для распределённого обучения.
Следующий шаг, который обычно предпринимают команды, — проверка на уровне GPU: отвечает ли драйвер и видны ли GPU системе. Она выявляет вышедший из строя или отсутствующий GPU, но не обнаруживает случаи, поиск которых отнимает больше всего времени.
- GPU может отвечать на запрос о состоянии, но давать сбой, как только к нему обращается рабочая нагрузка. Неисправность встроенной памяти или микропрограммы, управляющей устройством, может оставаться незаметной, пока задача не выделит память или не начнёт продолжительные вычисления. После этого задача завершается с ошибкой или замедляется.
- Высокоскоростной сетевой адаптер может быть на месте, но показывать меньше активных каналов, чем ожидалось. Коллективные операции всё ещё выполняются, однако пропускная способность резко падает, и задача не аварийно завершается — она просто еле движется. Причину легко упустить.
- Проверка может завершиться с ошибкой и при этом создать впечатление, что неисправная машина работает нормально. Если зависает драйвер, может зависнуть и диагностическая утилита, а простая проверка, ожидающая её ответа, тоже зависнет. После обновления драйвера может пропасть необходимый проверке исполняемый файл, из-за чего проверка перестанет запускаться и ничего не сообщит. Проверка, которая может незаметно перестать работать, хуже, чем её отсутствие: она создаёт ложное чувство уверенности, пока машина продолжает использоваться.
Поскольку задача выполняется как единая группа, любая из этих проблем на любой машине может замедлить или остановить всю задачу. При этом ни одна из них не обнаруживается проверкой состояния экземпляра или проверкой ответа драйвера. Система мониторинга должна исходить из того, что оборудование может сообщать неверные данные, а инструменты для его проверки могут выходить из строя.
Машина может пройти базовую проверку и всё равно быть неисправной. Многоуровневые проверки, тайм-ауты, резервный сценарий и проверки, завершающиеся ошибкой при отсутствии зависимости, закрывают эти пробелы.
Наш подход
Мы рассматриваем надёжность как систему из четырёх уровней, каждый из которых обнаруживает то, что не может обнаружить предыдущий:
1/ Состояние узлов и оборудования: выявлять неисправные GPU, сетевые адаптеры и соединения между устройствами, ошибки памяти и проблемы с температурой, а затем помечать машину как проблемную или выводить её из эксплуатации.
2/ Планировщик и платформа: повторять задачи и задания, проверять машину до того, как она начнёт выполнять работу, а также безопасно выводить машины из эксплуатации и заменять их.
3/ Цикл обучения: сохранять контрольные точки и использовать эластичные рабочие процессы, чтобы задача могла пережить потерю машины.
4/ Наблюдаемость: собирать метрики, отслеживать целевые показатели уровня сервиса и определять причины сбоев на всех трёх предыдущих уровнях.
Первые два уровня не дают аппаратным проблемам повлиять на выполнение задач. Третий исходит из того, что некоторые потери неизбежны, и позволяет свести их последствия к минимуму. Четвёртый показывает, работают ли первые три.
Четыре уровня: каждый обнаруживает то, чего не может обнаружить предыдущий.
Как работают проверки состояния
Проверки выполняются на двух уровнях. Проверки устройств отслеживают состояние каждого GPU. Агент уровня узла, построенный на основе открытого проекта Kubernetes node-problem-detector, запускается на каждой машине с GPU каждые 30 секунд и преобразует результаты проверок в условия состояния узла Kubernetes. Это тот же механизм, который Kubernetes использует, чтобы пометить узел как NotReady. Контроллеры отслеживают эти условия и решают, можно ли поручать машине работу.
Проверки GPU опираются на DCGM от NVIDIA: они отслеживают коды ошибок, сообщаемые драйвером, неисправимые ошибки памяти, целостность соединений между устройствами и температуру. Проверки сетевых адаптеров анализируют состояние хоста, проверяя количество и состояние соединений интерфейсов Elastic Fabric Adapter, по которым передаётся трафик между машинами. Некоторые из этих проверок заслуживают отдельного описания.
Двухуровневая проверка состояния. Результаты проверок устройств и узлов преобразуются в условия состояния узлов Kubernetes, на основе которых контроллеры помечают узлы, выводят их из эксплуатации, завершают работу и заменяют их.
- При сбое — отказ: если драйвер или диагностическая утилита не отвечает, проверка завершается по тайм-ауту и помечает машину как неисправную. Если проверка не может получить доступ к оборудованию, она сообщает о проблеме, а не о том, что всё в порядке. Поэтому зависший драйвер выводит машину из пула, вместо того чтобы позволить ей скрыть проблему за зависшей утилитой.
- Резервный сценарий: коды ошибок драйвера — самый надёжный признак неисправности GPU, однако инструмент для их считывания сам может выйти из строя при некоторых сбоях! В таких случаях мы напрямую читаем эти коды из журнала ядра dmesg, чтобы не пропустить ошибки Xid или SXid.
- Собственная карта критических ошибок: каталоги ошибок драйверов производителей отстают от новых поколений GPU и не всегда соответствуют тому, что мы наблюдаем в рабочей среде. Поэтому мы ведём собственную таблицу кодов, считающихся критическими для каждого семейства GPU. Код, который в одном поколении означает, что GPU нужно перезапустить, в другом может быть безвредным. Кроме того, при каждой успешной загрузке GPU одного поколения выдаёт серию кодов, обычно считающихся критическими. Мы выяснили, что их следует считать шумом, а не поводом выводить новые машины из эксплуатации.
- Никаких незаметных сбоев: если отсутствует необходимая зависимость, проверка завершается с ошибкой. Эта мера появилась после обновления, в ходе которого пропал исполняемый файл, необходимый для проверки сетевых адаптеров. Проверка незаметно перестала работать, и несколько дней в эксплуатацию поступали машины с неправильным количеством адаптеров, пока не выяснилось, что именно они замедляли выполнение задач. Теперь при отсутствии зависимости проверка завершается с ошибкой.
Автоматическое восстановление работоспособности проблемной машины
Когда проверка завершается с ошибкой, меняется условие состояния узла машины, и управление переходит к контроллерам. Мы различаем два уровня проблем. Состояние «проблема» блокирует запуск новых задач на машине, но не прерывает уже выполняющиеся, поскольку снятие задачи с машины нарушает её работу, а многие проблемы устраняются сами. Если состояние сохраняется дольше допустимого периода, оно повышается до состояния «сбой»: машина освобождается от задач, её задания завершаются (при необходимости с отменой обычных ограничений на перебои, чтобы действовать быстрее), машина выводится из эксплуатации и заменяется.
В нашем вычислительном стеке используется автоматическое восстановление узлов Karpenter. Мы настроили его с помощью политики и контроллера, которые задают для каждого условия состояния узла время ожидания до принятия мер. Поэтому при критической ошибке меры принимаются в течение нескольких минут. В то же время для нестабильного сигнала задаётся более длительный период ожидания, а ограничение не позволяет одновременно вывести из эксплуатации слишком большую часть кластера. Благодаря этому ошибка в самих проверках не может привести к каскадному выводу из эксплуатации всего кластера. Мы планируем предложить эту функцию всем пользователям Karpenter.
Поэтапное устранение проблем. Состояние «проблема» допускает ожидание и может пройти само; сохраняющийся сбой приводит к освобождению машины от задач и её выводу из эксплуатации, а затем — к быстрому исправлению на месте или выводу из эксплуатации при подтверждённой неисправности оборудования.
Не каждый сбой требует полной замены машины, а замена — самый медленный способ восстановить вычислительные мощности. Мы подбираем действие в зависимости от проблемы. Завершение проблемной задачи устраняет программный зависший процесс. Перезапуск одного GPU устраняет многие временные сбои устройства. Перезагрузка машины сбрасывает состояние микропрограммы, которое могло измениться за длительное время работы. Вывод из эксплуатации и замена предназначены только для оборудования, не прошедшего проверенный нами тест. В последнем случае мы записываем неизменяемый аппаратный идентификатор GPU, который остаётся привязанным к физическому устройству после перезагрузки и повторного запуска, даже если меняется экземпляр, сообщаем об этом облачному провайдеру и ведём собственный список. Так устройство, неоднократно дававшее сбой, не сможет незаметно вернуться в пул под новым идентификатором экземпляра.
Между обнаружением проблемы и планированием задач есть один тонкий момент. Ранее машину, только что прошедшую проверку, передавали планировщику, пока результаты проверок ещё не стабилизировались. Поэтому задача могла попасть на неё за мгновение до того, как медленная проверка выявляла проблему. Теперь мы удерживаем вычислительные мощности, пока проверки не завершатся и их результаты не стабилизируются, — это устранило такое окно.
Проверка машины до начала работы
Недавно запущенной машине не доверяют, пока она не докажет свою исправность. Прежде чем задействовать её вычислительные мощности, она проходит системные проверки, подтверждает наличие GPU и их топологию, а также выполняет серию диагностических тестов GPU. Затем мы ждём, пока проверки состояния выполнятся дважды, поскольку для некоторых длительных проверок — например, подсчёта сетевых адаптеров — требуется больше одного цикла. Если допустить машину к работе после единственной проверки, задача может запуститься до того, как будут получены результаты остальных проверок. Только после этого мы снимаем начальную метку, запрещающую назначать работу. При выводе машины из эксплуатации мы выполняем более глубокую диагностику и записываем идентификаторы GPU, добавляя их в тот же список неисправного оборудования. Это не позволяет незаметно вернуть в работу оборудование, которое однажды уже дало сбой.
Как помочь длительным задачам пережить потерю машины
Даже при всех перечисленных мерах некоторые машины теряются во время выполнения задачи, поэтому мы стараемся сделать последствия этого как можно менее серьёзными. Задачи сохраняют своё состояние в контрольных точках и при потере машины возобновляются с последней исправной контрольной точки, а не запускаются заново после нескольких недель работы. Рабочие процессы эластичны и допускают повторные попытки, а планировщик автоматически повторяет задачи, завершившиеся из-за проблем платформы.
Без определения причины сбоя автоматические повторы были бы небезопасны. Если повторять каждую неудачную задачу, мы могли бы скрыть настоящие ошибки в самой задаче и расходовать время GPU на повторение сбоя, который платформа не способна устранить. Поэтому перед повторным запуском мы определяем, вызвана ли ошибка платформой или приложением. Вот пример, показывающий, почему это важно: некоторые тайм-ауты в NCCL — библиотеке коллективных коммуникаций, которую GPU используют для обмена данными, — поначалу казались признаком сетевого сбоя, но в итоге были связаны с замедлением загрузки данных при большом количестве рабочих процессов. С GPU и сетью всё было в порядке — исправлять нужно было конвейер обработки данных. Если бы эти тайм-ауты принимали за аппаратные сбои, исправные машины выводились бы из эксплуатации, а причина оставалась бы неустранённой.
Как мы оцениваем эффективность
Мы отслеживаем надёжность GPU по трём целевым показателям уровня сервиса. Каждый разбит по причинам сбоев, чтобы по отклонению можно было определить ответственный уровень:
- Доступность: объём вычислительных мощностей GPU, доступных для планирования в каждый конкретный момент.
- Успешный запуск задачи: запускается ли задача своевременно после того, как необходимые ей вычислительные мощности становятся доступны.
- Успешное выполнение задачи: завершается ли задача без сбоев, вызванных платформой.
Для каждого показателя отдельно учитываются причины, связанные с оборудованием, драйвером, платформой и пользователем. Поэтому снижение доступности из-за неисправного оборудования отличается от снижения, вызванного изменением планировщика или неверной настройкой пользователя. Мы оптимизируем полезную производительность — объём полезной работы, выполненной всем парком машин, — потому что задачу, задержанную одним медленным GPU, можно принять за исправную по графику времени безотказной работы, хотя она почти не продвигается. Конкретные целевые значения для каждого показателя приведены здесь в иллюстративных целях.
Замкнутый цикл устранения проблем
Все эти компоненты образуют единый цикл. При выделении ресурсов, до запуска задачи, оборудование проверяется с помощью короткого запуска коллективных коммуникаций и попарных сетевых тестов. Поэтому любая машина, которая даст сбой в первую минуту, будет выявлена до допуска задачи к выполнению. Во время работы задачи мы пассивно отслеживаем ошибки драйвера и памяти, перегрев и другие проблемы. Мы рассматриваем каждую задачу как распределённую систему, что позволяет обнаружить отдельный медленный процесс, тормозящий остальные, а не только сбои целых машин. При обнаружении проблемы её устранение происходит автоматически и поэтапно. Этот цикл одинаков для задач, выполняемых через наш пакетный планировщик, Kueue или Ray.
Цикл обеспечения надёжности: проверка, мониторинг, активная диагностика, анализ задачи как системы, устранение проблем — и повторение цикла.
Безопасное совместное использование GPU
Не каждой рабочей нагрузке нужен целый GPU, а выделение отдельных GPU для небольших задач инференса ведёт к потере вычислительных мощностей. Мы разделяем GPU с помощью MIG, обеспечивая изоляцию арендаторов на уровне памяти. Благодаря этому сбой или чрезмерное потребление памяти в одном разделе не влияет на остальные. Мы также перенастраиваем разделы без перезапуска машины, чтобы пул мог по мере изменения спроса переключаться между множеством небольших и несколькими крупными разделами. Сегодня выделение GPU и сетевых адаптеров осуществляется через плагины устройств. Когда Karpenter получит поддержку Kubernetes Dynamic Resource Allocation, мы перейдём на этот механизм. Это важно, потому что при подключении GPU к сетевому адаптеру или CPU через неподходящий корневой комплекс PCIe пропускная способность снижается.
На машине с несколькими GPU мы изолируем один неисправный GPU, оставляя остальные в работе, а исправные GPU распределяем между несколькими арендаторами с изоляцией на уровне памяти.
Выделение ресурсов на уровне устройств меняет и подход к работе с единичными неисправными GPU. При поддержке DRA вместо потери всей машины мы могли бы изолировать неисправное устройство, оставив остальные в работе. Это особенно важно для инференса, когда множество небольших задач используют одну машину. Кроме того, благодаря гибкости выделения ресурсов задача может запросить необходимые ей характеристики, например объём памяти или поддержку тензорных ядер, а платформа подберёт подходящий тип GPU. Пользователям не придётся заранее привязываться к конкретной модели.
Результаты
Переход от базовых проверок и ручного вывода машин из эксплуатации к многоуровневым проверкам и автоматическому устранению проблем изменил два аспекта, заметных пользователям. В любой момент для планирования доступна большая доля парка GPU: неисправные машины быстро обнаруживаются и заменяются, а не остаются в пуле или ждут, пока инженер дежурной смены примет меры. Кроме того, из-за проблем платформы завершается с ошибкой меньше задач: большинство неисправностей выявляется до запуска задачи, а остальные устраняются автоматически, после чего задача запускается повторно. Поэтапное устранение проблем также позволяет за несколько минут восстановить вычислительные мощности, если достаточно завершить задачу, перезапустить GPU или перезагрузить машину, вместо того чтобы долго ждать её замены.
После внедрения описанных выше улучшений мы достигли следующих результатов:
- Доступность узлов GPU стабильно превышает 99,95%
- Доля успешно выполненных задач выросла примерно с 98% до более чем 99,5%
Что дальше
Мы расширяем активные проверки, чтобы охватить дополнительные типы сбоев под нагрузкой, увеличиваем объём тестирования на раннем этапе, чтобы устранять больше видов неисправностей до того, как с ними столкнутся пользователи, и делаем восстановление более точечным. Это позволит устранять больше типов ошибок на месте, а не запускать полный цикл замены узла при некоторых ошибках Xid. Кроме того, мы хотим чаще проводить диагностику между задачами.
Мы также работаем над тем, чтобы при размещении задач учитывалась топология оборудования. Для этого мы планируем использовать Kubernetes Dynamic Resource Allocation (DRA), когда эта функция появится в Karpenter. Например, можно будет учитывать Compute/IMEX Domains, корневые комплексы PCIe и узлы NUMA.
Что касается повышения производительности обучения на нескольких GPU, мы планируем запускать собственный планировщик Linux на базе sched_ext. Во-первых, мы сможем задавать приоритет процессам во время коллективной синхронизации — например, когда GPU обмениваются результатами перед следующим этапом вычислений. Так мы сократим общее время простоя GPU, задав этим задачам высокий приоритет на уровне всего парка машин. На одной машине эффект невелик, но на сотнях машин даже несколько процентов пропускной способности дают значительный прирост вычислительных мощностей. Во-вторых, мы сможем повысить приоритет процессов загрузки данных и устранить узкое место, которое иногда возникает, когда данные поступают на GPU слишком медленно для стабильного продвижения задачи.
Мы уже отслеживаем события троттлинга, однако GPU необязательно должен сообщать о троттлинге, чтобы тот повлиял на задачу. Если один GPU работает на 10–15 градусов горячее остальных, даже оставаясь в допустимых пределах, он задаёт темп всей группе. Поэтому значительную разницу температур можно считать неисправностью: остановить задачу и перенести её на более холодные GPU.
Кроме того, мы планируем расширить мониторинг NCCL RAS (надёжность, доступность и обслуживаемость), чтобы раньше выявлять проблемы NCCL — например, несовпадение коллективных операций или зависания — и быстрее возобновлять работу.
Заключение
Оборудование выходит из строя, и в масштабах нашей инфраструктуры это происходит достаточно часто, чтобы платформа считала сбои нормальным явлением. Выявление проблем до запуска задач и во время их выполнения, автоматическое восстановление машин и возможность возобновлять длительные задачи после потери машины позволяют командам выполнять масштабное обучение и инференс без постоянного контроля за оборудованием. Если вам интересны такие задачи, мы нанимаем сотрудников.
Надёжные вычисления на GPU для крупных задач обучения и инференса — статья, впервые опубликованная в Netflix TechBlog на Medium, где читатели продолжают обсуждение, отмечая и комментируя эту публикацию.
Netflix Tech Blog прочитано 417 раз
