Настройка API Gateway
API Gateway (сервис frontend) в Памир построен на основе веб-сервера nginx с динамической генерацией
конфигурации через docker-gen. Данный раздел описывает, как
работает динамическая конфигурация и в каких случаях её необходимо дополнять или заменять
статическим конфигом.
Динамическая конфигурация (на основе лейблов)
API Gateway отслеживает события Docker-демона и при каждом изменении состава запущенных
контейнеров пересобирает файл /etc/nginx/conf.d/default.conf из шаблона.
После пересборки конфигурации веб-сервер перезапускается автоматически.
Подключение сервиса
Чтобы сервис был доступен через API Gateway, в docker-compose.additional.yml на нём выставляются лейблы:
services:
my-service:
labels:
# Обязательно: включить проксирование
com.github.nginx-proxy.nginx-proxy.expose-service.enable: true
# Путь в URL (по умолчанию — имя сервиса из com.docker.compose.service)
com.github.nginx-proxy.nginx-proxy.expose-service.path: "my-service"
# Порт бэкенда (по умолчанию 80)
com.github.nginx-proxy.nginx-proxy.expose-service.port: "8080"
# Перезапись пути destination (опционально)
com.github.nginx-proxy.nginx-proxy.expose-service.dest: "/"
API Gateway сгенерирует location вида:
location ^~ /my-service/ {
proxy_pass http://my-service:8080/;
}
Аутентификация через auth_request
Если включена аутентификация (auth: true), каждый запрос к сервису сначала
проверяется через внутренний location /_pamir_auth:
location = /_pamir_auth {
internal;
proxy_pass http://<auth-service>:80/v3/internal/user;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
location ^~ /my-service/ {
auth_request /_pamir_auth;
proxy_pass http://my-service:8080/;
}
Auth-сервис отвечает кодом 2xx (allow) или 401/403 (deny).
Контейнер с auth-сервисом обнаруживается автоматически по лейблу:
services:
my-service:
labels:
com.github.nginx-proxy.nginx-proxy.auth-service.enable: "true"
Если auth-сервис недоступен или имеет статус unhealthy — ни один location с auth: true в конфиге
не создаётся.
Проброс заголовков аутентификации
При auth-headers: true API Gateway извлекает из ответа auth-сервиса заголовки с идентификатором
пользователя и его ролью и пробрасывает их в сервис:
services:
my-service:
labels:
com.github.nginx-proxy.nginx-proxy.expose-service.auth-headers: "true"
auth_request_set $p_user $upstream_http_x_user_login;
auth_request_set $p_roles $upstream_http_x_user_opensearch_role;
proxy_set_header X-User-Login $p_user;
proxy_set_header X-User-Opensearch-Role $p_roles;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Используется для сервисов, которым необходимо знать идентификатор пользователя — например, OpenSearch Dashboards (с последующей авторизацией в OpenSearch по заголовку, см. Ролевая модель OpenSearch).
Дополнительные заголовки
С помощью лейблов proxy-set-headers и add-headers можно добавить произвольные
заголовки запроса и ответа (значения разделяются |):
services:
my-service:
labels:
# Заголовки запроса к бэкенду
com.github.nginx-proxy.nginx-proxy.expose-service.proxy-set-headers: >-
X-Custom-Header "value"|Authorization "Basic dXNlcjpwYXNz"
# Заголовки ответа клиенту
com.github.nginx-proxy.nginx-proxy.expose-service.add-headers: >-
Set-Cookie "session=abc;"
Сервисы, проксируемые из коробки
| Сервис | Путь | Порт | Аутентификация | Проброс заголовков |
|---|---|---|---|---|
opensearch-dashboards | /os_dashboards/ | 5601 | ✓ | ✓ (X-User-Login, X-User-Opensearch-Role) |
rabbitmq | /rabbitmq/ | 15672 | ✓ | — (инжект статичных credentials) |
alertmanager | /alertmanager/ | 9093 | ✓ | — |
flower | /flower/ | 5555 | ✓ | — |
metrics-storage | /metrics-storage/ | 8428 | ✓ | — |
metrics-agent | /metrics-agent/ | 8429 | ✓ | — |
metrics-ruler | /metrics-ruler/ | 8880 | ✓ | — |
Когда нужен статический конфиг
Динамическая конфигурация работает только с контейнерами, доступными на том же Docker-узле, что и API Gateway. В следующих сценариях необходим статический конфиг.
Структура статического конфига
Директива /_pamir_auth и все динамически создаваемые location-блоки находятся внутри
server-блока с server_name backend (значение DEFAULT_HOST из .env). Этот же server-блок
является default_server — он обрабатывает все входящие запросы.
При написании статической конфигурации нельзя размещать location-блоки в отдельном
server { server_name _; } — такой блок никогда не получит запросы, поскольку default_server
перехватывает их раньше. Кроме того, /_pamir_auth доступен только внутри того же server-блока,
в котором определён.
Для корректного добавления статических locations используется механизм vhost.d: шаблон nginx
включает файл /etc/nginx/vhost.d/backend (где backend — значение DEFAULT_HOST) внутри
нужного server-блока. Это стандартный способ расширения конфигурации.
Статический конфиг разбивается на два файла:
| Файл | Куда монтировать | Что содержит |
|---|---|---|
metrics-cluster.conf | /etc/nginx/conf.d/metrics-cluster.conf | upstream-блоки (http-уровень) |
backend | /etc/nginx/vhost.d/backend | location-блоки (внутри server-блока) |
services:
frontend:
volumes:
- ./data/nginx/conf.d/metrics-cluster.conf:/etc/nginx/conf.d/metrics-cluster.conf:ro
- ./data/nginx/vhost/backend:/etc/nginx/vhost.d/backend:ro
Если в .env задано значение DEFAULT_HOST, отличное от backend, или настроен
LETSENCRYPT_HOST=pamir.example.com, имя файла в vhost.d/ должно совпадать с этим именем
(например, vhost.d/pamir.example.com).
Сценарий 1: сервисы Metrics на смежных серверах
Если компоненты Metrics (metrics-storage, metrics-agent, metrics-ruler)
развёрнуты на отдельных Docker-узлах, docker-gen их не видит и не создаёт locations.
Шаг 1. Отключить сервисы Metrics на узле с API Gateway в docker-compose.additional.yml:
services:
metrics-storage:
profiles: !override [never]
metrics-agent:
profiles: !override [never]
metrics-ruler:
profiles: !override [never]
Также возможно отключить построение динамической конфигурации для сервиса без его отключения сбросом лейблов:
services:
metrics-storage:
labels: !override []
metrics-agent:
labels: !override []
metrics-ruler:
labels: !override []
Шаг 2. Смонтировать оба файла статического конфига в контейнер через docker-compose.additional.yml:
services:
frontend:
volumes:
- ./data/nginx/conf.d/metrics-cluster.conf:/etc/nginx/conf.d/metrics-cluster.conf:ro
- ./data/nginx/vhost/backend:/etc/nginx/vhost.d/backend:ro
Шаг 3. Написать файл upstream-блоков data/nginx/conf.d/metrics-cluster.conf:
upstream metrics-storage {
server <IP-узла-storage-1>:8428;
server <IP-узла-storage-2>:8428 backup;
keepalive 4;
}
upstream metrics-agent {
server <IP-узла-agent-1>:8429;
server <IP-узла-agent-2>:8429 backup;
keepalive 4;
}
upstream metrics-ruler {
server <IP-узла-ruler>:8880;
keepalive 4;
}
Шаг 4. Написать файл location-блоков data/nginx/vhost/backend:
location ^~ /metrics-storage/ {
auth_request /_pamir_auth;
proxy_pass http://metrics-storage/;
}
location ^~ /metrics-agent/ {
auth_request /_pamir_auth;
proxy_pass http://metrics-agent/;
}
location ^~ /metrics-ruler/ {
auth_request /_pamir_auth;
proxy_pass http://metrics-ruler/;
}
Сценарий 2: кластер OpenSearch на смежных серверах (с вынесенным Dashboards)
Если opensearch-dashboards развёрнут на отдельном Docker-узле:
Шаг 1. Отключить сервис opensearch-dashboards на узле с API Gateway:
services:
opensearch-dashboards:
profiles: !override [never]
Шаг 2. Смонтировать статические конфиги в контейнер:
services:
frontend:
volumes:
- ./data/nginx/conf.d/opensearch-cluster.conf:/etc/nginx/conf.d/opensearch-cluster.conf:ro
- ./data/nginx/vhost/backend:/etc/nginx/vhost.d/backend:ro
Шаг 3. Написать файл upstream-блоков data/nginx/conf.d/opensearch-cluster.conf:
upstream opensearch-dashboards {
server <IP-узла-dashboards>:5601;
keepalive 4;
}
Шаг 4. Написать файл location-блоков data/nginx/vhost/backend:
location ^~ /os_dashboards/ {
auth_request /_pamir_auth;
# Извлечь идентификационные заголовки из ответа auth-сервиса
auth_request_set $p_user $upstream_http_x_user_login;
auth_request_set $p_roles $upstream_http_x_user_opensearch_role;
# Пробросить в Dashboards для авторизации в OpenSearch
proxy_set_header X-User-Login $p_user;
proxy_set_header X-User-Opensearch-Role $p_roles;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://opensearch-dashboards/;
}
Сценарий 3: изменение учётных данных RabbitMQ
По умолчанию API Gateway инжектирует в запросы к RabbitMQ Management UI фиксированные
credentials пользователя через заголовок Authorization (и соответствующую
cookie для UI). Это реализовано через лейблы в docker-compose.deps.yml сервиса rabbitmq:
labels:
com.github.nginx-proxy.nginx-proxy.expose-service.proxy-set-headers: >-
Authorization "Basic <base64(login:password)>"
com.github.nginx-proxy.nginx-proxy.expose-service.add-headers: >-
Set-Cookie "m=<uid>:<base64(login:password)>;"
При изменении пароля пользователя в RabbitMQ необходимо обновить соответствующие Base64-значения в этих лейблах.
Способ 1 — через лейблы (рекомендуется для одного узла):
-
Закодировать в base64 новые credentials:
echo -n 'login:new_password' | base64
# Пример вывода: bG9naW46bmV3X3Bhc3N3b3Jk -
URL-закодировать Base64-строку для cookie (заменить
=на%3D):echo -n 'login:new_password' | base64 | python3 -c \
"import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))"
# Пример вывода: bG9naW46bmV3X3Bhc3N3b3Jk (если нет = в конце) -
Переопределить лейблы в
docker-compose.additional.yml:docker-compose.additional.ymlservices:
rabbitmq:
labels:
com.github.nginx-proxy.nginx-proxy.expose-service.proxy-set-headers: >-
Authorization "Basic bG9naW46bmV3X3Bhc3N3b3Jk"
com.github.nginx-proxy.nginx-proxy.expose-service.add-headers: >-
Set-Cookie "m=<uid>:bG9naW46bmV3X3Bhc3N3b3Jk%3D%3D;" -
Перезапустить сервис:
pamirctl start rabbitmq frontend
Способ 2 — через статический конфиг (для кластерного развёртывания):
Аналогично сценариям 1 и 2: сбросить лейблы (labels: !override []), определить upstream
в conf.d/*.conf и разместить location с нужными заголовками proxy_set_header / add_header
в vhost/backend.
Применение изменений
Любые изменения в docker-compose.additional.yml (лейблы, volumes) применяются командой:
pamirctl start frontend # перезапустить только API Gateway
# или
pamirctl start # перезапустить все сервисы
Монтирование нового .conf-файла требует пересоздания контейнера API Gateway (pamirctl start frontend).
Изменения уже смонтированного файла применяются перезагрузкой конфига без пересоздания:
pamirctl restart frontend