Производственная практика OpenAI с почти 10 000 узлов в кластере Kubernetes

облачный носитель

OpenAI увеличил кластеры Kubernetes до 7500 узлов, предоставив масштабируемую инфраструктуру для крупномасштабных моделей нейронных сетей, таких как GPT-3, CLIP и DALL·E, а также для небольших экспериментальных исследований. Масштабирование одного кластера Kubernetes до такого масштаба происходит редко, и были внесены некоторые необходимые улучшения, но преимущество в том, что единая инфраструктура позволяет нашей исследовательской группе по машинному обучению быстро масштабироваться без изменения кода, чтобы сократить время эксперимента, ускорить ход исследований и разработок.

Со времени нашей последней статьи о масштабировании до 2500 узлов мы постоянно масштабировали нашу инфраструктуру, чтобы удовлетворить потребности наших исследователей, и по ходу дела узнали много других вещей. В этом посте обобщается соответствующий опыт, чтобы другие члены сообщества Kubernetes могли извлечь из него пользу, а затем описываются проблемы, которые необходимо решить.

1. Рабочая нагрузка

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

Большие задания машинного обучения могут получать доступ к нескольким узлам и ко всем аппаратным ресурсам на каждом узле и, таким образом, выполняться наиболее эффективно. Позволяет графическому процессору использовать NVLink для перекрестной связи или графическому процессору использовать GPUDirect для связи с сетевой картой. Таким образом, для многих наших рабочих нагрузок один модуль занимает весь узел, поэтому планирование не требует вытеснения ресурсов NUMA, ЦП или PCIE. Текущий кластер имеет полную двунаправленную полосу пропускания, поэтому нет необходимости учитывать топологию сети. Таким образом, нагрузка на планировщика относительно невелика.

Поскольку новая задача может содержать сотни требований к планированию подов, у kube-scheduler возникают сбои.

Самая большая работа — запустить MPI (параллельные вычисления), все модули в работе работают в одном коммуникаторе MPI. Смерть любого модуля приведет к приостановке и перезапуску всего задания. Задание периодически выполняет резервное копирование соответствующей информации (т. е. контрольных точек) и восстанавливает данные из самой последней резервной копии при перезапуске.

Мы не полностью полагаемся на Kubernetes для балансировки нагрузки. У нас очень мало трафика на уровне 7, так как нет необходимости в A/B-тестировании, сине-зеленых обновлениях, канареечных релизах и т. д. Поды напрямую взаимодействуют с MPI других подов через SSH (эта часть кажется немного сомнительной), а неservice endpoint. Обнаружение службы относительно ограничено, потому что мы выполняем поиск только один раз, когда задание запускается (когда модуль просто участвует в MPI).

Большинство заданий взаимодействуют с хранилищем типа BLOB-объектов, обычно передавая некоторые сегменты набора данных непосредственно в BLOB-объект или кэшируя его на локальный диск. Мы также используем некоторые PersistentVolumes, но хранилище BLOB-объектов более масштабируемо и не требует операций монтирования и размонтирования.

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


2. Сеть

По мере увеличения количества узлов и модулей в кластере мы обнаруживаем, чтоFlannelСложность удовлетворения спроса. Переключитесь на сетевую технологию узла узла для IP-конфигурации Azure VMSSes и связанных подключаемых модулей CNI. Это позволяет нам получить пропускную способность сети на уровне хоста в Pod.

Еще одна причина, по которой мы перешли на IP-адресацию на основе псевдонимов, заключается в том, что в наших крупнейших кластерах одновременно может использоваться около 200 000 IP-адресов. При тестировании сетей pod на основе маршрутов мы обнаружили значительное ограничение на количество маршрутов.

Модернизация SDN или Routing Engine — это проблема, но она упрощает настройку нашей сети. VPN или туннели могут быть добавлены без каких-либо дополнительных адаптеров. В то же время нам не нужно беспокоиться о фрагментации пакетов, поскольку некоторые участки сети имеют меньший MTU. Сетевая политика и мониторинг трафика просты, нет никакой двусмысленности в отношении происхождения и назначения пакетов.

Мы используем iptables на хосте для отслеживания использования сетевых ресурсов для каждого пространства имен и модуля. Это позволяет исследователям визуализировать использование сети. Поскольку многие из наших экспериментов имеют уникальные модели внешней и внутренней связи, они полезны для изучения возможных узких мест.

Правила iptables mangle можно использовать для пометки любого пакета, соответствующего определенным условиям. Ниже приведены правила, которые мы используем для определения того, является ли трафик внутренним или внешним. Правила FORWARD охватывают трафик от подов, а также ВХОДНОЙ и ВЫХОДНОЙ трафик от хостов:

iptables -t mangle -A INPUT ! -s 10.0.0.0/8 -m comment --comment "iptables-exporter openai traffic=internet-in"
iptables -t mangle -A FORWARD ! -s 10.0.0.0/8 -m comment --comment "iptables-exporter openai traffic=internet-in"
iptables -t mangle -A OUTPUT ! -d 10.0.0.0/8 -m comment --comment "iptables-exporter openai traffic=internet-out"
iptables -t mangle -A FORWARD ! -d 10.0.0.0/8 -m comment --comment "iptables-exporter openai traffic=internet-out"

После отметки iptables запустит счетчики для отслеживания байтов и пакетов, соответствующих этому правилу.

% iptables -t mangle -L -v
Chain FORWARD (policy ACCEPT 50M packets, 334G bytes)
 pkts bytes target     prot opt in     out     source               destination
....
1253K  555M            all  --  any    any     anywhere            !10.0.0.0/8           /* iptables-exporter openai traffic=internet-out */
1161K 7937M            all  --  any    any    !10.0.0.0/8           anywhere             /* iptables-exporter openai traffic=internet-in */

Мы используем Prometheus на основеiptables-exporterа затем подключите его к нашей системе мониторинга.

Особым аспектом нашей сетевой модели является то, что мы предоставляем исследователям диапазоны CIDR узлов, контейнеров и сервисных сетей. У нас есть модель радиальной сети, и мы используем собственные узлы и диапазоны CIDR для маршрутизации этого трафика. Исследователи подключаются к узловому узлу, откуда они могут получить доступ к любому отдельному кластеру. Но сами кластеры не могут общаться друг с другом. Это гарантирует, что кластеры изолированы друг от друга и что нет межкластерных зависимостей, нарушающих изоляцию.

мы используем хост NATпреобразовать сервисную сеть CIDR для обработки трафика из-за пределов кластера. Эта настройка дает нашим исследователям большую гибкость в выборе экспериментов и конфигурации сети.


3. API-сервер

API-сервер Kubernetes и кластеры etcd являются критически важными компонентами для работоспособности кластера, поэтому мы уделяем особое внимание нагрузке на эти системы. Мы используем Grafana, предоставленную проектом kube-prometheus, вместе с другими внутренними инструментальными панелями. Мы обнаружили, что оповещения HTTP (такие как 429, 5xx и т. д.) для сервера API по-прежнему очень эффективны.

В то время как большинство людей запускают сервер API внутри кластера k8s, мы предпочитаем запускать его вне кластера. Службы etcd и API Server работают на своих собственных выделенных узлах. В нашем самом большом кластере работает 5 серверов API и 5 узлов etcd, чтобы распределить нагрузку и свести к минимуму последствия в случае сбоя одного из них. У нас не было проблем с etcd с тех пор, как мы написали события Kubernetes для других кластеров etcd в нашем последнем посте. Сервер API не имеет состояния, и его обычно легко запускать в самовосстанавливающейся группе экземпляров или масштабируемом наборе. Мы не пытались настроить какие-либо автоматические функции, такие как самовосстановление кластеров etcd.

Сервер API потребляет значительный объем памяти и увеличивается линейно с количеством узлов в кластере. Для кластера с 7500 узлами мы наблюдали до 70 ГБ, используемого на сервер API.

Еще одна большая нагрузка на сервер API — это возможность НАБЛЮДЕНИЯ в API, напримерkubeletи node-exporter. Это НАБЛЮДЕНИЕ запускается, когда узлы добавляются или удаляются из кластера. а так как обычно сам каждый узел проходит черезkube-proxyмониторkubeletСервисы (переводчик: можно оптимизировать с помощью локального LB и назначить фиксированное количество Мастеров), поэтому пропускная способность, необходимая для этих ответов, является квадратичной величиной узла, иногда даже достигая 1 ГБ/с и более. Функция EndpointSlices в Kubernetes 1.17 обеспечивает огромную оптимизацию, которая снижает эту нагрузку в 1000 раз.

Как правило, мы уделяем пристальное внимание любым запросам API-сервера, масштаб которых зависит от размера кластера. Мы стараемся избегать взаимодействия DaemonSet с сервером API. Внедрение промежуточной службы кэширования (такой как агент кластера Datadog) кажется лучшей практикой, позволяющей избежать узких мест в масштабах всего кластера, когда есть реальная необходимость изменить компоненты мониторинга всех узлов.

По мере роста количества кластеров наши операции автоматического масштабирования в кластере постепенно уменьшаются. Иногда мы сталкиваемся с проблемами, когда автоматическое масштабирование превышает лимит. Когда к кластеру присоединяются новые узлы, выполняется много запросов, и одновременное добавление сотен узлов может привести к перегрузке службы API Server.


4. Мониторинг

Мы используем Prometheus для сбора метрик и Grafana для настройки графического интерфейса, панелей управления и оповещений. мы развертываем изkube-prometheusПроект запускается, проект собирает различные метрики и предоставляет удобную панель инструментов для завершения визуализации. Со временем мы добавили множество собственных уникальных информационных панелей, метрик и оповещений.

По мере роста числа узлов мы обнаружили, что огромное количество метрик, собираемых Prometheus, бесполезно. Хотя kube-prometheus предоставляет много полезных данных, некоторые из них мы никогда не используем. Используем интерфейс Prometheus删除некоторые из этих показателей.

В течение некоторого времени мы боролись с проблемой, из-за которой Prometheus потреблял все больше и больше памяти, пока, в конце концов, не стало OOM. Кажется, это происходит даже после установки очень большого объема памяти. Что еще хуже, при сбое требуется много времени для восстановления после загрузки.

В конце концов, мы нашли источник этих OOM, которым является взаимодействие между Grafana и Prometheus, где Grafana вызывает интерфейс Prometheus./api/v1/seriesЗапрос. /api/v1/seriesИнтерфейс получает все индикаторы мониторинга, что обеспечит непрерывный рост памяти. Мы улучшили Prometheus, чтобы включить этот контроль времени ожидания в контекст.

Хотя Prometheus дает сбой намного реже, восстановление WAL по-прежнему является проблемой, когда его необходимо перезапустить. Обычно восстановление всех журналов WAL занимает много времени, прежде чем Prometheus сможет собирать новые метрики и обслуживать запросы. С помощью Robust Perception мы нашли оптимизацию, настроив GOMAXPROCS=24. Prometheus пытается использовать все ядра во время воспроизведения WAL, а для серверов с большим количеством ядер вытеснение может снизить производительность.


5. Проверка здоровья

Для кластеров такого размера, конечно, необходимо полагаться на автоматизацию для обнаружения и удаления из кластера неправильно работающих узлов. С постепенным углублением мы создали полную систему проверки здоровья.

а. Пассивный осмотр

(Переводчик: вы можете назвать это мониторингом производительности) Некоторые проверки работоспособности являются пассивными и всегда выполняются на всех узлах. Они отслеживают основные системные ресурсы, такие как доступность сети, повреждение диска, переполнение диска или ошибки графического процессора и т. д. Есть много разных проблем с графическими процессорами, но одна из самых распространенных ошибок —无法纠正的ECC错误. Инструмент Nvidia Data Center GPU Manager (DCGM) позволяет запрашивать эту и многие другие ошибки.XidОшибки намного проще. Один из способов, которым мы отслеживаем эти ошибки, — этоdcgm-exporterВнесите метрики в нашу систему мониторинга Prometheus. Это индикатор DCGM_FI_DEV_XID_ERRORS. Кроме того, NVML Device Query API предоставляет подробную информацию о работоспособности и работе графического процессора.

Как только мы обнаруживаем ошибки, их обычно можно исправить, перезагрузив графический процессор или систему.

Другой формой проверки работоспособности является отслеживание событий обслуживания от вышестоящих облачных провайдеров. Большинство облачных провайдеров предоставляют способ узнать, отключена ли текущая виртуальная машина из-за предстоящего технического обслуживания. Например, установка исправлений обновления, замена оборудования и т. д.

Эти пассивно работающие мониторы работают на всех узлах. Если проверка работоспособности начнет давать сбой, нода автоматически установит тревогу, а при более серьезных сбоях проверки работоспособности мы также попытаемся выселить контейнер, группа действий определяется самим Подом, который можно настроить через Pod Disruption Budget, чтобы решить, разрешать ли это исключение.

б. Динамический тест графического процессора

К сожалению, не все проблемы с графическим процессором проявляются в виде кодов ошибок, видимых через DCGM. Мы создали собственную тестовую библиотеку, которая использует GPU для выявления других проблем и проверки того, что оборудование и драйверы работают должным образом. Эти тесты не могут работать в фоновом режиме, они должны монополизировать GPU на секунды или минуты.

Все узлы начинаются сpreflightTaints и метки добавляются в кластер. Эта зараза не позволяет планировать обычные модули на узле. Настройте DaemonSet для запуска тестовых модулей предварительной проверки на узлах с этой меткой. После успешного прохождения теста сам тест будет удаленpreflighttaints и labels, после этого узел готов к общему использованию.

Затем мы будем периодически запускать эти тесты в течение всего срока службы узла. Мы запускаем его как CronJob, чтобы он мог работать на любом доступном узле в кластере.


V. Квота ресурсов и их использование

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

пятно

У нас есть служба в каждом кластере, т.е.team-resource-manager, который имеет несколько функций. Его источником данных является ConfigMap, в котором указан кортеж (селектор узла, применяемая метка команды, количество заданий) для всех исследовательских групп с пропускной способностью в данном кластере. Он использует openai.com/team=teamname:NoSchedule для настройки соответствующего количества узлов.

team-resource-managerтакже настроитьadmission webhook(Переводчик: подключаемый модуль службы допуска) для применения соответствующего допуска на основе членства в команде отправителя при отправке каждой работы. Используя taints, мы можем гибко ограничивать планировщик подов Kubernetes, например разрешать поды с более низким приоритетом.任意Толерантность, которая позволяет командам делиться ресурсами без четкой координации.

CPU & GPU balloons

Помимо использования cluster-autoscaler для динамического масштабирования кластера виртуальных машин, мы также используем его для управления (удаления и повторного добавления) неработоспособных узлов в кластере. С этой целью мы будем страстными最小установлен в ноль, а кластер最大Установите доступную емкость. Однако, если средство кластерного автомасштабирования обнаружит простаивающие узлы, оно попытается масштабировать только до требуемой емкости. Такое масштабирование бездействия не является идеальным по ряду причин (задержка запуска ВМ, стоимость предварительного выделения, упомянутое выше влияние API-сервера).

Поэтому мы представили всплывающие подсказки Deployment для хостов CPU и GPU. Развертывание содержит最大值Количество низкоприоритетных конфигураций контейнера. Эти поды потребляют ресурсы внутри узла, поэтому модуль автоматического масштабирования кластера не считает их бездействующими. Однако, поскольку они имеют более низкий приоритет, планировщик может немедленно исключить их, чтобы освободить место для фактической работы. (Мы решили использовать Deployment вместо DaemonSet, чтобы не рассматривать DaemonSet как бездействующую рабочую нагрузку на узле.)

Следует отметить, что мы используем антипривязку контейнеров, чтобы обеспечить равномерное распределение контейнеров по узлам. Проблемы производительности с этим алгоритмом были исправлены, начиная с версии Kubernetes 1.18.


6. Планирование банд

В наших экспериментах обычно используется один или несколько StatefulSet, каждый из которых выполняется на отдельной части обучающей работы. Для оптимизатора исследователям необходимо запланировать все модули StatefulSet перед выполнением какого-либо обучения (поскольку мы часто используем MPI для совместной работы членов оптимизатора, а MPI чувствителен к изменениям членства в группах).

Однако по умолчанию Kubernetes не обязательно отдает приоритет запросам StatefulSet. Например, если два лабораторных задания запрашивают 100 % емкости кластера, но Kubernetes может запланировать только половину каждого лабораторного модуля, что приводит к тупиковой ситуации планирования, ни одно из лабораторных заданий не может быть завершено.

Мы попытались внедрить собственный планировщик, но столкнулись с некоторыми крайними случаями, которые вызвали конфликт с тем, как планируются обычные поды. Kubernetes 1.18 представил архитектуру подключаемых модулей Kubernetes framework, которая упрощает локальное добавление таких функций. Недавно мы представили плагин Coscheduling для решения этой проблемы.


7. Заключение

Есть еще много проблем, которые необходимо решить при масштабировании кластера Kubernetes. Некоторые из них включают:

а. Показатели мониторинга

В нашем масштабе встроенный в Prometheus механизм хранения TSDB медленно сжимается и требует много времени для восстановления WAL (Write-Ahead-Log) при каждом перезапуске, что доставляет нам массу больших неприятностей. Мы переходим на другие совместимые с Prometheus механизмы хранения и запросов. С нетерпением жду будущих сообщений в блоге о том, как это происходит!

B. Формирование сетевого трафика Pod

Когда мы расширим кластер, каждый под будет рассчитываться как имеющий определенную пропускную способность Интернета, тогда общий трафик всех подов будет очень неожиданным, поэтому нам необходимо внедрить технологию формирования трафика, чтобы предотвратить сетевые штормы, переполнение трафика и другие проблемы. .

Мы обнаружили, что Kubernetes является исключительно гибкой платформой для наших исследовательских нужд. Он масштабируется в соответствии с нашими самыми требовательными рабочими нагрузками. Хотя многое еще предстоит улучшить, команда суперкомпьютеров OpenAI продолжит изучать возможности масштабирования Kubernetes.

Для получения дополнительных материалов, пожалуйста, проверьте официальный аккаунт: DCOS

Бенджамин Чесс, Эрик Сиглер
Переводчик: Зуи
оригинал:openlove.com/blog/ трется лесом…


использованная литература