MAATRIX / Блог / Apache APISIX на сервере: частые ошибки и решения

Apache APISIX на сервере: частые ошибки и решения

MAATRIX

Apache APISIX — это Nginx с мозгами: под капотом OpenResty и LuaJIT, а вся конфигурация роутов, апстримов и плагинов хранится не в файлах, а в etcd и применяется на лету, без reload. Это же и источник половины проблем: если вы привыкли к классическому Nginx, где «поправил конфиг — перезапустил» решает всё, APISIX ломает эту привычку. Ниже — ошибки, с которыми реально сталкиваешься при развёртывании APISIX на своём сервере, и рабочие способы их обойти.

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

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

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

APISIX не стартует: `failed to fetch etcd version` и похожие

Самая частая причина падения при первом запуске — APISIX просто не может достучаться до etcd. Проверьте в логах:

tail -f /usr/local/apisix/logs/error.log

Типичная запись:

2026/08/29 10:12:03 [error] 1234#0: init_by_lua error: /usr/local/apisix/apisix/init.lua:xxx: failed to fetch etcd version: connection refused

Разбирайтесь по порядку:

  1. etcd вообще запущен? systemctl status etcd или, если etcd в контейнере, docker ps | grep etcd.
  2. Адрес в конфиге верный? В conf/config.yaml секция deployment.etcd.host должна указывать на реальный адрес и порт (обычно 2379), а не на 127.0.0.1, если etcd вынесен в отдельный контейнер или на другой хост.
  3. etcd слушает нужный интерфейс. Если etcd поднят с --listen-client-urls http://127.0.0.1:2379, снаружи контейнера или с другой машины он недоступен — нужно слушать 0.0.0.0 (и закрыть порт файрволом снаружи, оставив доступ только для APISIX).
  4. Версия etcd. APISIX 3.x требует etcd 3.4+ и использует API v3. Если у вас в системе древний etcd из пакетного менеджера дистрибутива — снесите его и ставьте бинарник с GitHub-релиза нужной версии.

Рабочий фрагмент config.yaml для связки с внешним etcd:

deployment:
  role: traditional
  role_traditional:
    config_provider: etcd
  etcd:
    host:
      - "http://10.0.0.5:2379"
    prefix: "/apisix"
    timeout: 30

После правки config.yaml APISIX нужно перезапустить целиком (apisix restart), это не тот файл, который применяется на лету — на лету применяются только записи, сделанные через Admin API.

Route создан, но отдаёт 404 или не подхватывается

Классическая ловушка новичка: вы отправили PUT на Admin API, получили 200 OK, а запрос к маршруту всё равно возвращает {"error_msg":"404 Route Not Found"}.

Проверьте по чек-листу:

  • URI совпадает буквально. APISIX по умолчанию матчит точный путь. Если роут создан на /api/users, а вы стучитесь на /api/users/, это разные маршруты. Нужен либо второй route, либо uris: ["/api/users", "/api/users/*"].
  • Host header важен. Если у route задан hosts, а вы обращаетесь по IP или другому домену, ответ будет 404. Уберите hosts из route или проверьте, что заголовок Host в запросе совпадает.
  • Приоритеты маршрутов. Два route с пересекающимися URI резолвятся по priority — по умолчанию 0. Если более общий маршрут (/*) создан позже с тем же приоритетом, порядок матчинга не гарантирован интуитивно, задавайте приоритет явно.
  • etcd действительно записал данные. Проверьте напрямую:
etcdctl --endpoints=http://127.0.0.1:2379 get /apisix/routes --prefix

Если запись есть в etcd, но APISIX её «не видит» — вероятно, воркеры не синхронизировались. Помогает apisix reload, но если это происходит регулярно, ищите проблему в сетевой связности между APISIX и etcd (обрывы, таймауты вотчей).

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

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

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

502/504 на апстриме, хотя бэкенд живой

Если сам сервис отвечает по curl напрямую, а через APISIX — 502 Bad Gateway или 504 Gateway Timeout, дело почти всегда в одном из трёх мест.

1. Апстрим указывает не туда. Проверьте узел через Admin API:

curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: <ваш-admin-key>'

Частая ошибка — в nodes указан localhost или 127.0.0.1, хотя APISIX работает в контейнере, а бэкенд — в соседнем контейнере в другой docker-сети. Указывайте имя сервиса из compose-сети или реальный внутренний IP.

2. Таймауты апстрима меньше, чем реально нужно бэкенду. По умолчанию timeout.connect/send/read — 6 секунд. Для тяжёлых API-запросов (генерация отчётов, экспорт) этого может не хватать:

upstream:
  timeout:
    connect: 6
    send: 60
    read: 60

Задаётся на уровне upstream или прямо в route.

3. keepalive-пул исчерпан. Под нагрузкой APISIX может упираться в лимит соединений к апстриму. Смотрите метрику apisix_upstream_status (если включён плагин prometheus) и увеличивайте keepalive_pool в upstream при необходимости.

Полезно параллельно проверить логи именно access, а не только error:

tail -f /usr/local/apisix/logs/access.log | grep ' 502 \| 504 '

Плагины не применяются или конфликтуют друг с другом

APISIX выполняет плагины в строгом порядке приоритетов (priority), и если вы навешали на один route несколько плагинов — например limit-req, jwt-auth и proxy-rewrite — порядок их срабатывания не тот, что порядок в JSON, а тот, что задан внутренним приоритетом плагина. Итог: proxy-rewrite может отработать раньше, чем jwt-auth успел провалидировать токен, и наружу утечёт лишний заголовок.

Проверить фактически применённые плагины на route:

curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: <ваш-admin-key>' | jq '.value.plugins'

Частые грабли с конкретными плагинами:

ПлагинТипичная ошибкаРешение
limit-req / limit-countЛимиты не работают за балансировщиком/CDNНастроить X-Real-IP через real-ip плагин или key_type: "var" с нужной переменной вместо IP
jwt-auth401 даже с валидным токеномПроверить рассинхрон времени между сервером и клиентом (JWT чувствителен к exp/nbf), сверить algorithm в consumer
proxy-cacheКэш не инвалидируетсяУказать cache_key явно, включающий нужные параметры запроса, а не полагаться на дефолт
corsБраузер всё равно ругается на CORSПлагин cors должен идти вместе с обработкой preflight — проверить, что OPTIONS-запросы не блокируются другим плагином раньше

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

Admin API недоступен или отдаёт 401/403

По умолчанию Admin API APISIX слушает 0.0.0.0:9180 с дефолтным ключом edd1c9f034335f136f87ad84b625c8f из коробочного config.yaml — это критическая дыра, если сервер смотрит в интернет без файрвола. Первое, что нужно сделать сразу после установки:

deployment:
  admin:
    admin_key:
      - name: "admin"
        key: "сгенерированный-длинный-случайный-ключ"
        role: admin
    allow_admin:
      - "127.0.0.1/32"
      - "10.0.0.0/8"

Если после этой правки Admin API вдруг перестал отвечать даже с разрешённого адреса — почти всегда забыли перезапустить APISIX (config.yaml не хот-релоадится) или в allow_admin не попал реальный исходящий IP (за NAT или в docker-сети это может быть не тот адрес, что вы ожидаете).

401 с правильным ключом обычно значит, что заголовок передан неверно — нужен именно X-API-KEY, а не Authorization: Bearer:

curl -i http://127.0.0.1:9180/apisix/admin/routes \
  -H 'X-API-KEY: сгенерированный-длинный-случайный-ключ'

Высокое потребление памяти и деградация под нагрузкой

APISIX сам по себе лёгкий (это же Nginx-воркеры), но специфика Lua-плагинов и etcd-вотчей даёт свои паттерны утечек и деградации.

  • Слишком много воркеров на маленьком сервере. По умолчанию nginx_config.worker_processes: auto — на VPS с 1-2 CPU это может создать больше воркеров, чем реально нужно, и каждый держит своё соединение к etcd. Для небольших серверов имеет смысл зафиксировать число явно:
nginx_config:
  worker_processes: 2
  • Кэш плагинов растёт без ограничений. Плагины вроде proxy-cache или response-rewrite с большими телами ответов могут раздувать shared dict. Проверьте лимиты в apisix.yaml / config.yaml секции plugin_config для конкретного плагина и не забывайте про cache_ttl.
  • etcd watch reconnect storm. Если etcd периодически теряет связь с APISIX (нестабильная сеть между хостами, что особенно критично при разнесении etcd и APISIX по разным дата-центрам), каждый воркер начинает реконнект и заново подгружает весь конфиг из etcd — на большом количестве routes это заметно грузит CPU. Если у вас именно такая архитектура, разумно держать etcd и APISIX в одной локации с низкой задержкой, а не тянуть через интерконнект между разными регионами.

Мониторить стоит через встроенный плагин prometheus — он отдаёт метрики на /apisix/prometheus/metrics, и связка с Grafana даёт вменяемую картину по латентности и статусам без гадания по логам. Если у вас уже есть стек мониторинга, посмотрите статью про Prometheus и Grafana на сервере — принципы сбора метрик там применимы и к APISIX.

Проблемы с TLS: сертификат не подхватывается или mTLS рвёт соединения

APISIX умеет хранить SSL-сертификаты прямо в etcd через Admin API (/apisix/admin/ssl), и это второй источник путаницы после etcd-подключения.

Если браузер видит дефолтный self-signed сертификат вместо загруженного:

  1. Проверьте, что snis в объекте ssl совпадает с доменом запроса точно, включая регистр и отсутствие www., если он не указан отдельно.
  2. Убедитесь, что сертификат и приватный ключ переданы как единая PEM-строка с реальными переводами строк (\n), а не как путь к файлу — Admin API ожидает содержимое, а не путь.
  3. Проверьте порядок цепочки — сначала сертификат домена, затем промежуточные, в конце (опционально) корневой.

Для автоматизации через Let's Encrypt проще не городить свой скрипт вокруг Admin API, а поставить apisix-ingress-controller в Kubernetes или использовать cert-manager-подобный подход вне k8s через cron с certbot и последующей отправкой обновлённого сертификата в etcd. Если вы вообще не завязаны на APISIX-специфику для получения сертификатов, посмотрите общий разбор в статье про Let's Encrypt SSL на сервере — сам выпуск сертификата там не отличается от любого другого сервиса.

mTLS между APISIX и апстримом настраивается через upstream.tls (client cert) и требует, чтобы у бэкенда были доверенные CA именно те, которыми подписан клиентский сертификат APISIX — рассинхрон CA-цепочек даёт тихий разрыв соединения без внятного сообщения об ошибке на стороне APISIX, только upstream SSL certificate verify error в error.log.

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

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

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

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

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

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

APISIX и Kong Gateway — в чём принципиальная разница для выбора?

Оба построены на OpenResty, но Kong по умолчанию тянет за собой PostgreSQL (или работает в DB-less режиме), а APISIX изначально спроектирован вокруг etcd и горячего применения конфигурации без reload. Если у вас уже есть etcd в инфраструктуре или вы цените низкую задержку применения изменений — APISIX логичнее. Сравнение конкретики есть в статье про Kong Gateway на сервере.

Нужен ли отдельный сервер под etcd?

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

Можно ли работать без Admin API, чисто через YAML-конфиг?

Да, режим standalone с config_provider: yaml появился как раз для тех, кто не хочет держать etcd. Конфигурация роутов пишется в apisix.yaml и подхватывается по изменению файла — удобно для GitOps-подходов, но теряется динамическое управление через API на лету.

Как понять, что проблема в APISIX, а не в самом Nginx/OpenResty под капотом?

Смотрите одновременно error.log (там видны и стандартные nginx-ошибки, и Lua-трейсы) и вывод apisix version — если ошибка сопровождается трейсбеком с путями /usr/local/apisix/apisix/..., это уровень плагинов и логики APISIX, а не голого nginx.

Что делать, если после апдейта APISIX перестали работать старые плагины?

Проверить changelog версии на breaking changes в схеме плагина (schema в Admin API мог измениться) и прогнать curl .../admin/routes/<id> — если поле подсвечено как невалидное, значит формат конфигурации плагина изменился между версиями, нужно смигрировать JSON руками.

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

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

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