Сценарий: микросервисный gateway по образцу ngx-trace-gateway

Рейтинг: 52.9% · 8 голосов
Самый подробный курс по Nginx - от первого конфига до production API-gateway. Архитектура и event loop, контексты и location, виртуальные хосты, статика, reverse proxy и upstream-балансировка, кэширование, HTTPS и TLS (post-quantum), HTTP/2 и HTTP/3 QUIC, безопасность и rate limiting, realip, JSON-логи и сквозная трассировка, метрики, тюнинг и траблшутинг. Плюс OpenResty (Lua, фазы, JWT, динамика) и Angie (нативные Prometheus, ACME, dynamic upstream). На основе эталонного gateway и лучших практик. Для новичков и SRE/DevOps. Актуально на 2026 (nginx 1.30/1.31, Angie, ingress-nginx EOL -> Gateway API).
Ответить
Аватара пользователя
Igor_NgINX
Сообщения: 44
Зарегистрирован: 11 май 2026, 05:31

Сценарий: микросервисный gateway по образцу ngx-trace-gateway

Сообщение Igor_NgINX »

Оглавление курса (44)
  1. Что такое Nginx и почему он захватил веб
  2. Установка Nginx, Angie и OpenResty: пакеты, версии, Docker
  3. Первый конфиг с нуля: минимальный сервер за 5 минут
  4. Архитектура процессов: master, worker и event loop
  5. Анатомия конфигурации: контексты, наследование и модульность
  6. Виртуальные хосты: server и server_name
  7. Директива location: матчинг и приоритеты по шагам
  8. Раздача статики: root, alias, try_files, sendfile
  9. Переменные, map и блок if
  10. rewrite, return и канонизация URL
  11. Работа с заголовками: add_header и proxy_set_header
  12. Модель доверия прокси-заголовков: realip и защита от спуфинга
  13. Сжатие: gzip, brotli и zstd
  14. Reverse proxy: proxy_pass и проброс запроса
  15. upstream и балансировка нагрузки
  16. Таймауты, повторы и устойчивость прокси
  17. FastCGI и PHP: nginx + PHP-FPM (и uwsgi/scgi)
  18. WebSocket, gRPC и стриминг через Nginx
  19. Кэширование ответов: proxy_cache и микрокэш
  20. HTTPS и TLS: сертификаты, протоколы, шифры (2026)
  21. Let's Encrypt и ACME: certbot, acme.sh, нативный ACME Angie
  22. HTTP/2 и HTTP/3 (QUIC) в 2026
  23. Безопасность: заголовки, скрытие версии, ограничения
  24. Rate limiting и защита от перегрузки
  25. Контроль доступа: allow/deny, auth_basic, auth_request и mTLS
  26. Логирование: access_log, форматы и структурированный JSON
  27. Сквозная трассировка запросов и OpenTelemetry
  28. Метрики и мониторинг Nginx
  29. Производительность и тюнинг под нагрузку
  30. Траблшутинг: 502, 504, 403, 404 и debug-лог
  31. OpenResty: Nginx как платформа на LuaJIT
  32. Фазы обработки запроса и Lua-хуки
  33. Lua API: ngx.*, shared dict, cosocket и lua-resty-core
  34. Практика OpenResty: авторизация, JWT, кэш, Redis
  35. Angie: современный форк Nginx 2026
  36. Динамическая конфигурация и service discovery
  37. Nginx как API-gateway и Kubernetes Gateway API
  38. Динамические модули, njs и WAF
  39. Stream-модуль: проксирование TCP и UDP
  40. Деплой, перезагрузка и конфиг как код
  41. Сценарий: статика, SPA и кэширование за CDN
  42. Сценарий: микросервисный gateway по образцу ngx-trace-gateway (вы здесь)
  43. Капстоун: собираем production-gateway с нуля
  44. Чек-лист безопасности, CVE 2026 и аудит конфигурации
Зачем вообще нужен отдельный nginx gateway перед микросервисами

Представь типичный продакшен. У тебя десяток сервисов: auth, billing, catalog, search, admin-ui. Каждый слушает свой порт, у каждого свои таймауты, свои заголовки, свой формат логов. Клиент стучится напрямую, CORS течёт, rate-limit размазан по приложениям, а когда в 3 часа ночи падает один запрос - ты не можешь проследить его путь от мобильного клиента до базы, потому что у каждого хопа свой непонятный id. Знакомо?

Решение - единая точка входа. nginx gateway (он же reverse proxy, он же edge) забирает на себя TLS, маршрутизацию, кэш, лимиты, безопасность и трассировку. Приложения за ним становятся тупыми и счастливыми: они принимают чистый HTTP, доверяют заголовкам от шлюза и не думают про внешний мир. Это и есть классический паттерн nginx микросервисы: шлюз снаружи держит политику, сервисы внутри держат бизнес-логику.

Беда в том, что наивный шлюз быстро превращается в один файл на 2000 строк, который никто не решается трогать. В этом уроке мы разбираем готовый эталон - проект ngx-trace-gateway. Это рабочий nginx production конфиг на OpenResty, собранный по уму: модульный, с насквозь прошитой трассировкой, JSON-логами на 50+ полей, метриками, семью зонами rate-limit и слоем безопасности. Цель урока - не просто полюбоваться, а понять механику каждого решения настолько, чтобы развернуть такой шлюз под свои бэкенды и осознанно его править.

Изображение

Модульная структура: nginx модульный конфиг вместо монолита

Первое, что отличает эталон от поделки - это структура. Точка входа

Код: Выделить всё

nginx.conf
почти пустая, в ней только параметры мастер-процесса и два include:

Код: Выделить всё

worker_processes  auto;
error_log         /var/log/nginx/error.log  warn;
pid               /var/run/nginx.pid;
pcre_jit          on;

include /etc/nginx/conf.d/events.conf;
include /etc/nginx/conf.d/http.conf;
Разберём по строчкам. worker_processes auto - nginx сам поднимет столько воркеров, сколько у тебя ядер CPU, это правильный дефолт почти всегда. error_log ... warn - не сыпем в лог info-мусором, ловим только важное. pcre_jit on - включаем JIT-компиляцию регулярок (а их в шлюзе много - в map, в location, в фильтрах безопасности), без этого regex медленнее в разы. Дальше - events.conf (модель соединений) и http.conf (вся остальная вселенная).

events.conf тоже минималистичен и осмыслен:

Код: Выделить всё

events {
    worker_connections  10240;
    multi_accept        on;
    use                 epoll;
}
worker_connections 10240 - сколько одновременных соединений держит один воркер; это потолок зависит от системного

Код: Выделить всё

ulimit -n
, так что одно без другого не работает. multi_accept on - воркер за один цикл события забирает не одно, а сразу пачку новых соединений, что полезно при всплесках. use epoll - эффективный event-loop под Linux, основа всей событийной модели nginx.

А вот http.conf - это диспетчер. Он задаёт базу (mime.types, charset, карту WebSocket-апгрейда) и подключает ровно 14 файлов политик из globals/ строго по порядку, а в конце - виртуальные хосты:

Код: Выделить всё

http {
    include       mime.types;
    default_type  application/octet-stream;

    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }
    charset utf-8;
    recursive_error_pages on;

    include /etc/nginx/conf.d/globals/global_hop_name.conf;
    include /etc/nginx/conf.d/globals/global_tcp.conf;
    include /etc/nginx/conf.d/globals/global_proxy.conf;
    include /etc/nginx/conf.d/globals/global_fastcgi.conf;
    include /etc/nginx/conf.d/globals/global_uwsgi.conf;
    include /etc/nginx/conf.d/globals/global_scgi.conf;
    include /etc/nginx/conf.d/globals/global_rate_limit.conf;
    include /etc/nginx/conf.d/globals/global_trace.conf;
    include /etc/nginx/conf.d/globals/global_gzip.conf;
    include /etc/nginx/conf.d/globals/global_logging.conf;
    include /etc/nginx/conf.d/globals/global_cache.conf;
    include /etc/nginx/conf.d/globals/global_ssl.conf;
    include /etc/nginx/conf.d/globals/global_security.conf;
    include /etc/nginx/conf.d/globals/global_error_pages.conf;
    include /etc/nginx/sites-enabled/*.conf;
}
Почему именно так и почему порядок важен. include в nginx - это буквально текстовая подстановка файла в это место конфига. То есть всё, что ты видишь, можно было бы написать одной портянкой. Но разбивка по политикам даёт три вещи. Первое - читаемость: тебе нужно поменять лимиты - открываешь global_rate_limit.conf, а не ищешь limit_req_zone в монолите. Второе - переиспользование: один и тот же proxy/cache/ssl применяется ко всем vhost-ам автоматически, не копипастишь. Третье - порядок важен для зависимостей: map-переменные из global_hop_name и global_trace должны быть объявлены до того, как их используют global_proxy (в proxy_set_header) и global_logging (в log_format). Поэтому hop_name идёт первым, trace - до logging, а sites-enabled - в самом конце, когда все политики уже в силе.

Маленькая, но важная деталь: vhost-ы лежат в sites-available, а http.conf подключает sites-enabled. Включение хоста - это симлинк, выключение - удаление симлинка. Конфиг хоста не трогаешь, состояние храним отдельно. Это nginx эталон обращения с виртуальными хостами, унаследованный из Debian-практики.

Сквозная трассировка: главная фишка nginx reverse proxy конфига

Это сердце проекта и причина, по которой он называется trace-gateway. Задача: каждый запрос должен иметь единый сквозной id, который живёт через всю цепочку сервисов, плюс цепочку хопов, по которой видно, через какие узлы он прошёл. И всё это - без единой строчки Lua, чистыми map.

Начнём с имени узла. global_hop_name превращает технический hostname в логическую роль:

Код: Выделить всё

map $hostname $hop_physical {
    default $hostname;
}

map $hostname $hop_name {
    api-01.prod.local        api-nginx-01;
    edge-01.prod.local       edge-01;
    internal-01.prod         internal-nginx-01;
    default                  $hop_physical;
}
Зачем? hostname в проде вида prod-web-nginx-01-dpc01.local в логах нечитаем. А $hop_name = edge-01 сразу говорит, какой слой инфраструктуры написал лог. Если узла нет в карте - падаем на физический hostname, ничего не теряем.

Теперь сама трассировка - global_trace. Это цепочка из map, где выход одного становится входом другого:

Код: Выделить всё

map $http_x_request_id $trace_request_id {
    ""      $request_id;
    default $http_x_request_id;
}

map $request_id $local_request_id {
    default $request_id;
}

map "$hop_name:$local_request_id" $current_hop {
    default "$hop_name:$local_request_id";
}

map $http_x_request_chain $incoming_chain {
    ""      "";
    default $http_x_request_chain;
}

map $incoming_chain $request_hops_chain {
    ""      $current_hop;
    default "$incoming_chain, $current_hop";
}
Прочитаем логику как программу. $trace_request_id - сквозной id: если клиент или предыдущий прокси прислал X-Request-Id, берём его; если заголовок пуст - генерируем встроенный $request_id (это уникальная 32-символьная hex-строка, которую nginx сам выдаёт каждому запросу). Так первый хоп в цепочке рождает id, а все последующие его наследуют. $local_request_id - всегда свой $request_id, уникальный именно для этого инстанса: он отвечает на вопрос не "что за запрос вообще", а "что за хоп конкретно тут". $current_hop склеивает роль и локальный id: edge-01:ab12cd. И финал - $request_hops_chain: если входящей цепочки нет, мы первый узел и кладём себя; если есть - дописываем себя через запятую. На выходе в логах рисуется маршрут вида edge-01:ab12, api-nginx-01:ef34, internal-nginx-01:gh56.

Куда это всё уезжает? В global_proxy все три идентификатора одновременно и отдаются клиенту через add_header, и пробрасываются в бэкенд через proxy_set_header:

Код: Выделить всё

add_header       X-Request-ID   $trace_request_id always;
proxy_set_header X-Request-ID   $trace_request_id;

add_header       X-Request-Chain $request_hops_chain always;
proxy_set_header X-Request-Chain $request_hops_chain;

proxy_set_header X-Prev-Request-ID $local_request_id;
add_header       X-HOP-Name        $hop_name always;
proxy_set_header X-HOP-Name        $hop_name;
Обрати внимание на хитрость с X-Prev-Request-ID: вниз по цепочке мы отправляем свой $local_request_id как "prev" для следующего узла - то есть следующий nginx увидит, кто стоял до него. Так маршрут восстанавливается не только вперёд по chain, но и назад по парам prev/local в логах соседних узлов. Заодно проект тащит мобильные идентификаторы X-Mobile-Launch-ID и X-Mobile-Request-ID - их nginx не генерирует, а только читает из заголовков, логирует и прозрачно прокидывает. Идея универсальна: шлюз - честный курьер контекста, он ничего не теряет по дороге.

Структурированные JSON-логи и метрики

Текстовый access.log в микросервисах бесполезен - его не распарсить нормально. Эталон логирует в JSON через escape=json, и это меняет всё: лог сразу льётся в ELK/Loki/ClickHouse как структурированное событие. Severity не выдумывается, а выводится из статуса картой:

Код: Выделить всё

map $status $log_level {
    ~^2   "info";
    ~^3   "info";
    ~^4   "warn";
    ~^5   "error";
    default "info";
}

log_format structured_log escape=json
    '{'
        '"timestamp":"$time_iso8601",'
        '"timestamp_msec":"$msec",'
        '"severity":"$log_level",'
        '"trace":{'
            '"hop_name":"$hop_name",'
            '"trace_request_id":"$trace_request_id",'
            '"hops_chain":"$request_hops_chain"'
        '},'
        '"http":{"method":"$request_method","uri":"$uri","status":"$status"},'
        '"timings":{'
            '"request_time":"$request_time",'
            '"upstream_response_time":"$upstream_response_time"'
        '}'
    '}';
В реальном файле полей за полсотни: ISO8601-метка плюс $msec для точной корреляции, client ip и forwarded_for, TLS-протокол/cipher/SNI/session_reused, сетевые $tcpinfo, апстрим-адрес/статус/кэш-статус и тайминги. Почему критичны два тайминга. $request_time - сколько nginx обрабатывал запрос целиком, включая чтение тела и отдачу клиенту. $upstream_response_time - сколько именно ждали бэкенд. Если первое большое, а второе маленькое - тормозит клиентский канал, а не сервис. Этого одного разделения хватает, чтобы не гадать, кто виноват в латентности.

Метрики живут отдельно. global_metrics поднимает служебный сервер на внутреннем порту 8080, отдаёт stub_status и закрыт ACL-ом:

Код: Выделить всё

server {
    listen 0.0.0.0:8080;
    access_log off;
    location = /nginx_status {
        stub_status;
        allow 127.0.0.1;
        allow 10.0.0.0/8;
        allow 172.16.0.0/12;
        allow 192.168.0.0/16;
        deny  all;
    }
}
Порт 8080 не пробрасывается наружу контейнера - его снимает только prometheus-exporter из docker-сети. stub_status даёт базу: активные соединения, accepts/handled/requests, reading/writing/waiting. Этого достаточно для пульса, а детальную аналитику тянут из JSON-логов. Тут стоит честно сказать про 2026 год: stub_status - это минимум миниморум. Если хочешь богатые метрики из коробки, без сторонних экспортеров - смотри в сторону Angie: у него нативный REST API /status с детальным JSON по http/upstreams/zones/caches и встроенный вывод в формате Prometheus, всё бесплатно. На ванильном nginx этот же эталон останется на stub_status плюс лог-парсинг, и это рабочая, проверенная связка.

Переиспользуемые политики: proxy, cache, security, 7 зон rate-limit

Теперь то, ради чего вся модульность - набор политик, которые пишутся один раз и работают для всех хостов.

global_proxy - дефолты проксирования: таймауты (connect 5s, read/send 10s), буферизация (proxy_buffers 16 16k, busy 32k), proxy_next_upstream error timeout с tries 3 - то есть на сетевой ошибке или таймауте шлюз перепробует до трёх апстримов. Тут же proxy_http_version 1.1. Маленькая ремарка по 2026: начиная с nginx 1.29.7 (вошло в stable 1.30) соединение к бэкенду по умолчанию уже HTTP/1.1 с keepalive, раньше был HTTP/1.0 с close. Но явная директива 1.1 в эталоне - правильная страховка совместимости со старыми версиями и обязательна для проброса Upgrade (WebSocket).

global_cache + кэш-политика в proxy - вот где эталон показывает зрелость. Зона объявляется отдельно:

Код: Выделить всё

proxy_cache_path /var/cache/nginx/gate levels=1:2 keys_zone=appcache:128m
                 inactive=60m max_size=10g use_temp_path=off;
А в proxy включается умное поведение:

Код: Выделить всё

proxy_cache_key "$scheme$request_method$host$request_uri$http_authorization";
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
proxy_cache_background_update on;
proxy_cache_revalidate on;
proxy_cache_valid 200 1h;
add_header X-Proxy-Cache $upstream_cache_status always;
Три директивы тут - признак продакшена. proxy_cache_lock on спасает от thundering herd: когда популярный объект протух, к бэкенду уйдёт ровно один запрос-лидер обновить кэш, остальные подождут до lock_timeout, а не ломанутся толпой. proxy_cache_background_update on - пока объект освежается, пользователь получает чуть устаревшую, но мгновенную версию, без штрафа по латентности. proxy_cache_revalidate on - при обновлении nginx шлёт If-None-Match/If-Modified-Since, и если бэкенд отвечает 304, мы экономим трафик и просто продлеваем кэш. А заголовок X-Proxy-Cache $upstream_cache_status (HIT/MISS/EXPIRED/BYPASS) - твой главный отладчик кэша прямо в браузере. Важная тонкость ключа: в него входит $http_authorization, чтобы не отдать чужой приватный ответ из кэша другому пользователю.

global_rate_limit - семь зон. Это не перебор, это разделение по характеру трафика:

Код: Выделить всё

limit_req_zone $binary_remote_addr zone=app_login:10m        rate=5r/s;
limit_req_zone $binary_remote_addr zone=security_sensitive:5m rate=1r/s;
limit_req_zone $binary_remote_addr zone=public_api_ip:20m     rate=60r/m;
limit_req_zone $api_consumer_id    zone=public_api_user:20m   rate=120r/m;
limit_req_zone $rl_api_key         zone=public_api_key:20m    rate=10r/s;
limit_req_zone $binary_remote_addr zone=admin_area:5m         rate=10r/s;
limit_req_zone $binary_remote_addr zone=internal_clients:10m  rate=200r/s;
Логика: логин и выдача SMS-кодов - жёстко (5r/s и 1r/s по IP), публичный API мягче (60r/m), но при этом считается и по IP, и по пользователю ($api_consumer_id из заголовка X-Consumer-Id), и по API-ключу - в одном location можно повесить несколько limit_req, применятся все. Ключ $binary_remote_addr (а не $remote_addr) экономит память: бинарное представление IP короче строкового. Подключаются зоны уже в location через

Код: Выделить всё

limit_req zone=app_login burst=5 nodelay;
- burst задаёт ведро всплеска, nodelay отдаёт его сразу, delay - сглаживает.

global_security + app_redirects_security - два слоя защиты. Глобальный шлёт безопасные заголовки на все ответы:

Код: Выделить всё

server_tokens off;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "no-referrer" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
Одна правка под 2026: в эталоне есть

Код: Выделить всё

add_header X-XSS-Protection "1; mode=block";
- этот заголовок сегодня считается устаревшим, OWASP советует его либо не слать, либо ставить "0" и полагаться на Content-Security-Policy. Так что в своём форке этот add_header я бы убрал, а вместо него настроил CSP. Остальное - server_tokens off (прячем версию), nosniff, SAMEORIGIN - актуально и полезно. HSTS в эталоне закомментирован не зря: включать его глобально, когда не все домены гарантированно на HTTPS - способ потерять доступ, поэтому HSTS вешают на конкретный 443-server осознанно.

Второй слой - app_redirects_security, подключается внутри vhost и делает канонизацию плюс грубую фильтрацию. Канонизация убирает дубли для SEO и чистит логи:

Код: Выделить всё

if ($request_uri ~ "^(.*/)(index\.php|index\.html)$") { return 301 $scheme://$host$1; }
if ($request_uri ~ "^[^?]*\?$")  { return 301 $scheme://$host$uri; }
if ($request_uri ~ "//+")        { return 301 $scheme://$host$uri; }
if ($needs_slash = 1)            { return 301 $scheme://$host$uri/; }
Переменная $needs_slash заранее посчитана картой в default.conf: она равна 1 для "чистых" путей без расширения и без слеша в конце, и 0 для файлов, корня и того, что уже со слешем - так добавление трейлинг-слеша не уходит в бесконечный редирект. Дальше идут простые фильтры по $query_string (попытки script, GLOBALS/_REQUEST, union select), защита от path traversal по ..|%2e%2e, запрет .php в /upload, бан dotfiles, .git/.svn, бэкапов .bak/.sql/.zip и hotlink-защита по valid_referers. Честная рамка: автор сам пишет в комментариях, что это не замена WAF (ModSecurity/NAXSI) - это дешёвый фильтр от самого тупого мусора, который снимает шум до того, как он доедет до приложения.

upstream и vhost-ы замыкают картину. Группа апстримов с весами, пассивным контролем здоровья и keepalive:

Код: Выделить всё

upstream backend_upstream {
    server 10.0.0.1:9000 weight=5 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:9000 max_fails=3 fail_timeout=30s;
    server 10.0.0.4:9000 backup;
    keepalive 64;
}
max_fails/fail_timeout - это пассивные проверки: 3 ошибки за окно и узел на 30 секунд выводится из ротации. Важный факт 2026: активных health-checks в open-source nginx нет, только это пассивное поведение - если нужны активные проверки и slow_start из коробки и бесплатно, это снова про Angie. keepalive 64 держит пул живых соединений к бэкендам, без него каждый запрос - новый TCP+TLS handshake, и именно ради keepalive нужен тот самый proxy_http_version 1.1 выше.

Типичные грабли при работе с таким шлюзом
  • Порядок include. Переставил global_logging выше global_trace - и в логи поедут пустые $trace_request_id, потому что map ещё не объявлен в момент использования. Зависимости map нужно держать сверху: hop_name, потом trace, потом всё остальное.
  • add_header не наследуется при переопределении. Если в location ты добавишь хоть один свой add_header, все глобальные add_header в этом location пропадут - таково правило наследования директивы. Лечится модулем headers-more (more_set_headers) либо дублированием. Это самая частая причина "куда делись security-заголовки на /api".
  • if внутри location - зло. Знаменитое "if is evil": в app_redirects_security if используется на уровне server для return 301/403 - это допустимый и безопасный кейс. Но if с try_files или proxy_pass внутри location ведёт себя непредсказуемо. Правило: if только для return/rewrite на уровне server.
  • $remote_addr без realip - это адрес ближайшего прокси, а не клиента. Если перед шлюзом стоит CDN или балансировщик, то и rate-limit по $binary_remote_addr, и логи, и geo посчитают IP прокси. Нужен модуль realip с set_real_ip_from, доверяющий ТОЛЬКО известным сетям CDN - иначе клиент подделает X-Forwarded-For и обойдёт лимиты.
  • proxy_buffering и стриминг. Буферизация включена глобально и это правильно для обычного API, но для SSE/стримов/долгих ответов её надо выключать в конкретном location (proxy_buffering off), иначе клиент будет ждать, пока nginx добуферит весь ответ.
  • Держи nginx и модули обновлёнными. В 2026 актуальны CVE в rewrite-модуле и HTTP/3 - чем меньше у тебя самописных rewrite/if-правил и чем свежее версия, тем спокойнее. Минимализм в правилах канонизации - это ещё и безопасность.
Мини-лаба: разверни и расширь шлюз под свой бэкенд

Повтори руками по шагам.
  • Шаг 1. Подними эталон в docker-compose на образе openresty/openresty. Healthcheck должен гонять

    Код: Выделить всё

    nginx -t
    - это проверка синтаксиса до старта.
  • Шаг 2. Проверь конфиг руками:

    Код: Выделить всё

    docker exec -it openresty openresty -t
    . Должно быть "syntax is ok" и "test is successful". Сломай нарочно - убери одну закрывающую скобку в http.conf и повтори, посмотри как nginx показывает файл и строку.
  • Шаг 3. Заведи свой апстрим. В upstream.conf добавь

    Код: Выделить всё

    upstream my_api { server 10.0.0.50:8000 max_fails=3 fail_timeout=30s; keepalive 32; }
    и в http_server-секции в location ^~ /myapi/ поставь

    Код: Выделить всё

    proxy_pass http://my_api;
    . Перечитай конфиг без даунтайма:

    Код: Выделить всё

    docker exec openresty openresty -s reload
    .
  • Шаг 4. Проверь трассировку.

    Код: Выделить всё

    curl -i http://localhost/myapi/ping
    - в ответе найди X-Request-ID, X-Request-Chain и X-HOP-Name. Теперь повтори с заголовком:

    Код: Выделить всё

    curl -i -H "X-Request-ID: my-trace-123" http://localhost/myapi/ping
    - в ответе X-Request-ID должен стать my-trace-123, а не сгенерированный. Это значит сквозной id работает.
  • Шаг 5. Проверь лимиты. Повесь на свой location

    Код: Выделить всё

    limit_req zone=public_api_ip burst=5 nodelay; limit_req_status 429;
    , reload, и пульни

    Код: Выделить всё

    for i in $(seq 1 20); do curl -s -o /dev/null -w "%{http_code}\n" http://localhost/myapi/ping; done
    - часть ответов должна стать 429.
  • Шаг 6. Проверь метрики и логи.

    Код: Выделить всё

    docker exec openresty curl -s http://127.0.0.1:8080/nginx_status
    - увидишь счётчики stub_status. Потом

    Код: Выделить всё

    docker exec openresty tail -n1 /var/log/nginx/access.log
    - убедись, что это валидный JSON с полями trace и timings.
Контрольные вопросы
  • Почему global_hop_name и global_trace обязаны подключаться в http.conf раньше, чем global_proxy и global_logging? Что попадёт в логи, если перепутать порядок?
  • Чем по смыслу отличаются три идентификатора - $trace_request_id, $local_request_id и $prev_request_id? Какой из них рождается на первом хопе, а какой меняется на каждом узле?
  • Что делают вместе proxy_cache_lock, proxy_cache_background_update и proxy_cache_revalidate и какую конкретную проблему решает каждая под нагрузкой?
  • Почему в эталоне семь отдельных зон rate-limit, а не одна общая? Приведи пример, когда $binary_remote_addr - плохой ключ, и чем его заменить.
Итог

ngx-trace-gateway - это не магия, а дисциплина. Пустой nginx.conf, события, http-диспетчер, 14 файлов политик в правильном порядке, vhost-ы в конце. Сквозная трассировка собрана из обычных map без Lua и проброшена через add_header/proxy_set_header. Логи - JSON с severity из статуса и разделением request_time против upstream_response_time. Метрики - изолированный stub_status за ACL. Политики proxy/cache/security/rate-limit написаны один раз и работают для всех. Возьми этот скелет, замени апстримы и домены на свои, выкинь устаревший X-XSS-Protection, добавь realip под свой CDN - и у тебя готовый, прозрачный и поддерживаемый nginx gateway, который не страшно открывать через год.
👍 ❤️3 🔥1 😄 🤔2
Аватара пользователя
zhenya2024
Сообщения: 1
Зарегистрирован: 04 июн 2026, 05:59

Re: Сценарий: микросервисный gateway по образцу ngx-trace-gateway

Сообщение zhenya2024 »

Дошло наконец зачем разбивать на globals - переставил logging выше trace и реально получил пустые id в логах, как в граблях написано. Спасибо, без урока неделю бы искал.
👍3 ❤️ 🔥 😄 🤔
Аватара пользователя
rebima
Сообщения: 1
Зарегистрирован: 23 май 2026, 00:54

Re: Сценарий: микросервисный gateway по образцу ngx-trace-gateway

Сообщение rebima »

А если перед шлюзом nginx стоит ещё один балансировщик и оба пишут chain - они корректно склеятся? И X-Prev-Request-ID на втором хопе будет id первого, правильно понял?
👍2 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Сценарий: статика, SPA и кэширование за CDN
Следующая глава →
Капстоун: собираем production-gateway с нуля

Все главы курса «Nginx профессионально: от первого конфига до API-gateway на OpenResty и Angie»

Поделиться темой: ✈ Telegram VK
  • Похожие темы

Вернуться в «Nginx профессионально: от первого конфига до API-gateway на OpenResty и Angie»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость