MAATRIX / Блог / Kong Gateway в Docker Compose: готовый файл

Kong Gateway в Docker Compose: готовый файл

MAATRIX

Когда микросервисов становится больше двух-трёх, каждый начинает сам разбираться с аутентификацией, лимитами запросов и логированием — код дублируется, а поведение расходится от сервиса к сервису. Kong Gateway решает это на уровне одной точки входа: все запросы идут через него, а плагины дают единую аутентификацию, rate limiting и логи для всех сервисов сразу, без правок в самих микросервисах. Ниже — рабочий docker-compose.yml, DB-less альтернатива и настройка ключевых плагинов через Admin API.

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

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

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

Зачем Kong, если уже есть Nginx или Traefik

Nginx и Traefik как reverse proxy для Docker отлично маршрутизируют трафик по доменам и путям, но у них нет встроенной модели «плагинов на сервис»: аутентификацию и лимиты туда приходится добавлять через lua-модули или отдельные сервисы. Kong построен именно как API-шлюз — поверх того же Nginx/OpenResty, но с декларативной моделью Services → Routes → Plugins и Admin API, через который всё настраивается без правки конфигов и перезапуска.

Практическая разница:

ЗадачаNginxTraefikKong
Маршрутизация по домену/путивручную в конфигеметки на контейнереAdmin API / decK
Аутентификация по ключу/JWTпишется вручнуюнет из коробкиплагин key-auth/jwt
Rate limiting per-consumerсложнобазовый middlewareплагин с учётом клиента
Централизованные логи запросовaccess_logбазовыйплагины file-log, http-log, tcp-log
Порог входанизкийнизкийвыше — своя БД/DB-less конфиг

Если у вас три сервиса и нужна только маршрутизация — Traefik или Nginx проще и дешевле в поддержке. Kong оправдан, когда за шлюзом реально несколько команд/сервисов и нужна единая политика доступа и лимитов, которую вы не хотите размазывать по коду.

Архитектура и готовый docker-compose.yml (Postgres-режим)

Kong умеет работать в двух режимах, и выбор стоит сделать до первого docker compose up — переключаться между ними на живой системе неудобно. DB-режим (Postgres) хранит сервисы, маршруты, плагины и потребителей в PostgreSQL, конфигурация меняется через Admin API или Kong Manager и применяется сразу, без перезапуска — подходит, когда конфиг правят часто и вручную. DB-less режим держит всю конфигурацию в одном YAML-файле, который Kong читает при старте: никакой базы, конфиг удобно версионировать в git и катить через CI, но изменения применяются через kong reload, а не на лету через API. Для инфраструктуры как кода обычно удобнее DB-less, для многокомандной среды — Postgres. Ниже — оба варианта, начиная с Postgres.

Файл поднимает Postgres, применяет миграции отдельным контейнером и только потом стартует сам Kong — так исключается гонка «Kong запустился раньше, чем накатилась схема БД».

# docker-compose.yml
services:
  kong-database:
    image: postgres:16-alpine
    container_name: kong-database
    restart: unless-stopped
    environment:
      POSTGRES_USER: kong
      POSTGRES_DB: kong
      POSTGRES_PASSWORD: ${KONG_PG_PASSWORD}
    volumes:
      - kong_pg_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "kong"]
      interval: 5s
      timeout: 5s
      retries: 10
    networks:
      - kong-net

  kong-migrations:
    image: kong:3.8
    container_name: kong-migrations
    command: kong migrations bootstrap
    restart: on-failure
    depends_on:
      kong-database:
        condition: service_healthy
    environment:
      KONG_DATABASE: postgres
      KONG_PG_HOST: kong-database
      KONG_PG_USER: kong
      KONG_PG_PASSWORD: ${KONG_PG_PASSWORD}
    networks:
      - kong-net

  kong:
    image: kong:3.8
    container_name: kong
    restart: unless-stopped
    depends_on:
      kong-migrations:
        condition: service_completed_successfully
    environment:
      KONG_DATABASE: postgres
      KONG_PG_HOST: kong-database
      KONG_PG_USER: kong
      KONG_PG_PASSWORD: ${KONG_PG_PASSWORD}
      KONG_PROXY_ACCESS_LOG: /dev/stdout
      KONG_ADMIN_ACCESS_LOG: /dev/stdout
      KONG_PROXY_ERROR_LOG: /dev/stderr
      KONG_ADMIN_ERROR_LOG: /dev/stderr
      KONG_ADMIN_LISTEN: 0.0.0.0:8001
      KONG_PROXY_LISTEN: 0.0.0.0:8000, 0.0.0.0:8443 ssl
    ports:
      - "8000:8000"     # прокси, HTTP
      - "8443:8443"     # прокси, HTTPS
      - "127.0.0.1:8001:8001"  # Admin API — только на localhost!
    healthcheck:
      test: ["CMD", "kong", "health"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - kong-net

volumes:
  kong_pg_data:

networks:
  kong-net:
    driver: bridge

Пароль от Postgres кладите в .env рядом с файлом, а не в сам compose-файл — подробнее о подходе к секретам в статье про управление паролями через Docker secrets. Порт 8001 (Admin API) сознательно пробрасывается только на 127.0.0.1: через него можно создавать и удалять любые сервисы и плагины без аутентификации из коробки.

Если бэкенд-сервисы уже описаны в другом docker-compose.yml, подключите их к сети kong-net (external: true) или объедините проекты в один файл — иначе Kong не достучится до них по DNS-имени контейнера. О типах сетей в Docker — в статье про bridge, host и overlay.

Поднимаем:

cp .env.example .env   # заполните KONG_PG_PASSWORD
docker compose up -d
docker compose logs -f kong
curl -s http://127.0.0.1:8001/status | jq

Если status отвечает JSON с database.reachable: true — Kong готов принимать конфигурацию.

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

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

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

DB-less альтернатива: один YAML вместо базы

Меньше сервисов в compose, конфиг версионируется в git.

# docker-compose.dbless.yml
services:
  kong:
    image: kong:3.8
    container_name: kong
    restart: unless-stopped
    environment:
      KONG_DATABASE: "off"
      KONG_DECLARATIVE_CONFIG: /kong/declarative/kong.yml
      KONG_PROXY_ACCESS_LOG: /dev/stdout
      KONG_ADMIN_ACCESS_LOG: /dev/stdout
      KONG_PROXY_ERROR_LOG: /dev/stderr
      KONG_ADMIN_ERROR_LOG: /dev/stderr
      KONG_ADMIN_LISTEN: 0.0.0.0:8001
      KONG_PROXY_LISTEN: 0.0.0.0:8000, 0.0.0.0:8443 ssl
    volumes:
      - ./kong.yml:/kong/declarative/kong.yml:ro
    ports:
      - "8000:8000"
      - "8443:8443"
      - "127.0.0.1:8001:8001"
    healthcheck:
      test: ["CMD", "kong", "health"]
      interval: 10s
      timeout: 5s
      retries: 5

А сам kong.yml описывает сервисы, маршруты и плагины декларативно:

# kong.yml
_format_version: "3.0"

services:
  - name: orders-service
    url: http://orders-api:3000
    routes:
      - name: orders-route
        paths:
          - /api/orders
    plugins:
      - name: rate-limiting
        config:
          minute: 60
          policy: local

consumers:
  - username: mobile-app
    keyauth_credentials:
      - key: ${MOBILE_APP_KEY}

После правки файла конфигурация применяется командой docker compose exec kong kong reload или перезапуском контейнера. Admin API здесь доступен только для чтения (GET) — это нормально, а не баг: изменения вносятся через файл.

Сервисы, маршруты и аутентификация через Admin API

В режиме с базой конфигурация задаётся вызовами к Admin API — тем же способом, каким её потом будет менять ваш CI или скрипт. Регистрируем бэкенд-сервис и маршрут к нему:

# сервис: куда Kong будет проксировать запросы
curl -i -X POST http://127.0.0.1:8001/services \
  --data name=orders-service \
  --data url=http://orders-api:3000

# маршрут: по какому пути этот сервис доступен снаружи
curl -i -X POST http://127.0.0.1:8001/services/orders-service/routes \
  --data name=orders-route \
  --data paths[]=/api/orders

Проверка:

curl -i http://127.0.0.1:8000/api/orders

Запрос на 8000 порт уходит через Kong к http://orders-api:3000 — при условии, что контейнер orders-api в той же docker-сети, что и Kong. Для команд, которые не хотят гонять curl вручную, есть decK — CLI-утилита, синхронизирующая шлюз с YAML-файлом (deck sync -s kong.yml).

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

# включаем key-auth на сервисе
curl -i -X POST http://127.0.0.1:8001/services/orders-service/plugins \
  --data name=key-auth

# заводим потребителя и выдаём ему ключ
curl -i -X POST http://127.0.0.1:8001/consumers \
  --data username=mobile-app

curl -i -X POST http://127.0.0.1:8001/consumers/mobile-app/key-auth \
  --data key=your-secret-key-here

Теперь запрос без ключа получит 401 Unauthorized, а с ним — пройдёт:

curl -i http://127.0.0.1:8000/api/orders \
  -H "apikey: your-secret-key-here"

Для сценариев, где токен выдаёт ваш собственный auth-сервис (а не Kong), уместнее плагин jwt: Kong не выдаёт токены сам, а проверяет подпись присланного JWT по секрету или публичному ключу, привязанному к потребителю:

curl -i -X POST http://127.0.0.1:8001/services/orders-service/plugins \
  --data name=jwt

curl -i -X POST http://127.0.0.1:8001/consumers/mobile-app/jwt \
  --data algorithm=HS256 \
  --data secret=your-jwt-secret

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

Rate limiting и защита от перегрузки

Плагин rate-limiting ограничивает число запросов на сервис, маршрут или конкретного потребителя — удобно разделить лимиты для бесплатного и платного тарифа API, не трогая код сервисов.

# общий лимит на сервис: 100 запросов в минуту, 2000 в час
curl -i -X POST http://127.0.0.1:8001/services/orders-service/plugins \
  --data name=rate-limiting \
  --data config.minute=100 \
  --data config.hour=2000 \
  --data config.policy=local

policy=local считает лимит в памяти узла — просто, но при нескольких инстансах Kong за балансировщиком каждый узел считает отдельно, не общий лимит. Для честного лимита на кластере нужен policy=redis с общим Redis:

curl -i -X PATCH http://127.0.0.1:8001/plugins/{plugin-id} \
  --data config.policy=redis \
  --data config.redis.host=redis \
  --data config.redis.port=6379

Отдельно можно выдать конкретному потребителю более щедрый лимит — плагин применяется на уровне consumer, и Kong использует именно его вместо общего:

curl -i -X POST http://127.0.0.1:8001/consumers/mobile-app/plugins \
  --data name=rate-limiting \
  --data config.minute=500

Важно не путать rate limiting с защитой от DDoS: Kong ограничивает легитимных клиентов по идентификатору (ключ, JWT-subject, IP), но от объёмной атаки с тысяч случайных IP это не спасает — для такого нужен CDN/anti-DDoS перед шлюзом.

Логирование и мониторинг

Из коробки Kong пишет access/error логи в stdout/stderr контейнера (это уже настроено переменными KONG_PROXY_ACCESS_LOG в примере выше) — их можно смотреть через docker compose logs kong или собирать любым Docker-log-драйвером. Для структурированных логов запросов (метод, статус, латентность, потребитель) есть плагин file-log или http-log:

# каждый запрос — отдельной JSON-строкой в файл
curl -i -X POST http://127.0.0.1:8001/services/orders-service/plugins \
  --data name=file-log \
  --data config.path=/tmp/orders-access.log

# либо отправка логов на внешний коллектор
curl -i -X POST http://127.0.0.1:8001/services/orders-service/plugins \
  --data name=http-log \
  --data config.http_endpoint=http://log-collector:8080/logs

Если у вас уже есть стек логирования, http-log можно направить в Loki через промежуточный приёмник — как собрать саму связку, описано в статье про Grafana Loki в Docker Compose. Для метрик (RPS, latency, коды ответов) в Kong есть плагин prometheus, отдающий /metrics в формате, который Prometheus снимает без дополнительного экспортёра.

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

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

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

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

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

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

Kong обязательно требует PostgreSQL?

Нет, только в классическом режиме. DB-less (KONG_DATABASE: "off") работает с одним YAML-файлом без базы — подходит, если конфигурация меняется редко.

Admin API безопасно оставлять открытым в интернет?

Нет. У него нет аутентификации по умолчанию — любой, кто достучится, может удалить сервисы и плагины. Порт 8001 пробрасывайте только на 127.0.0.1, для удалённого доступа — SSH-туннель или VPN, либо RBAC (Kong Enterprise).

Чем Kong отличается от Kong Enterprise?

Open-source образ покрывает маршрутизацию, плагины аутентификации/лимитов/логирования и Admin API — этого хватает для большинства self-hosted сценариев. Enterprise добавляет Kong Manager с ролевой моделью доступа и часть продвинутых плагинов.

Можно ли поставить Kong перед уже работающим Nginx?

Да, но обычно не нужно: либо Kong сам терминирует TLS, либо перед ним внешний балансировщик, а лишний слой Nginx/Traefik становится избыточным. Двойное проксирование оправдано разве что при постепенной миграции маршрутов.

Как обновить Kong без даунтайма?

В Postgres-режиме — поднять контейнер с новым тегом, дождаться healthcheck, переключить балансировщик, погасить старый; миграции схемы идут отдельным kong-migrations job. В DB-less проще: новый контейнер стартует с тем же kong.yml, старый гасится сразу после healthcheck нового.

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

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

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