Разработка AI-микросервиса под ключ: FastAPI, Docker, масштабирование

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

Направления AI-разработки

Часто задаваемые вопросы

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1530
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1354
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1070
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1328
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    772
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1074

Разработка AI-микросервиса: что ломается под нагрузкой

В разработке AI-микросервиса демонстрационный контур выглядит убедительно: один запрос, одна модель, чистая память. Как только на вход приходит реальный поток, время ответа под нагрузкой перестаёт быть предсказуемым — и первым это замечает пользователь, который уходит, не дождавшись ответа.

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

Пиковый трафик добивает контур отсутствием понятной деградации. Нет лимита на одновременные вычисления, нет порога, после которого сервис честно отвечает «занят», нет разогрева модели при старте и ограничения на размер входных данных. Итог — отказ сервиса целиком: падение процессов по памяти, зависшие соединения, потерянные запросы и ручной перезапуск, который не гарантирует, что следующий пик пройдёт иначе.

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

Что меняется после внедрения: измеримые эффекты

Эффекты внедрения фиксируем как метрики до начала работ: снимаем текущее время выпуска изменений, историю восстановлений после сбоев и долю ручных операций сопровождения. Через несколько недель после запуска те же замеры повторяем — видно, что изменилось по факту, а не «стало удобнее».

Время выпуска изменений сокращается в разы

Сервис на FastAPI описан конфигурацией, поэтому окружение поднимается воспроизводимо, а не «на той машине, где работало». Изменение проходит автотесты и выкладку по одному и тому же сценарию: вместо дней ручной сборки и согласований — часы, а для типовых правок логики ответа — десятки минут.

Восстановление становится предсказуемым

Каждый релиз помечен образом, предыдущая версия остаётся доступной для отката. Ограничения по памяти и времени ответа заданы явно, поэтому перезапуск контейнера возвращает сервис в рабочее состояние за минуты, а не после ручного разбора логов.

Уходит целый класс отказов при перегрузке

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

Меньше ручных операций сопровождения

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

Режимы работы сервиса: синхронный, потоковый, пакетный

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

Режим Когда подходит Ограничения режима
Синхронный Короткие задачи: классификация, извлечение полей, ответ по одному документу Соединение держится до конца генерации: длинный ответ упирается в тайм-ауты клиента и прокси, пропускная способность падает при росте одновременных запросов
Потоковый Диалоги и длинные тексты, где первые слова нужны пользователю сразу Обрыв канала рвёт ответ, отданный фрагмент не переиграть, выше требования к стабильности сети и журналированию
Пакетный Массовая обработка: ночные прогоны по архиву, пересчёт индексов, разбор выгрузок Результата нет «сейчас»: нужны очередь, хранилище промежуточных результатов и контроль провалов по каждому заданию

Что учесть до старта

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

Порядок работ: аудит, схема, разработка, передача

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

  1. Аудит текущего контура. Разбираем источники данных, их полноту и доступность, ограничения инфраструктуры, требования к времени ответа. На выходе — карта рисков и понимание, где решение даёт эффект.
  2. Схема взаимодействия. Фиксируем, кто вызывает сервис, в каком формате приходят данные, как обрабатываются отказы и что происходит при недоступности модели. Схему согласуем до кода: правки на этом шаге стоят дешевле всего.
  3. Проверка на исторических данных. Собираем набор примеров и сравниваем модели по метрикам задачи: доля верных ответов, полнота, число ложных срабатываний. Так выбираем подход и порог для включения в реальный поток.
  4. Разработка AI-микросервиса. Модель изолирована от бизнес-логики, версии моделей и журнал запросов ведутся отдельно. Сборка и развёртывание воспроизводимые — стенд не расходится с рабочим контуром.
  5. Нагрузочная проверка. Прогоняем сервис на реальном профиле запросов: время ответа под пиком, поведение при отказах, рост очереди. По результатам настраиваем лимиты и правила масштабирования.
  6. Передача в сопровождение. Отдаём конфигурации, схему, регламент обновления модели и разбора инцидентов, обучаем команду. Первое время сопровождаем релизы и следим за качеством на живом потоке.

Что остаётся у вас: конфигурации, схемы, регламенты

Работа заканчивается не демонстрацией, а набором артефактов, по которым сервис живёт без нас. Всё, что мы делаем в процессе, оформляется так, чтобы ваша команда могла развернуть окружение с нуля, объяснить проверяющему, какие данные куда идут, и выкатить обновление без переписки с подрядчиком. Ниже — что именно передаётся в репозиторий и во внутреннюю базу знаний.

  • Конфигурации окружений — отдельные наборы параметров для разработки, тестового и рабочего контура, секреты хранятся вне репозитория и подставляются при развёртывании, поэтому один и тот же образ ведёт себя предсказуемо везде.
  • Схема потоков данных — откуда приходит запрос, какие модели и сервисы его обрабатывают, что пишется в хранилище, что кэшируется и что уходит во внешние системы; на схеме отмечены точки, где данные обезличиваются.
  • Политики доступа — роли и области видимости, правила выпуска и отзыва ключей, ограничение частоты обращений, журналирование вызовов с идентификатором запроса для разбора инцидентов.
  • Документация сервиса — описание контрактов, примеры запросов и ответов, коды ошибок и их причины, ограничения по размеру входных данных и поведение при перегрузке.
  • Регламент сопровождения — порядок выпуска версий, схема отката на предыдущий образ, перечень типовых сбоев и первых действий дежурного, точки эскалации.
  • Метрики и оповещения — набор показателей времени ответа, доли ошибок и потребления ресурсов, пороги срабатывания и маршрут уведомлений до конкретной команды.
  • Инструкция по развёртыванию — пошаговый запуск на чистом окружении, требования к ресурсам, порядок миграции данных и проверка работоспособности после установки.

Кейс: как сервис перестал падать под пиковой нагрузкой

Проект — сервис автоматической обработки документов: на входе файл, на выходе разметка полей и оценка уверенности модели. В ровном потоке он работал предсказуемо, но на пиках, когда запросов приходило в 4–5 раз больше обычного, начинал деградировать: время ответа для 95% запросов уходило с 0,4 секунды до 8–9, доля отказов доходила до 12%, а часть задач терялась при перезапуске контейнера.

Где было узкое место

Расчёт модели выполнялся прямо в обработчике запроса: пока она считала, рабочий процесс был занят и не принимал новые соединения. Дальше включалась лавина — очередь росла, ожидание превышало лимит, заданный клиентом, клиент повторял запрос, и нагрузка удваивалась. К этому добавлялся холодный старт: каждая новая реплика поднималась несколько минут, поэтому масштабирование запаздывало относительно пика.

Что изменили и какие метрики получили

Расчёт вынесли в отдельный пул воркеров, а веб-слой оставили только на приём и валидацию — приём соединений перестал зависеть от загрузки модели. Запросы пошли через очередь с ограниченным параллелизмом и объединением мелких файлов в пачки, результаты переиспользуются по контрольной сумме содержимого, модель поднимается один раз на процесс. На приёме и валидации сервис держится на FastAPI — асинхронная обработка не блокирует рабочий процесс.

После перестройки пропускная способность выросла в 3,5 раза на том же парке машин. На прежних пиках время ответа для 95% запросов держится в пределах 0,5 секунды, доля отказов опустилась ниже 0,5%, повторы со стороны клиента почти исчезли. Перезапуск реплики больше не теряет задачи: очередь держит их до подтверждения обработки — узким местом была не модель, а тяжёлый вызов в критическом пути запроса.

Что делать, если сервис уже развёрнут, но не масштабируется?

Уже развёрнутый сервис отвечает на запросы, но на росте нагрузки время ответа растёт и появляются отказы — полная пересборка нужна редко. Узкое место обычно локально: состояние в памяти процесса, синхронная обработка тяжёлых запросов, отсутствие метрик. Снимаем профиль под нагрузкой и находим, где именно теряется пропускная способность.

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

Вторая — синхронная обработка: долгий запрос держит обработчик и не даёт принимать новые. Очередь задач с отдельными исполнителями разводит приём и вычисление — масштабирование идёт по длине очереди, а всплески сглаживаются без простоя. Третья — окружение: пул соединений к базе, кэш горячих ответов, лимиты одновременных вычислений. Рост часто упирается в исчерпание соединений или таймауты, а не в модель, и это правится настройками.

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

Завершает работу передача в сопровождение: схема развёртывания, описание настроек, порядок действий при отказе, правила оповещения. Команда заказчика эксплуатирует сервис дальше сама или вместе с нами.

Сомнения по нагрузке, доступам и сопровождению

Возражения на этом этапе обычно одни и те же: как переносить уже работающую модель, кто и как получает доступ к данным, не смешаются ли контуры и что делать с дообучением после запуска. Ниже — прямые ответы без общих слов.

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

Как перенести работающую модель без остановки сервиса?

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

Какой доступ к данным нужен сервису?

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

Зачем изолировать окружения?

У разработки, предпродуктивного и рабочего контуров свои базы, ключи и конфигурации при одинаковых образах. Проверки идут на обезличенных наборах, поэтому тестовый запуск не задевает рабочие данные.

Что происходит с дообучением после запуска?

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

Кто отвечает за сервис после сдачи?

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

Обсудим задачу и вернёмся с оценкой и планом

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

Дальше достаточно нескольких уточнений по нагрузке и ограничениям. Чем конкретнее исходные данные, тем меньше допущений в плане внедрения и тем точнее состав команды под ваш случай.

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

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

Опишите задачу письмом или на созвоне — вернёмся с оценкой и планом.