MAATRIX / Блог / Сколько RAM нужно для Apache APISIX

Сколько RAM нужно для Apache APISIX

MAATRIX

Apache APISIX — высокопроизводительный API-шлюз на базе Nginx/OpenResty, и в этом же кроется путаница с расчётом памяти: часть ресурсов ест сам APISIX, а часть — etcd, который хранит конфигурацию маршрутов и обычно живёт рядом. Документация проекта даёт общие рекомендации «от 1 vCPU и 1 ГБ RAM», но не говорит, как это меняется с ростом числа маршрутов, плагинов и соединений. Разберём, из чего реально складывается потребление RAM у APISIX, и дадим ориентиры под конкретные сценарии.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Из чего складывается память APISIX

APISIX — это не монолит, а связка из двух разных компонентов, и считать память нужно для каждого отдельно.

Data plane (сам APISIX). Построен поверх OpenResty (Nginx + LuaJIT). При старте поднимается master-процесс и несколько worker-процессов — именно worker'ы обрабатывают трафик, выполняют маршрутизацию и плагины. Каждый worker — отдельный процесс со своей копией Lua-состояния, поэтому память в первом приближении растёт пропорционально числу worker-процессов, а не числу запросов в секунду.

Control plane (etcd). APISIX по умолчанию хранит конфигурацию routes, upstream'ов, plugin-конфигов и SSL-сертификатов в etcd — распределённом key-value хранилище на Go. APISIX-worker'ы держат вотчер на etcd и подтягивают изменения конфигурации в свой локальный кэш при любом обновлении. Это значит, что etcd — это не опция, а обязательный сосед APISIX в классическом режиме развёртывания, и его память нужно закладывать отдельно, даже если он крутится на том же сервере.

Что конкретно тратит RAM внутри worker-процесса:

  • Базовый OpenResty-рантайм — LuaJIT VM и загруженные Lua-модули самого APISIX (роутер, схема валидации, ядро плагинов) — фиксированная база, которая присутствует даже без единого маршрута.
  • Локальный кэш конфигурации — копия всех routes/upstreams/plugin-конфигов, синхронизированная из etcd, хранится в памяти каждого worker'а. Чем больше маршрутов и сложнее плагины на них, тем больше эта копия.
  • Shared dict (общая для worker'ов память через mmap) — используется, например, плагинами limit-count/limit-req для счётчиков лимитов и plugin prometheus для метрик. Не умножается на число worker'ов, но резервируется целиком при старте.
  • Буферы соединений — keepalive к клиентам и upstream'ам, буферы тела запроса/ответа (особенно при буферизации больших payload'ов).
  • Кэш плагинов — часть плагинов (JWT, opa, ext-plugin) держат локальные кэши валидированных токенов или decision-результатов.

Практический вывод: тюнинг памяти APISIX — это работа с числом worker'ов, объёмом конфигурации и лимитами соединений, а не попытка угадать «сколько нужно на трафик».

Базовый footprint в простое

Ориентировочно (это оценка, а не гарантированное измерение — реальные цифры зависят от версии APISIX, ОС и включённых плагинов):

  • APISIX без маршрутов, с дефолтным config.yaml и небольшим числом worker'ов, в простое занимает около 150–300 МБ суммарного RSS по всем worker-процессам — это загруженный Lua-рантайм и служебные модули ядра, которые есть независимо от нагрузки.
  • etcd рядом, при небольшом количестве ключей (десятки-сотни маршрутов) и стандартных настройках, добавляет ещё порядка 50–150 МБ. etcd в первую очередь чувствителен к дисковой задержке (для WAL и снапшотов), а не к объёму RAM — но отдельный лимит памяти под него закладывать всё равно нужно, иначе он попадёт под общий OOM вместе с APISIX.
  • Admin API (порт 9180) работает в тех же worker-процессах, что и data plane — отдельного бюджета под него не нужно, но при активном использовании (частые изменения конфигурации через API) растёт частота записи в etcd.

Итого голый APISIX + etcd на одном сервере без реального трафика — грубо говоря, 300–500 МБ занятой памяти. Дальше всё зависит от того, что вы на него навесите.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Как растёт память с нагрузкой и числом маршрутов

Три фактора реально двигают потребление RAM вверх:

1. Число worker-процессов. По умолчанию worker_processes: auto в config.yaml — APISIX поднимет по одному worker'у на каждое ядро CPU. Это стандартная практика для Nginx-подобных серверов, но означает, что на 8-ядерном сервере у вас 8 копий Lua-рантайма и 8 копий кэша конфигурации в памяти одновременно. Если ядер много, а трафик скромный, снизьте worker_processes до фиксированного числа и сэкономьте память ценой меньшего параллелизма.

2. Количество маршрутов и upstream'ов. Каждый route/upstream/plugin-конфиг — распарсенная Lua-таблица в памяти каждого worker'а. Для десятков-сотен маршрутов это несущественно (единицы-десятки МБ), но при тысячах маршрутов с объёмными плагин-конфигами прирост становится заметным и умножается на число worker'ов.

3. Число активных соединений. Каждое keepalive-соединение (клиент → APISIX и APISIX → upstream) держит буферы. Параметр event.worker_connections задаёт лимит соединений на worker — по умолчанию около 10000. Реальная память растёт не с лимитом, а с фактическим числом одновременно открытых keepalive-сессий: тысячи долгоживущих соединений (SSE/websocket) — заметная добавка к RSS, короткие HTTP-запросы почти не сказываются.

RPS сам по себе на память влияет слабо — это вопрос CPU. Память растёт от того, сколько одновременно открытых соединений и насколько сложна конфигурация в каждом worker'е.

Плагины: что добавляет заметный вес

Не все плагины одинаково влияют на память — вот основные категории:

  • Лимитирование (limit-count, limit-req, limit-conn) — используют shared dict фиксированного размера для счётчиков. Влияние небольшое и не растёт с трафиком (ограниченное число ключей = число уникальных клиентов/маршрутов).
  • Аутентификация (jwt-auth, key-auth, basic-auth, openid-connect) — держат в памяти таблицы consumer'ов и, для JWT/OIDC, могут кэшировать JWKS-ключи и результаты валидации. При тысячах consumer'ов это заметная, но не критичная добавка.
  • Метрики и трейсинг (prometheus, opentelemetry, skywalking) — держат буферы метрик в shared dict и локальные счётчики per-route/per-service. prometheus-плагин чувствителен к числу уникальных label-комбинаций: тысячи маршрутов с метками по route_id заметно поднимают кардинальность и память экспортёра.
  • Трансформация тела (proxy-rewrite, response-rewrite, grpc-transcode) — работают с буферами тела запроса. При полной буферизации (а не потоковой передаче) больших payload'ов это временный, но реальный расход на каждый активный запрос.
  • Кэширование (proxy-cache) — при памяти как backend'е кэша размер напрямую задаётся в конфигурации кэш-зоны и должен закладываться отдельным явным бюджетом, а не считаться «фоновой» добавкой.

Практический совет: если вы включаете десяток плагинов «на всякий случай» на каждый маршрут, потребление памяти растёт линейно с числом активных плагин-конфигов на worker. Держите набор плагинов на продакшен-маршрутах осознанным, а не «включим всё, вдруг пригодится».

Настройка: как управлять памятью через config.yaml

Основные рычаги лежат в conf/config.yaml:

apisix:
  node_listen: 9080
  enable_admin: true

nginx_config:
  worker_processes: auto      # можно зафиксировать числом, напр. 2
  worker_rlimit_nofile: 20480
  event:
    worker_connections: 10620  # лимит соединений на worker

Что стоит проверить в первую очередь при нехватке памяти:

  • Зафиксировать worker_processes вместо auto, если ядер на сервере больше, чем реально нужно для вашего трафика — меньше worker'ов означает меньше копий кэша конфигурации в памяти.
  • Снизить worker_connections, если пиковое число одновременных соединений заведомо ниже дефолтных ~10000 — буферы аллоцируются по факту соединений, но лимит ограничивает потенциальный максимум и защищает от неожиданных скачков нагрузки.
  • Ограничить APISIX и etcd через cgroup/Docker-лимиты (--memory в docker-compose или MemoryMax= в systemd unit) — так при аномалии упадёт конкретный контейнер, а не вся система через глобальный OOM killer.
  • Развернуть etcd отдельно от APISIX на среднем и крупном проекте — так data plane (реплики APISIX) масштабируется независимо от control plane, и падение памяти на одном не утащит за собой другой. На небольшом проекте это избыточно — etcd прекрасно живёт на одном сервере с APISIX в контейнерах.

Сколько закладывать: таблица конфигураций

Цифры ориентировочные, для дефолтного набора плагинов и без экзотических кастомных Lua-модулей — на реальном проекте стоит смотреть на фактическое потребление через docker stats или мониторинг, а не только на таблицу.

СценарийМаршрутыОдновременные соединенияRAM (APISIX)RAM (etcd)vCPU
Dev/тест, один инстансдо 20до 100512 МБ – 1 ГБ256 МБ1
Малый прод (несколько сервисов)до 100до 10001–2 ГБ512 МБ2
Средний прод (API-шлюз для команды)до 500до 50002–4 ГБ512 МБ – 1 ГБ2–4
Высокая нагрузка (много плагинов, тысячи маршрутов)1000+10000+4–8 ГБ и выше1–2 ГБ (отдельный сервер)4–8

Для сценариев «средний прод» и выше etcd разумнее выносить на отдельный сервер (или в отдельный кластер из 3 нод для отказоустойчивости) — это и снижает риск конкуренции за память с APISIX, и упрощает горизонтальное масштабирование data plane: можно поднять несколько реплик APISIX за балансировщиком, все они подключатся к одному control plane. Если решаете, на чём поднимать балансировку перед несколькими репликами APISIX, пригодится статья про HAProxy или Nginx для балансировки.

Мониторинг и что делать при нехватке памяти

Прежде чем гадать «хватит ли 2 ГБ», проще включить встроенный плагин prometheus (эндпоинт /apisix/prometheus/metrics) и собирать метрики в Grafana — это даст реальную картину по вашему трафику, а не усреднённые ориентиры из таблицы. Разворачивание связки описано в статье как установить и настроить Grafana и Prometheus на VPS.

Признаки того, что памяти не хватает:

  • worker-процессы APISIX периодически перезапускаются без видимой причины — проверьте journalctl -k | grep -i "out of memory" на предмет срабатывания OOM killer;
  • растёт задержка ответов при неизменном RPS — часто это своп (если включён) или частая пересборка Lua-таблиц конфигурации при активных изменениях через Admin API;
  • etcd отдаёт предупреждения о медленных apply — это чаще про дисковую задержку, чем про RAM, но при дефиците памяти под файловый кэш ОС эффект усиливается.

Поскольку APISIX построен на том же Nginx/OpenResty-стеке, что и обычный Nginx-reverse-proxy, часть типовых проблем с памятью пересекается — например, разрастание буферов при неправильной буферизации тела запроса. Если раньше сталкивались с похожим на голом Nginx, пригодится статья про высокое потребление памяти Nginx — многие причины и способы диагностики переносятся на APISIX почти без изменений.

Если после диагностики видно, что дело не в конфигурации, а в честной нехватке ресурсов под растущий трафик — проще заранее взять сервер с запасом по памяти, чем гоняться за утечками на проде.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли запустить APISIX вообще без etcd?

В отдельных версиях APISIX 3.x есть standalone-режим, где конфигурация читается из локального YAML-файла без запущенного etcd — это снижает требования к памяти и упрощает деплой для небольших статичных конфигураций, но теряется динамическое изменение маршрутов через Admin API. Сверьтесь с документацией конкретной версии — поведение этого режима менялось между релизами.

Сколько памяти реально нужно etcd при 50 маршрутах?

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

worker_processes: auto — это всегда правильный выбор?

Не всегда. На многоядерном сервере с невысоким трафиком auto создаст лишние worker-процессы, каждый со своей копией кэша конфигурации — чистый перерасход. Если параллелизм по всем ядрам не нужен, зафиксируйте меньшее число вручную.

Как понять, что OOM убивает именно APISIX?

Смотрите journalctl -k | grep -i "oom" — там указан PID и имя процесса. В Docker дополнительно проверьте docker inspect <container> | grep -i oomkilled — покажет, упирался ли контейнер в собственный memory limit.

Нужно ли etcd на отдельном сервере с самого начала?

Нет, для dev и небольшого прод-окружения совместное размещение с APISIX — нормальная практика. Разделение имеет смысл при горизонтальном масштабировании data plane или когда etcd нужен в отказоустойчивом кластере.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →