Беззмінний веб-сервіс проти сервера моделей
Типовий контейнер веб-сервісу майже беззмінний: образ містить код програми та невелику кількість залежностей, він запускається за кілька секунд, і будь-яка інстанція функціонально еквівалентна будь-якій іншій, що робить горизонтальне масштабування та розгортання Kubernetes з перемиканням навантаження на невизначеність таким ефективним — знищити один поди, запустити інший, нічого не втрачається, оскільки все значуще було всередині цього поди. Сервер моделей машинного навчання порушує цю припущення певним і важливим чином: контейнер містить не лише код, але й код плюс великий, версійний бінарний артефакт (навчені ваги, який може сягати десятків гігабайт для великої моделі), який повинен бути завантажений у пам’ять або пам’ять GPU перед тим, як контейнер може обробити будь-який запит, а цей процес завантаження може тривати від секунд до кількох хвилин залежно від розміру моделі та місця зберігання ваг.
Це змінює операційні припущення, на яких було побудовано зауваження Kubernetes. Запит готовності, який просто перевіряє «чи слухає HTTP-сервер», може повідомити, що поди готовий, поки модель завантажується, що призведе до того, що балансувальник навантажень маршрутизуватиме реальний трафік до поду, який вичерпає час або зламається; розгортання машинного навчання потребує запиту готовності, який конкретно перевіряє, чи модель завантажена в пам’ять. Час запуску також безпосередньо впливає на швидкість масштабування під навантаженням або випуску нової версії моделі — час завантаження моделі тривалістю дві хвилини означає, що автомасштабування реагуватиме з затримкою у дві хвилини, а не майже миттєве масштабування беззмінного веб-сервісу, яке необхідно враховувати при плануванні ємності, а не відкидати його.
GPU не є ресурсом, який можна розділити як процесор або пам’ять”, “paragraphs”: [“Kubernetes планує процесор і пам’ять за допомогою дискретних, ділимих одиниць – ви можете попросити 250 мілікоре процесора та 512 МБ пам’яті, і планувальник з радістю розмістить багато невеликих контейнерів на одному вузлі, спільно використовуючи базовий ресурс. GPU в стандартній моделі плагіна пристроїв Kubernetes розглядаються як цілі, нероздільні одиниці: контейнер отримує весь GPU або взагалі нічого, оскільки до відносно нещодавнього часу не було стандартного та безпечного способу дозволити незалежним процесам спільно використовувати пам’ять GPU та обчислення без впливу одного на інший або аварійного завершення роботи. Це означає, що легка модель, яка потребує лише 2 ГБ пам’яті з 40 ГБ GPU, все ще монополізує весь пристрій за допомогою нерозумного планування, що є справді неефективним дефолтним станом, який призвів до цілого екосистеми обхідних шляхів – багатоінстанційне GPU (MIG) NVIDIA на новіших серверних картках та програмні планувальники таймінгу, які дозволяють кільком контейнерам спільно використовувати цикли обчислень GPU в черзі, з певною перешкодою між робочими навантаженнями.
GPU вузли також досить дорогі та рідкісні за обсягом доступності в хмарі, тому вважати їх звичайними вузлами обчислень у групі автоматичного масштабування є помилкою, яку часто роблять команди і потім виправляють. Виділений пул GPU з специфічними мітками та толерантністю (так лише контейнери, які явно запитують GPU, розгортаються там, щоб утримувати звичайний веб-трафік від дорогого обладнання), поєднаний із агресивним масштабуванням до нуля при бездіяльності, є стандартною практикою, особливо тому, що інстанси GPU можуть коштувати від п’яти до двадцяти разів дорожче за годину порівняно з інстансами CPU, а плата за непрацюючий A100 вночі через те, що пул вузлів для пакетної висновування ніколи не масштабувався, є поширеною та дорогою помилкою на початку.
Версіонування артефакту, а не лише коду
Традиційна Docker-зображення з тегами (myapp:v1.4.2) захоплюють код застосунку, але для контейнера машинного навчання модельні ваги є другим, незалежно версіюваним артефактом, який змінюється набагато швидше за код – команда даних може перенавчати та випускати нову модель щотижня, тоді як код розгортання змінюється лише раз на квартал. Вбудовування ваг безпосередньо в образ (КОПІЮВАТИ model.bin у Dockerfile) означає, що кожен оновлення моделі запускає повний перебір образу та передачу великих розмірів даних, що є повільним і зв’язує дві речі, які повинні бути незалежно розгортатися та відкочуватися, незалежно. Більш поширений звичайний шаблон розгортання замінює модельний артефакт зовнішнім станом, який витягується під час запуску контейнера (або монтується через init-контейнер) з реєстру моделей або сховища об'єктів, а версія артефакту фіксується хешем або тегом у конфігурації розгортання, а не в Dockerfile – таким чином відкат поганої моделі не вимагає перебудови та розгортання всього образу, лише перенаправлення розгортання на попередню версію артефакту, що є швидшим і безпечнішим шляхом відкату.
Парне виведення проти безперервного обслуговування в якості різних шаблонів контейнерів
Парне виведення (інтерфейс користувача, що відповідає протягом менше ніж, скажімо, 200 мілісекунд) та парне виведення (оцінка десяти мільйонів записів вночі для щоденного звіту) є архітектурно такими різними, що зазвичай розгортаються як різні типи робочих навантажень Kubernetes, а не просто різні конфігурації одного й того ж. Безперервне обслуговування зазвичай це Розгортання з горизонтальним масштабуванням вузлів, яке реагує на глибину черги запитів або затримку, підтримуючи його в постійному стані готовності, оскільки затримка холодного запуску неприйнятна для кінцевого користувача, який чекає відповіді. Парне виведення більш природньо розгортається як робоче навантаження Kubernetes Job або, для дійсно великих навантажень, керується двигуном робочого процесу, таким як Kubeflow Pipelines або Argo Workflows, які можуть запустити пул вузлів GPU, розподілити десять мільйонів записів по них і знищити весь пул до нуля вартості в момент завершення роботи — шаблон, який був би неефективним для безперервного обслуговування (постійні холодні запуски), але є саме правильним для чергуючого навантаження, керованого розкладом, де ви оптимізуєте загальну вартість і пропускну здатність, а не затримку за запитом.
Часті запитання
Чому Kubernetes за замовчуванням не може ділити одну GPU між багатьма маленькими контейнерами для обслуговування моделей?
Стандартний плагін Kubernetes GPU розглядає GPU як нероздільну одиницю, оскільки історично не було безпечного стандартного способу ізолювати пам’ять та обчислення GPU між незалежними процесами; новіше апаратне розділення (MIG) та програмне планування за часом дозволяють вирішити цю проблему.
Чи потрібно випікати навчені ваги моделі в Docker образ?
Зазвичай ні для виробничих систем, оскільки ваги оновлюються з іншого, часто набагато швидшого темпу, ніж код застосунку; завантаження артефакту моделі з зовнішнього реєстру під час запуску зберігає незалежне версіонування та можливість відкату оновлень моделі та коду.
Чому час завантаження моделі має більше значення в розгортаннях машинного навчання, ніж у типових веб-сервісах?
Завантаження великої моделі в пам’ять або пам’ять GPU перед тим, як контейнер може обслуговувати трафік, може займати секунди чи хвилини, що безпосередньо уповільнює час реакції на масштабування та вимагає готовності моделі, орієнтованої на модель, щоб балансер навантаження не маршрутизував трафік до контейнера, який ще завантажується.
Чому робочі завдання з пакетного інференсу зазвичай розгортаються інакше, ніж реальні API моделей в режимі реального часу?
Пакетний інференс є переривчастим та оптимізованим для пропускної здатності, тому він підходить для Kubernetes Job або двигуна робочого процесу, який масштабує пул GPU від нуля до повної потужності навколо завдання; обслуговування в режимі реального часу потребує постійного підтримки теплого стану, щоб уникнути затримки холодного запуску для кінцевих користувачів, що підходить для стандартного автоматично масштабованого Deployment.
Спробуйте наживо
Усе, що вище, працює прямо у вашому браузері — відкрийте the simulation і змінюйте параметри під час роботи. Нічого не встановлюється, нічого не завантажується на сервер, уся модель живе в одній вкладці.
▶ Відкрити симуляцію the simulation