Apache APISIX на сервере: частые ошибки и решения
Apache APISIX — это Nginx с мозгами: под капотом OpenResty и LuaJIT, а вся конфигурация роутов, апстримов и плагинов хранится не в файлах, а в etcd и применяется на лету, без reload. Это же и источник половины проблем: если вы привыкли к классическому Nginx, где «поправил конфиг — перезапустил» решает всё, APISIX ломает эту привычку. Ниже — ошибки, с которыми реально сталкиваешься при развёртывании APISIX на своём сервере, и рабочие способы их обойти.
Содержание
- APISIX не стартует: `failed to fetch etcd version` и похожие
- Route создан, но отдаёт 404 или не подхватывается
- 502/504 на апстриме, хотя бэкенд живой
- Плагины не применяются или конфликтуют друг с другом
- Admin API недоступен или отдаёт 401/403
- Высокое потребление памяти и деградация под нагрузкой
- Проблемы с TLS: сертификат не подхватывается или mTLS рвёт соединения
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество 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
Разбирайтесь по порядку:
- etcd вообще запущен?
systemctl status etcdили, если etcd в контейнере,docker ps | grep etcd. - Адрес в конфиге верный? В
conf/config.yamlсекцияdeployment.etcd.hostдолжна указывать на реальный адрес и порт (обычно2379), а не на127.0.0.1, если etcd вынесен в отдельный контейнер или на другой хост. - etcd слушает нужный интерфейс. Если etcd поднят с
--listen-client-urls http://127.0.0.1:2379, снаружи контейнера или с другой машины он недоступен — нужно слушать0.0.0.0(и закрыть порт файрволом снаружи, оставив доступ только для APISIX). - Версия 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-auth | 401 даже с валидным токеном | Проверить рассинхрон времени между сервером и клиентом (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 сертификат вместо загруженного:
- Проверьте, что
snisв объекте ssl совпадает с доменом запроса точно, включая регистр и отсутствиеwww., если он не указан отдельно. - Убедитесь, что сертификат и приватный ключ переданы как единая PEM-строка с реальными переводами строк (
\n), а не как путь к файлу — Admin API ожидает содержимое, а не путь. - Проверьте порядок цепочки — сначала сертификат домена, затем промежуточные, в конце (опционально) корневой.
Для автоматизации через 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 ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →