Введение: зачем нужен reverse proxy в Docker
На одном хосте с Docker часто работают несколько веб-приложений: админ-панель, API, документация, клиентский портал. Каждое приложение живет в отдельном контейнере и слушает свой порт. Прокидывать наружу десяток портов и запоминать, что на 8081 сидит Grafana, а на 8082 - Nextcloud, неудобно и небезопасно. Reverse proxy решает эту задачу: он принимает весь входящий трафик на 80 и 443 портах и распределяет его по контейнерам на основе правил.
В этой статье разобраны два инструмента: Nginx и Traefik. Nginx требует ручной конфигурации и дает полный контроль над каждым маршрутом. Traefik автоматически обнаруживает контейнеры через Docker API и строит маршруты из меток. Оба подхода рабочие, но подходят под разные сценарии.
Материал содержит готовые конфигурации для Docker Compose, примеры маршрутизации по доменным именам, портам и URL-путям, а также разбор типичных ошибок. Если вам нужно быстро поднять рабочий reverse proxy, начните с раздела про выбор инструмента, затем переходите к настройке.
Смежная тема - организация сетевого взаимодействия между контейнерами - раскрыта в руководстве по сетям bridge, host и overlay в Docker.
Выбор инструмента: Nginx или Traefik
Выбор между Nginx и Traefik сводится к одному вопросу: как часто меняется состав контейнеров и маршруты. Если у вас стабильный стек из трех-пяти сервисов, Nginx с ручной конфигурацией работает предсказуемо и не требует дополнительных абстракций. Если контейнеры создаются и удаляются регулярно, Traefik экономит часы ручной правки конфигов.
Критерии выбора reverse proxy
- Автоматическое обновление конфигурации. Traefik читает метки контейнеров и перестраивает маршруты на лету. Nginx требует перезагрузки после изменения конфига.
- Сложность маршрутизации. Nginx дает тонкий контроль над rewrite, заголовками, кэшированием. Traefik покрывает типовые сценарии через middleware.
- Требования к производительности. Оба инструмента работают на уровне сотен и тысяч запросов в секунду. Разница на практике незначительна для типовых веб-приложений.
- Знакомство с инструментом. Если команда уже знает Nginx, внедрение Traefik потребует времени на изучение модели конфигурации.
Сравнительная таблица Nginx и Traefik
| Параметр | Nginx | Traefik |
|---|---|---|
| Автоматическое обнаружение контейнеров | Нет, ручная настройка | Да, через Docker API |
| Формат конфигурации | Собственный синтаксис | YAML, CLI-флаги, Docker-метки |
| Поддержка Docker | Через контейнеризацию Nginx | Нативный провайдер Docker |
| Производительность | Высокая | Высокая |
| Сложность освоения | Средняя | Средняя, но другая модель |
| Экосистема | Огромная, много модулей | Middleware, интеграция с Let's Encrypt |
Для простых проектов с ручной конфигурацией выбирайте Nginx. Для продакшн-среды с частыми изменениями и автоматическим выпуском SSL-сертификатов - Traefik. Подробное сравнение обратных прокси для Docker, включая Nginx Proxy Manager и Caddy, есть в отдельном материале.
Подготовка окружения: Docker Compose и сети
Reverse proxy должен видеть все контейнеры, на которые он проксирует трафик. Для этого создается общая пользовательская сеть. Контейнеры в одной сети обращаются друг к другу по имени сервиса, без проброса портов наружу.
Создание общей сети для контейнеров
Базовая структура docker-compose.yml с общей сетью:
networks:
proxy:
driver: bridge
services:
app1:
image: nginx:alpine
networks:
- proxy
app2:
image: httpd:alpine
networks:
- proxyСеть proxy объединяет все сервисы. Reverse proxy подключается к этой же сети и обращается к app1 и app2 по именам. Порты контейнеров наружу не публикуются, доступ к ним идет только через прокси.
Использование переменных окружения в Docker Compose
Домены, порты и пути выносятся в файл .env, чтобы не дублировать значения в конфигурации:
# .env
APP1_DOMAIN=app1.example.com
APP2_DOMAIN=app2.example.com
PROXY_NETWORK=proxyВ docker-compose.yml переменные подставляются через ${VAR}:
services:
nginx:
image: nginx:alpine
environment:
- APP1_DOMAIN=${APP1_DOMAIN}
- APP2_DOMAIN=${APP2_DOMAIN}
networks:
- ${PROXY_NETWORK}Переменные окружения удобны при переносе конфигурации между стендами: dev, staging, production отличаются только содержимым .env.
Настройка Nginx в качестве reverse proxy
Nginx запускается в контейнере, конфигурационный файл монтируется через volume. Базовая структура:
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- proxyКонфигурация описывает серверные блоки с директивами server_name, listen и location.
Маршрутизация по доменным именам (виртуальные хосты)
Два домена направляются на два разных контейнера:
server {
listen 80;
server_name app1.example.com;
location / {
proxy_pass http://app1:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name app2.example.com;
location / {
proxy_pass http://app2:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Заголовки X-Forwarded-* обязательны: без них приложение за прокси не узнает реальный IP клиента и протокол запроса.
Маршрутизация по портам
Один домен, разные порты для разных сервисов:
server {
listen 8081;
server_name app.example.com;
location / {
proxy_pass http://app1:80;
}
}
server {
listen 8082;
server_name app.example.com;
location / {
proxy_pass http://app2:80;
}
}Схема подходит для служебных панелей, к которым обращаются по явному порту: http://app.example.com:8081.
Маршрутизация по URL-путям
Один домен, разные пути ведут на разные контейнеры:
server {
listen 80;
server_name app.example.com;
location /app1/ {
proxy_pass http://app1:80/;
}
location /app2/ {
proxy_pass http://app2:80/;
}
}Обратите внимание на слэш в конце proxy_pass. Если указать http://app1:80 без завершающего слэша, путь /app1/ будет передан приложению целиком. Со слэшем Nginx отрезает /app1/ и передает корень. Это частая причина ошибок 404.
Настройка Traefik в качестве reverse proxy
Traefik подключается к Docker-сокету и читает метки контейнеров. Маршруты описываются не в отдельном файле, а прямо в docker-compose.yml каждого сервиса.
services:
traefik:
image: traefik:v3.0
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxyФлаг exposedbydefault=false заставляет Traefik игнорировать контейнеры без явных меток. Это защищает от случайного проброса лишних сервисов.
Маршрутизация по доменным именам с Traefik
Метки на контейнере приложения:
services:
app1:
image: nginx:alpine
labels:
- "traefik.enable=true"
- "traefik.http.routers.app1.rule=Host(`app1.example.com`)"
- "traefik.http.routers.app1.entrypoints=web"
networks:
- proxyTraefik видит контейнер, читает правило Host и направляет запросы с домена app1.example.com на этот сервис. При добавлении нового контейнера перезапуск Traefik не требуется.
Маршрутизация по портам с Traefik
Для разных портов определяются отдельные entrypoints:
services:
traefik:
image: traefik:v3.0
command:
- "--entrypoints.web.address=:80"
- "--entrypoints.admin.address=:8081"
ports:
- "80:80"
- "8081:8081"
app1:
image: nginx:alpine
labels:
- "traefik.enable=true"
- "traefik.http.routers.app1.rule=Host(`app.example.com`)"
- "traefik.http.routers.app1.entrypoints=admin"
networks:
- proxyЗапрос на app.example.com:8081 попадает в entrypoint admin и маршрутизируется на app1.
Маршрутизация по URL-путям с Traefik
Правило PathPrefix направляет запросы по префиксу пути:
services:
app1:
image: nginx:alpine
labels:
- "traefik.enable=true"
- "traefik.http.routers.app1.rule=Host(`app.example.com`) && PathPrefix(`/app1`)"
- "traefik.http.routers.app1.entrypoints=web"
networks:
- proxyЗапросы на app.example.com/app1 и app.example.com/app1/anything уходят на app1. Для подмены пути на корень приложения используется middleware stripprefix. Подробнее о цепочках middleware и сложной маршрутизации в Traefik рассказано в пошаговом руководстве с YAML-примерами.
Типичные ошибки и их решение
Большинство проблем при настройке reverse proxy сводится к четырем причинам: сеть, слэши, заголовки, конфликты портов.
Ошибки сети и связности контейнеров
Симптом: Nginx или Traefik возвращает 502 Bad Gateway. Причина - прокси не может достучаться до контейнера приложения.
Проверьте, что оба контейнера подключены к одной сети:
docker network inspect proxyВ выводе должны быть оба контейнера. Если приложение в другой сети, добавьте его в общую сеть или создайте дополнительное подключение. Обращайтесь к сервису по имени из docker-compose.yml, а не по IP: IP-адреса контейнеров меняются при перезапуске.
Ошибки конфигурации proxy_pass и location
Симптом: приложение отвечает 404 на всех страницах или не находит статические файлы. Причина - неправильная обработка пути.
Правило простое: если location заканчивается на слэш, proxy_pass тоже должен заканчиваться на слэш. Тогда Nginx отрезает путь location и передает остаток. Если слэши не совпадают, путь склеивается некорректно.
Для WebSocket-соединений добавьте заголовки:
location /ws/ {
proxy_pass http://app1:80;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}Без этих заголовков WebSocket-соединение разрывается сразу после установки.
Конфликт портов возникает, когда контейнер приложения публикует тот же порт на хосте, что и прокси. Уберите ports у приложений, оставьте публикацию только у reverse proxy. Внутренние порты контейнеров не конфликтуют между собой.
Заключение: итоги и рекомендации
Reverse proxy в Docker - это единая точка входа для всех веб-приложений на хосте. Nginx подходит для стабильных стеков с ручной конфигурацией, Traefik - для динамических сред с автоматическим обнаружением контейнеров. Оба инструмента поддерживают маршрутизацию по доменам, портам и URL-путям.
Начните с создания общей сети в Docker Compose, затем настройте один сценарий маршрутизации и проверьте его. Не публикуйте порты приложений наружу, весь трафик должен идти через прокси. Проверяйте заголовки X-Forwarded-* и следите за слэшами в proxy_pass.
Для углубления в тему изучите руководство по маршрутизации процессов с готовыми конфигурациями и настройку Nginx Proxy Manager для production.