Иллюзия обычного сервиса
Для многих обывателей, да и инженеров, которые не углублялись в тему, работа с LLM выглядит как работа с обычным сервисом: мы просто кидаем запросы по нужному endpoint и получаем JSON с ответом. Схема знакомая, инфраструктура привычная — и первое время кажется, что никакой специфики нет.
Но стоит выйти за рамки единственного GPU-сервера, как сразу появляются вопросы, на которые стандартные инструменты не дают ответа: как здесь работает кэш? От чего зависит время ответа и почему оно меняется от запроса к запросу? Что делать с огромным контекстным окном, которое само по себе требует значительных ресурсов памяти?
Почему Kubernetes не справляется
Пока всё происходит на одном GPU-сервере, эти вопросы не слишком важны: узкое место очевидно, масштаб управляем. Но как только речь заходит о масштабных распределённых системах — начинаются настоящие трудности.
Обычный Kubernetes просто не понимает, как устроен запрос языковой модели. Планировщик K8s видит поды и ресурсы — но не видит ни prefill/decode-фазы генерации, ни состояния KV-кэша, ни того, насколько дорого в действительности обходится конкретный запрос с длинным контекстом. Это фундаментальное несоответствие между абстракциями оркестратора и природой LLM-вычислений.
Что изменилось за последний год
Тем не менее за последний год платформенные инженеры очень хорошо продвинулись в этом вопросе. В экосистеме появился ряд механизмов, которые начинают закрывать этот разрыв: DRA (Dynamic Resource Allocation) — новый API для управления специализированными устройствами вроде GPU, GIE (GPU Instance Exposure) и LLM-D — инструменты для inference-специфичной маршрутизации и планирования нагрузки.
В этой статье я хочу подробно разобрать, как именно строится K8s-кластер под высоконагруженные LLM: какие компоненты нужны, как они взаимодействуют и на какие подводные камни стоит обратить внимание ещё до того, как система уйдёт в production.