Skip to content
mrVanich
Назад до нотаток

Docker reverse proxy: Nginx, порти та доступ до контейнерів у локальній мережі

Reverse proxy – це єдина вхідна точка для кількох вебсервісів. Він приймає запит з браузера, визначає домен або шлях і передає його потрібному контейнеру. Так не потрібно відкривати в локальну мережу десяток портів: назовні доступний тільки Nginx на 80/443, а застосунки залишаються у внутрішній Docker-мережі.

Що часто називають «світчем» у Docker

  • Публікація портів: 8081:80. Windows отримує порт 8081 і передає його в контейнер на 80. Сервіс відкривається як http://IP_ПК:8081.
  • Reverse proxy: один Nginx на 80/443 розподіляє запити за доменами або шляхами – наприклад, php.local.test до PHP, api.local.test до Python.

Не призначайте постійні IP контейнерам лише для маршрутизації. У Docker Compose сервіси знаходять один одного за іменами php-app та python-api через вбудований DNS. Це і є нормальна внутрішня комутація контейнерів.

Схема стенду

Браузер / локальна мережа
             |
       Windows:80, 443
             |
      reverse-proxy (Nginx)
        |                 |
   php-app:80       python-api:8000

Лише reverse proxy має секцію ports. Застосунки використовують expose, тобто доступні тільки іншим контейнерам у приватній мережі.

1. Структура проєкту на D:

D:\docker-proxy-lab\
├── compose.yaml
├── proxy\default.conf
├── php-app\index.html
└── python-api\
    ├── Dockerfile
    ├── requirements.txt
    └── app.py
New-Item -ItemType Directory -Force D:\docker-proxy-lab\proxy
New-Item -ItemType Directory -Force D:\docker-proxy-lab\php-app
New-Item -ItemType Directory -Force D:\docker-proxy-lab\python-api
Set-Location D:\docker-proxy-lab

2. Compose: три сервіси, одна мережа

Створіть D:\docker-proxy-lab\compose.yaml:

services:
  proxy:
    image: nginx:1.27-alpine
    ports:
      - "80:80"
    volumes:
      - ./proxy/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - php-app
      - python-api
    networks: [web]
    restart: unless-stopped

  php-app:
    image: nginx:1.27-alpine
    volumes:
      - ./php-app:/usr/share/nginx/html:ro
    expose: ["80"]
    networks: [web]
    restart: unless-stopped

  python-api:
    build: ./python-api
    expose: ["8000"]
    networks: [web]
    restart: unless-stopped

networks:
  web:
    driver: bridge

Якщо порт 80 зайнятий IIS або іншою програмою Windows, для тесту замініть mapping на 8080:80. Тоді користуйтеся адресою http://localhost:8080.

3. Конфігурація Nginx reverse proxy

У D:\docker-proxy-lab\proxy\default.conf:

server {
    listen 80;
    server_name php.local.test;
    location / {
        proxy_pass http://php-app: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 api.local.test;
    location / {
        proxy_pass http://python-api:8000;
        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;
    }
}

4. Тестові застосунки

У D:\docker-proxy-lab\php-app\index.html:

<h1>PHP / static application</h1>
<p>Запит прийшов через Docker reverse proxy.</p>

У D:\docker-proxy-lab\python-api\requirements.txt:

Flask==3.1.1

У D:\docker-proxy-lab\python-api\app.py:

from flask import Flask, jsonify
app = Flask(__name__)
@app.get("/")
def index():
    return jsonify(service="python-api", status="ok")
@app.get("/health")
def health():
    return jsonify(status="healthy")

У D:\docker-proxy-lab\python-api\Dockerfile:

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
CMD ["flask", "--app", "app", "run", "--host=0.0.0.0", "--port=8000"]

5. Локальні доменні імена

Відкрийте Блокнот від імені адміністратора і додайте в C:\Windows\System32\drivers\etc\hosts:

127.0.0.1 php.local.test
127.0.0.1 api.local.test

Тепер один Nginx на одному порту розрізняє два сайти за HTTP-заголовком Host.

6. Запуск і тест

Set-Location D:\docker-proxy-lab
docker compose up --build -d
docker compose ps
curl.exe http://php.local.test
curl.exe http://api.local.test/health

Відкрийте http://php.local.test та http://api.local.test. Браузер іде на один порт Windows, а Nginx обирає правильний контейнер.

Як дати доступ локальній мережі

Для прямого тесту окремого сервісу використайте mapping "8081:80". Дізнайтеся IP ПК командою ipconfig, після чого адреса буде схожа на http://192.168.1.50:8081. Дозвольте цей TCP-порт у Windows Defender Firewall лише для профілю Private і вимкніть правило після тесту.

Якщо сервіс має бути доступний тільки на цьому ПК, прив’яжіть mapping до loopback: "127.0.0.1:8081:80". Для суто внутрішніх контейнерів взагалі не задавайте ports – залишайте лише expose.

Навіщо це потрібно

  • один вхідний порт замість десятків портів застосунків;
  • розподіл сервісів за доменами або шляхами;
  • єдине місце для HTTPS, логів, rate limiting і правил безпеки;
  • застосунки не стають напряму видимими з мережі;
  • схема легко переноситься на VPS або внутрішній сервер.

Для навчального середовища Nginx достатній. Для автоматичного HTTPS у кількох доменах можна окремо розглянути Traefik або Caddy, але розуміння базового Nginx reverse proxy корисне у будь-якому стеку.

Джерела