Развертывание многоконтейнерных веб-приложений с помощью Docker Compose и Nginx
Введение
Современные веб-приложения редко работают как единый монолитный процесс. Вместо этого они состоят из нескольких сотрудничающих сервисов: интерфейса, API, базы данных, кэша, очереди сообщений и, возможно, бегуна. Ручное управление жизненным циклом каждого контейнера, их сетей и их зависимостей быстро становится подверженным ошибкам и трудоемким. Docker Compose и Nginx вместе обеспечивают проверенное на практике, удобное для производства решение для определения и развертывания этих многоконтейнерных систем с минимальным трением. Docker Compose позволяет описать весь стек приложений в одном файле YAML, в то время как Nginx действует как надежный обратный прокси и балансировщик нагрузки, маршрутизация трафика в соответствующую службу. В этом расширенном руководстве мы пройдем через реалистичную настройку, углубимся в детали конфигурации, рассмотрим лучшие практики безопасности и покажем вам, как масштабироваться от разработки до производства.
Предпосылки
Перед погружением убедитесь, что на вашей системе установлено следующее:
- Двигатель докера (версия 20.10 или новее) — Установите докер
- Docker Compose (V2 теперь интегрирован в Docker CLI; проверьте с ) — Установите Docker Compose
- Базовое знакомство с командами терминала и синтаксисом YAML.
Необязательно иметь доменное имя, указывающее на ваш сервер, если вы планируете следовать разделу конфигурации SSL.
Docker Compose в глубине
Docker Compose - это не просто обертка с «запуском докера». Это декларативный инструмент, который позволяет определять услуги, сети, объемы, переменные среды, политики перезапуска и проверки здоровья в одном файле под названием . Спецификация Compose (теперь часть Compose Specification) стандартизирует описание многоконтейнерных приложений, делая вашу настройку портативной для различных трубопроводов CI / CD и облачных провайдеров.
Услуги
Каждое изображение контейнера, его порты, объемы, среда и зависимости определяются под ключом . Сервисы могут общаться друг с другом через выделенную мостовую сеть, которую Compose создает автоматически. Это означает, что вам никогда не нужно открывать порты хоста для межсервисной связи — только обратный прокси-сервер должен подвергать порты внешнему миру.
Сети
По умолчанию Compose создает единую сеть для всех сервисов, предоставляя им обнаружение сервисов на основе DNS с использованием имени службы в качестве имени хоста. Например, служба под названием может быть достигнута другими службами просто как . Вы также можете определить пользовательские сети для изоляции — например, поместив только обратный прокси-сервер в общедоступную сеть и сохраняя контейнеры баз данных во внутренней сети.
networks:
frontend:
backend:
services:
nginx:
networks:
- frontend
app:
networks:
- frontend
- backend
db:
networks:
- backend
Объемы
Объемы являются предпочтительным механизмом для сохранения данных, генерируемых контейнерами Docker. В Compose можно объявить именованные тома на верхнем уровне и монтировать их в сервисы. Это необходимо для баз данных, загрузок файлов и любого государственного сервиса.
volumes:
db_data:
services:
postgres:
image: postgres:16
volumes:
- db_data:/var/lib/postgresql/data
Переменные окружающей среды
Вы можете жестко закодировать их в файле Compose для разработки, но для производства вы должны использовать файлы или секреты Docker. Compose автоматически загружает файл , расположенный в том же каталоге, если вы не указываете .
services:
api:
image: my-api:latest
env_file:
- ./api.env
environment:
- NODE_ENV=production
Настройка Nginx как обратного прокси — Advanced
Nginx сияет как обратный прокси из-за его высокой производительности, низкого объема памяти и богатого набора функций. В многоконтейнерной архитектуре Nginx сидит на краю, принимает запросы клиентов и пересылает их в соответствующий сервис на основе запроса URI, заголовков или даже содержимого тела. Он также может обрабатывать SSL-окончание, кэширование, ограничение скорости и балансировку нагрузки.
Обратный прокси с несколькими восходящими потоками
Вот более полная конфигурация, которая демонстрирует, как разделить трафик между двумя различными приложениями, включая обработку статических активов и WebSockets:
upstream app1_upstream {
server app1:8000;
}
upstream app2_upstream {
server app2:8001;
}
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# App 1 – main web frontend
location / {
proxy_pass http://app1_upstream;
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;
}
# App 2 – admin dashboard
location /admin/ {
proxy_pass http://app2_upstream/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# WebSocket support (e.g., for live updates)
location /ws/ {
proxy_pass http://app1_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Static assets – serve directly for better performance
location /static/ {
alias /var/www/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Обратите внимание на использование блоков . Они позволяют определить группу серверов для балансировки нагрузки. Даже с одним сервером использование группы upstream позволяет легко добавлять больше реплик позже, не касаясь блока сервера.
SSL/TLS с функцией Let’s Encrypt
Для производства, обслуживание HTTPS не является предметом переговоров. Вы можете автоматизировать обновление сертификата с помощью Certbot или acme.sh внутри контейнера коляски. Общим шаблоном является установка сертификатов в качестве объемов в контейнер Nginx. Например:
services:
nginx:
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./certs:/etc/nginx/certs:ro
- ./static:/var/www/static:ro
ports:
- "80:80"
- "443:443"
depends_on:
- app1
- app2
Для автоматического обновления вы можете добавить сопутствующий контейнер, такой как или , который выполняет работу cron и перезагружает Nginx при обновлении сертификатов.
Балансировка нагрузки и проверки здоровья
При масштабировании сервиса на несколько реплик Nginx может распределять запросы с помощью круглых ромбов, наименьших соединений или хэша IP. объедините это с опцией Docker Compose :
upstream api_servers {
least_conn;
server api:80 max_fails=3 fail_timeout=30s;
}
Для обнаружения нездоровых контейнеров Nginx может быть настроен с помощью (требуется NGINX Plus или использовать из версии с открытым исходным кодом с небольшим обходным путем).
Создание файла Docker Compose — реальный пример
Давайте объединим все в практический файл Compose для веб-приложения, состоящего из API Node.js, интерфейса React, обслуживаемого Nginx, базы данных PostgreSQL и кэша Redis. Контейнер Nginx будет обслуживать статические файлы и запросы прокси-API.
version: "3.9"
services:
nginx:
image: nginx:alpine
container_name: reverse-proxy
restart: unless-stopped
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./frontend/build:/var/www/frontend:ro
- ./certs:/etc/nginx/certs:ro
ports:
- "80:80"
- "443:443"
depends_on:
- api
- frontend
networks:
- public
frontend:
image: my-frontend:latest
# In production, frontend is built into static files and served by Nginx
# This container may run a dev server, but Nginx will bypass it for static files
container_name: frontend-dev
ports:
- "3000:3000"
networks:
- public
# healthcheck: ...
api:
image: my-api:latest
container_name: api-server
restart: unless-stopped
env_file:
- ./api/.env.production
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
networks:
- public
- internal
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4000/health"]
interval: 30s
timeout: 10s
retries: 3
postgres:
image: postgres:16-alpine
container_name: database
restart: unless-stopped
environment:
POSTGRES_USER: app_user
POSTGRES_DB: app_db
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- pgdata:/var/lib/postgresql/data
secrets:
- db_password
networks:
- internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app_user"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: cache
restart: unless-stopped
volumes:
- redisdata:/data
networks:
- internal
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
volumes:
pgdata:
redisdata:
networks:
public:
internal:
internal: true
secrets:
db_password:
file: ./secrets/db_password.txt
Ключевые моменты в этом файле:
- Сеть изоляции: сеть определяется как , то есть только услуги, подключенные к ней, могут общаться. База данных и кэш недоступны из-за пределов наложения Docker.
- Секреты: Чувствительные данные, такие как пароли, хранятся в виде секретов Docker, смонтированных в виде файлов внутри контейнера. Это позволяет избежать утечки их в переменные среды, которые могут быть раскрыты через .
- Проверки здоровья: Службы объявляют проверки здоровья, чтобы могли дождаться состояния . Nginx начнется только после того, как API будет здоровым.
- Статические активы: Выход сборки интерфейса устанавливается непосредственно в Nginx, что позволяет Nginx обслуживать статические файлы, не нажимая на сервер разработчиков интерфейса. Это улучшает производительность и разделяет проблемы.
Развертывание приложения - шаг за шагом
При наличии готового файла Compose и конфигурации Nginx развертывание включает в себя несколько простых команд. Во-первых, убедитесь, что все файлы конфигурации находятся на месте:
project/
├── docker-compose.yml
├── nginx/
│ └── nginx.conf
├── api/
│ └── .env.production
├── frontend/
│ └── build/
├── certs/
│ └── (fullchain.pem, privkey.pem)
└── secrets/
└── db_password.txt
Тогда беги.
docker compose pull # pull latest images
docker compose up -d # start all services in background
docker compose ps # verify services are running
docker compose logs -f # tail logs for all services
После того, как все будет готово, проверьте приложение, посетив ваш домен. Проверьте журналы Nginx, чтобы подтвердить, что запросы прокси-кодируются правильно. Если вам нужно обновить параметры конфигурации или среды, отредактируйте соответствующие файлы и запустите:
docker compose up -d --no-deps --build nginx # rebuild only nginx if config changed
Для развертывания с нулевым временем простоя рассмотрите возможность использования сине-зеленых или прокрутки обновлений с функцией Compose и проверками здоровья Nginx вверх по течению.
Масштабирование и настройка производительности
Docker Compose делает тривиальным масштабирование сервисов без гражданства, таких как API:
docker compose up -d --scale api=3
Сейчас запущены три реплики API-контейнера. Nginx, сконфигурированный с вышестоящим блоком, будет автоматически распределять между ними запросы. Для дальнейшего повышения производительности:
- Включить сжатие Gzip в Nginx для текстовых ответов.
- Установите соответствующие заголовки кэша для статических активов (как показано в примере).
- Используйте CDN для глобальной доставки статических файлов.
- Тюне Nginx обрабатывает , чтобы соответствовать ядрам процессора:
- Увеличить пределы соединения:
Для баз данных убедитесь, что у вас есть объединение соединений в вашем API, и рассмотрите возможность использования PgBouncer в качестве контейнера для коляски.
Устранение общих проблем
Даже при хорошо структурированной установке все может пойти не так. Вот частые подводные камни и как их исправить:
- Контейнер не может связаться с другой службой по имени: Проверьте, что обе службы находятся в одной сети Docker. Используйте для тестирования.
- Nginx возвращает 502 Bad Gateway: Контейнер верхнего потока может не работать или не прослушивать ожидаемый порт. Проверьте с . Также убедитесь, что URL-адрес прокси-пропуска включает правильный протокол и порт.
- Ошибки разрешения с томами: Файлы, смонтированные в контейнеры, наследуют право собственности хоста. Используйте пространства имен пользователей или установите директиву в Nginx и служебном контейнере.
- Сертификат SSL не продлевает: Если используется Certbot автономно, убедитесь, что порты 80 и 443 не заблокированы Nginx во время обновления.
- Переменные среды, не загруженные : Проверьте, находится ли файл в правильном месте или используйте директиву .
Лучшие практики безопасности
Работа многоконтейнерных приложений в производстве требует бдительности:
- Никогда не запускайте контейнеры в качестве корня , если это не необходимо. Используйте директиву или укажите в Dockerfile.
- Сохраняйте изображения в актуальном состоянии с регулярным сканированием уязвимостей. Используйте официальные базовые изображения и версии с пин-кодами.
- Ограничение сетевого воздействия: Только выставлять в интернет обратные прокси-порты. Все остальные сервисы должны быть во внутренних сетях.
- Использовать управление секретами для паролей, ключей API и токенов. Секреты Docker — хорошее начало; для более крупных настроек рассмотрите HashiCorp Vault.
- Включить Docker Content Trust для проверки подписей изображений.
- Регулярно регистрируйте и проверяйте контейнерную активность с использованием или централизованного решения для регистрации, такого как стек ELK.
- Ограничение ресурсов в вашем файле Compose, чтобы предотвратить голод других контейнеров:
services:
api:
deploy:
resources:
limits:
cpus: '0.50'
memory: 256M
reservations:
cpus: '0.25'
memory: 128M
Эти ограничения соблюдаются при использовании роя Docker; для обычного композитного материала они соблюдаются с помощью (хотя лучше использовать рой или K8 для оркестровки производства).
Заключение
Docker Compose и Nginx вместе образуют мощную, масштабируемую и поддерживаемую платформу для развертывания многоконтейнерных веб-приложений. Определяя ваш стек в одном файле YAML, вы достигаете паритета среды от разработки до производства. Nginx, как обратный прокси, обеспечивает центральную точку для SSL терминации, маршрутизации трафика, балансировки нагрузки и кэширования. Мы рассмотрели основные компоненты: определения услуг, сети, объемы, переменные среды, конфигурацию Nginx с оптимизацией безопасности и производительности, советы по масштабированию и устранению неполадок. С помощью этого фундамента вы готовы создавать, развертывать и эксплуатировать современные контейнеризованные приложения с уверенностью. Для дальнейшего чтения обратитесь к документации [FLT: 1] и [FLT: 2]]Nginx [[FLT: 3]] и изучите реальные примеры из таких как [FLT: 4]] Awesome Compose [[FLT: 5]] для вдохновения.