Reverse Proxy — это прокси-сервер, который принимает запросы от клиентов и перенаправляет их на внутренние сервисы, такие как frontend и backend. Одним из самых популярных решений для reverse proxy является Nginx, который может быть использован для маршрутизации запросов между сервисами внутри Docker.

Основные задачи Reverse Proxy:

  1. Маршрутизация запросов: перенаправление клиентских запросов к правильному сервису.
  2. Упрощение конфигурации: клиент обращается к одному серверу (reverse proxy), который затем направляет запросы на различные backend и frontend сервисы.
  3. Изоляция: backend и frontend сервисы скрыты за прокси, клиенты взаимодействуют только с proxy-сервером.

Структура проекта:

  • Frontend: простое приложение на Node.js с использованием Express для отдачи статического контента.
  • Backend: Node.js приложение на Express для обработки API-запросов.
  • Nginx: используется для маршрутизации запросов к frontend и backend.

Пример структуры файлов:


/my-app 
    /frontend    
        Dockerfile    
         server.js    
        index.html 
        package.json 
    /backend    
        Dockerfile    
        app.js  
        package.json
    /nginx    
        nginx.conf  
docker-compose.yml   

Шаг 1: Настройка Frontend

frontend/server.js:


const express = require('express');
const path = require('path');

const app = express();
const PORT = 3000;

// Обслуживаем статический контент
app.use(express.static(path.join(__dirname)));

// Отправляем index.html для всех запросов
app.get('*', (req, res) => {
    res.sendFile(path.resolve(__dirname, 'index.html'));
});

app.listen(PORT, () => {
    console.log(`Frontend server is running on port ${PORT}`);
});

frontend/index.html:


<!DOCTYPE html> 
<html lang="en"> 
<head>    
    <meta charset="UTF-8">    
    <meta name="viewport" content="width=device-width, initial-scale=1.0">    
    <title>Frontend</title> 
</head> 
<body>    
    <h1>Welcome to the Frontend</h1> 
</body> 
</html>   

frontend/Dockerfile:


# Используем Node.js как базовый образ
FROM node:14

# Устанавливаем рабочую директорию внутри контейнера
WORKDIR /app

# Копируем package.json и package-lock.json (если он есть)
COPY package*.json ./

# Устанавливаем зависимости
RUN npm install

# Копируем остальные файлы проекта в контейнер
COPY . .

# Открываем порт 3000
EXPOSE 3000

# Команда для запуска приложения
CMD ["node", "server.js"]

Шаг 2: Настройка Backend

backend/app.js:


const express = require('express');
const app = express();
const PORT = 4000;

// Простое API
app.get('/api', (req, res) => {
    res.json({ message: 'Hello from the backend!' });
});

// Новый маршрут для корневого пути
app.get('/', (req, res) => {
    res.send('Welcome to the Backend!');
});

app.listen(PORT, () => {
    console.log(`Backend server is running on port ${PORT}`);
});

backend/Dockerfile:


# Используем Node.js как базовый образ
FROM node:14

# Устанавливаем рабочую директорию внутри контейнера
WORKDIR /app

# Копируем package.json и package-lock.json (если он есть)
COPY package*.json ./

# Устанавливаем зависимости
RUN npm install

# Копируем остальные файлы проекта в контейнер
COPY . .

# Открываем порт 4000
EXPOSE 4000

# Команда для запуска приложения
CMD ["node", "app.js"]

Шаг 3: Настройка Nginx как Reverse Proxy

nginx/nginx.conf:


events {
    worker_connections 1024;  # Установка количества соединений
}

http {
    server {
        listen 80;
        server_name localhost;

        location / {
            proxy_pass http://frontend:3000;  # Прокси для frontend
        }

        location /api/ {
            proxy_pass http://backend:4000;  # Прокси для backend
            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: Настройка Docker Compose

docker-compose.yml:


services:
  frontend:
    build: ./frontend
    container_name: frontend
    ports:
      - "3000:3000"  # Фронтенд работает на порту 3000
    networks:
      - mynetwork

  backend:
    build: ./backend
    container_name: backend
    ports:
      - "4000:4000"  # Бэкенд работает на порту 4000
    networks:
      - mynetwork

  nginx:
    image: nginx:alpine
    container_name: nginx
    ports:
      - "80:80"  # Nginx работает на порту 80
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf  # Привязка конфигурации
    networks:
      - mynetwork
    depends_on:
      - frontend
      - backend

networks:
  mynetwork:
    driver: bridge

Шаг 5: Файл package.json (frontend) & package.json (backend)

frontend:


{
  "name": "frontend",
  "version": "1.0.0",
  "main": "server.js",
  "dependencies": {
    "express": "^4.17.1"
  }
}

backend:


{
  "name": "backend",
  "version": "1.0.0",
  "main": "app.js",
  "dependencies": {
    "express": "^4.17.1"
  }
}

Шаг 6: Запуск приложения

  1. Перейдите в корневую директорию проекта /my-app.
  2. Выполните команду для сборки и запуска контейнеров:
    
    docker-compose up --build   

Как это работает:

  • Nginx действует как reverse proxy и принимает все запросы на порт 80.
  • Запросы, начинающиеся с /api, перенаправляются на backend-сервис (контейнер backend на порту 4000).
  • Все остальные запросы (например, к корневому URL) перенаправляются на frontend-сервис (контейнер frontend на порту 3000).
  • Клиенты взаимодействуют с Nginx, который маршрутизирует их запросы в зависимости от URL.

Пример запросов:

  1. Запрос к frontend:
    • Откройте в браузере http://localhost, и вы увидите страницу frontend, отданную через Node.js.
  2. Запрос к backend:
    • Перейдите на http://localhost/api, и вы увидите JSON-ответ от backend-сервиса:
      
      {"message": "Hello from the backend!"}   

Вывод:

Использование Nginx как reverse proxy для разделения frontend и backend приложений через Docker предоставляет эффективный способ маршрутизации запросов. Такая архитектура упрощает управление, повышает безопасность и делает приложение масштабируемым.

Проект на GitHub: https://github.com/wa-magazin/reverseproxy