Kong Gateway на сервере: частые ошибки и решения
Kong Gateway ставят перед микросервисами, чтобы не писать аутентификацию, лимиты и логирование в каждом сервисе отдельно, а вынести это в один слой прокси с плагинами. На боевом сервере Kong ломается предсказуемо в одних и тех же местах: миграции базы не проходят, сервисы отвечают 502 без внятной причины, Admin API оказывается открытым наружу, а плагины конфликтуют друг с другом по порядку выполнения. Разберём эти ситуации по порядку — с командами для диагностики и рабочими конфигами.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Архитектура Kong и где чаще всего рвётся
Kong — это Nginx с прослойкой на Lua (OpenResty), которая на каждый запрос читает конфигурацию из базы данных или из декларативного YAML-файла и решает, куда маршрутизировать трафик и какие плагины применить. Три сущности, вокруг которых строится вся конфигурация:
- Service — описание бэкенда (URL, таймауты, ретраи).
- Route — правило, по какому пути/хосту/методу запрос попадает в Service.
- Plugin — навешивается на Service, Route, Consumer или глобально.
Есть два режима работы, и путаница между ними — источник половины проблем новичков:
- DB-режим (PostgreSQL) — конфигурация хранится в базе, меняется через Admin API «на лету», нужен
kong migrations. - DB-less режим — конфигурация лежит в одном YAML-файле (
kong.yml), Kong перечитывает его при рестарте или через/configэндпоинт. Проще в эксплуатации, но без хранения состояния плагинов между рестартами (например, счётчиков rate-limiting без внешнего Redis).
Для одного сервера с десятком сервисов DB-less обычно надёжнее — меньше движущихся частей, нечему рассинхронизироваться. Для кластера из нескольких нод Kong под общим Admin API нужен PostgreSQL как единый источник правды.
Ошибки установки и миграций базы данных
Самая частая ошибка при первом запуске в DB-режиме:
Error: [PostgreSQL error] failed to connect to `host=kong-database port=5432`: connection refused
Причина почти всегда одна: контейнер Kong стартует раньше, чем PostgreSQL готов принимать соединения. depends_on в docker-compose не ждёт готовности базы, а только порядок запуска контейнера. Решение — healthcheck на Postgres и condition: service_healthy:
services:
kong-database:
image: postgres:16
environment:
POSTGRES_USER: kong
POSTGRES_PASSWORD: kongpass
POSTGRES_DB: kong
healthcheck:
test: ["CMD-SHELL", "pg_isready -U kong"]
interval: 5s
timeout: 5s
retries: 10
kong-migrations:
image: kong:3.7
command: kong migrations bootstrap
depends_on:
kong-database:
condition: service_healthy
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-database
KONG_PG_PASSWORD: kongpass
kong:
image: kong:3.7
depends_on:
kong-migrations:
condition: service_completed_successfully
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-database
KONG_PG_PASSWORD: kongpass
KONG_PROXY_ACCESS_LOG: /dev/stdout
KONG_ADMIN_ACCESS_LOG: /dev/stdout
KONG_PROXY_ERROR_LOG: /dev/stderr
KONG_ADMIN_ERROR_LOG: /dev/stderr
ports:
- "8000:8000"
- "8443:8443"
Отдельный контейнер kong-migrations с kong migrations bootstrap — правильный паттерн: он должен завершиться (service_completed_successfully) до старта основного Kong. Запускать bootstrap вручную каждый раз при деплое не нужно — для обновления версии используется kong migrations up, а не bootstrap повторно (он упадёт с ошибкой, что схема уже существует).
Вторая типичная ошибка после обновления минорной версии Kong — забытый kong migrations finish. Начиная с Kong 2.x миграции идут в два шага: up применяет новую схему без разрыва совместимости со старой версией (для zero-downtime апгрейда на кластере), finish завершает переход. Если после up не выполнить finish, часть новых полей плагинов будет недоступна, а в логах — молчаливые предупреждения, которые легко пропустить.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать сервер502/503/504: сервис недоступен, а где именно — непонятно
Kong возвращает 502 Bad Gateway почти так же, как обычный Nginx в роли реверс-прокси — суть проблемы та же: Kong достучался до апстрима, но получил невалидный ответ или обрыв соединения. Разница в том, что диагностировать это нужно через Admin API, а не через правку конфига руками.
Порядок диагностики:
# Проверить, что Service и Route вообще существуют и указывают, куда нужно
curl -s http://localhost:8001/services/my-service | jq
curl -s http://localhost:8001/services/my-service/routes | jq
# Проверить health-статус апстрима, если используется Upstream с балансировкой
curl -s http://localhost:8001/upstreams/my-upstream/health | jq
Частые причины и решения:
| Симптом | Причина | Решение |
|---|---|---|
| 502 сразу на всех запросах | В Service.url опечатка в хосте/порту или сервис не в той docker-сети | Проверить docker network inspect, что Kong и бэкенд в одной сети |
| 502 периодически | Апстрим не успевает отвечать в connect_timeout (по умолчанию 60000 мс) | Снизить или увеличить таймаут точечно под сервис: PATCH /services/my-service {"connect_timeout":5000} |
| 503 Service Unavailable | Все таргеты Upstream помечены UNHEALTHY активным health-check | Проверить /upstreams/{id}/health, вручную вернуть таргет: POST /upstreams/{id}/targets/{target}/healthy |
| 504 Gateway Timeout | Бэкенд отвечает дольше read_timeout | Поднять read_timeout/write_timeout в Service, но сначала выяснить, почему бэкенд медленный |
Отдельно стоит проверить, что DNS внутри контейнера резолвит имя сервиса — если Kong запущен не в той же docker-сети, что бэкенд, он банально не видит хост по имени, и в логах будет name resolution failed. Это не ошибка Kong, а типичная грабля docker-сетей: все сервисы, между которыми должен ходить трафик, обязаны быть в одной user-defined сети, а не полагаться на связку через localhost.
Admin API открыт наружу — критичная дыра, а не мелочь
Это ошибка конфигурации, а не «частая проблема» в смысле сбоя — но встречается она постоянно, потому что по умолчанию Kong слушает Admin API на 0.0.0.0:8001 без какой-либо аутентификации. Любой, кто достучится до порта 8001, может создать Service, Route или плагин — то есть полностью перехватить маршрутизацию трафика.
Проверка с внешней машины:
curl -s http://<IP-сервера>:8001/status
Если ответ пришёл — порт открыт наружу, это нужно закрывать немедленно. Три рабочих варианта:
- Слушать только localhost — самый простой вариант для одиночного сервера:
KONG_ADMIN_LISTEN=127.0.0.1:8001
Управлять через SSH-туннель: ssh -L 8001:127.0.0.1:8001 user@server.
- Закрыть портом на файрволе, если Admin API нужен с других машин в приватной сети:
ufw deny 8001/tcp
ufw allow from 10.0.0.0/24 to any port 8001
- Вынести Admin API за отдельный reverse-proxy с базовой аутентификацией — если нужен доступ извне (например, для CI/CD), но открывать порт напрямую нельзя.
Отдельно проверьте, что в docker-compose.yml порт 8001 не прокинут наружу случайно через ports: ["8001:8001"] — такая строка, скопированная из туториала «для локальной разработки», в проде превращается в открытую дверь в систему маршрутизации.
Плагины: где чаще всего конфликтуют аутентификация и лимиты
Плагины Kong выполняются в фиксированном порядке по фазам (access, header_filter, body_filter, log), и внутри фазы — по приоритету, зашитому в самом плагине. Отсюда две типичные ошибки.
Rate-limiting считает лимит до аутентификации. Если повесить rate-limiting и key-auth на один Route без разбора приоритетов, может получиться, что лимит применяется по IP до того, как Consumer определён по ключу — и тогда лимит общий на всех клиентов за одним NAT, а не персональный. Правильно — вешать rate-limiting на уровне Consumer (consumer_id в конфиге), а не глобально на Route, если нужен лимит на клиента:
curl -X POST http://localhost:8001/consumers/acme-corp/plugins \
--data "name=rate-limiting" \
--data "config.minute=100" \
--data "config.policy=redis" \
--data "config.redis.host=redis" \
--data "config.redis.port=6379"
Политика policy=local считает лимит в памяти конкретной ноды Kong — на кластере из нескольких инстансов это даёт разный лимит на каждой ноде вместо общего. Для честного общего лимита нужен policy=redis с внешним Redis, это частая причина жалоб «лимит не работает, хотя я всё настроил».
JWT-плагин падает с 401 Unauthorized без объяснений. Причина обычно одна из трёх: секрет/публичный ключ Consumer не совпадает с тем, которым подписан токен; поле iss (issuer) в токене не совпадает с key Consumer в Kong; либо часы серверов разъехались, и токен считается ещё не валидным (nbf) или уже истёкшим. Проверить последнее просто:
date -u && curl -s http://localhost:8001/status | jq
Если время на сервере с Kong и на сервере, выпускающем токены, расходится больше чем на пару минут — чините NTP (chronyd/systemd-timesyncd), а не плагин.
Ещё одна практическая деталь: плагины, включённые глобально (без привязки к Service/Route/Consumer), применяются вообще ко всем запросам, включая Admin API, если тот проксируется через тот же Kong. Это иногда приводит к тому, что администратор сам блокирует себе доступ собственным rate-limiting плагином.
Логирование, health-check и производительность
По умолчанию логи идут в файлы внутри контейнера, что на сервере без внешнего сборщика логов означает — при падении контейнера всё потеряно. Для прод-сервера логи стоит сразу направить в stdout/stderr (как в конфиге выше) и собирать через docker logs или внешний агент, либо подключить плагин file-log/http-log/syslog под конкретный Service.
Полезные точки для мониторинга состояния:
# Общий статус ноды, включая память и соединения к базе
curl -s http://localhost:8001/status | jq
# Список активных плагинов на конкретном сервисе — быстро понять, что реально применяется
curl -s http://localhost:8001/services/my-service/plugins | jq '.data[].name'
По производительности: Kong в DB-режиме держит кэш конфигурации в памяти воркера и обновляет его через polling (db_update_frequency, по умолчанию каждые 5 секунд) — изменение через Admin API применяется не мгновенно на всех нодах кластера, а с задержкой в несколько секунд. Если тестируете конфиг и не видите изменений сразу — это не баг, а нормальная задержка кэша.
Ресурсоёмкость Kong заметно выше голого Nginx или Traefik из-за прослойки Lua и обращений к базе — на сервере с 1-2 ГБ RAM под нагрузкой в несколько сотен RPS с активными плагинами память может подойти к пределу, и тогда лучше брать план с запасом.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Kong обязательно нужна PostgreSQL?
Нет, DB-less режим с одним YAML-файлом полностью рабочий и проще в эксплуатации на одном сервере. PostgreSQL нужен, если конфигурация должна централизованно обновляться на кластере из нескольких нод через Admin API «на лету», без ручной синхронизации файлов.
Почему после kong reload пропадают изменения, сделанные через Admin API в DB-less режиме?
Потому что в DB-less режиме источник правды — файл kong.yml, а не память процесса. Изменения через /config эндпоинт живут до перезапуска/релоада, если не записаны обратно в файл. Для постоянных изменений нужно менять сам YAML и заливать через POST /config.
Как узнать, какая версия Kong стоит и не пора ли обновляться?
curl -s http://localhost:8001/ | jq .version. Версии до Kong 3.x получают security-патчи ограниченное время, для нового прод-развёртывания лучше сразу брать актуальную ветку 3.x.
Можно ли использовать Kong как обычный SSL-терминатор без плагинов?
Формально да, но это оверинжиниринг — для чистой терминации TLS и проксирования достаточно Nginx или Traefik с автоматическим Let's Encrypt. Kong оправдан именно там, где нужны плагины: единая аутентификация, лимиты, трансформация запросов на уровне шлюза для десятков сервисов.
Что делать, если миграция базы упала на середине и Kong не стартует ни в каком состоянии?
Проверить статус миграций kong migrations status, при необходимости откатить проблемную миграцию (kong migrations reset — только на некритичной базе, это удаляет всю схему) и прогнать bootstrap заново на чистой БД, восстановив конфигурацию из бэкапа или декларативного файла.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →