Как запустить веб-приложение на Nginx в Docker

Nginx – это хорошо известный высокопроизводительный HTTP-сервер и обратный прокси-сервер. Nginx предоставляет балансировку нагрузки, кэширование, поддержку SSL/TLS, журналирование и ротацию логов. В статье мы будем использовать Nginx для сервера одностраничного приложения (SPA). На этом примере мы поймём, как создавать и запускать Docker-образ с веб-приложением и NGINX.
Небольшие приготовления: создаём веб-приложение
Для начала нам нужно какое-то веб-приложение, которое будет обслуживаться сервером Nginx. Вы можете использовать любой JavaScript-фреймворк, в нашем примере мы используем демо-проект Angular:
ng new angular-nginx-docker --minimal cd angular-nginx-docker ng build --prod
В результате выполнения указанных команд будет создано новое приложение Angular, находящееся в папке $PWD/dist/angular-nginx-docker. Этот каталог содержит файл index.html и несколько типичных для одностраничного приложения JavaScript и CSS файлов. Приложение, весь код и команды из этой статьи находятся в GitHub-репозитории.
Запускаем приложение на Nginx в Docker-контейнере
Nginx и Docker на удивление хорошо работают вместе. Вместо того чтобы устанавливать Nginx на нашу машину напрямую, мы будем запускать его в Docker на примере nginx:alpine.
Образ nginx:alpine очень производителен, хотя занимает лишь около 20 Мб дискового пространства. Для интерактивного запуска образа nginx: alpine в контейнере мы выполняем следующую команду:
run-nginx-docker-image.sh:
docker run -it -p 80:80 \ -v /$PWD/dist/angular-nginx-docker://usr/share/nginx/html:ro \ nginx:alpine
Приведённая команда монтирует том в Docker-контейнер в режиме read only (устанавливается с помощью флага: ro). Том отображает наше приложение в дефолтную корневую папку веб-контента Nginx. Команда также сопоставляет порт 80 в контейнере с портом 80 в хосте, чтобы мы могли увидеть наше приложение на http://localhost. Когда клиент запрашивает содержимое приложения, мы видим в консоли логи Nginx.
Без дополнительных настроек Nginx использует дефолтные файлы конфигурации веб-сервера. Если вам интересно, вы можете использовать следующие команды для их изучения в Docker-образе:
docker run -it nginx:alpine sh cd etc/nginx/
Исходная конфигурация NGINX уже может обслуживать наше приложение. Но мы можем оптимизировать веб-сервер, создав папку .nginx и поместив в нее конфигурационный файл nginx.conf со следующим содержимым:
nginx.conf
# Запускать в качестве менее привилегированного пользователя по соображениям безопасности.. user nginx; # Значение auto устанавливает число максимально доступных ядер CPU, # чтобы обеспечить лучшую производительность. worker_processes auto; events { worker_connections 1024; } http { server { # Hide nginx version information. server_tokens off; listen 80; root /usr/share/nginx/html; include /etc/nginx/mime.types; location / { try_files $uri $uri/ /index.html; } gzip on; gzip_vary on; gzip_http_version 1.0; gzip_comp_level 5; gzip_types application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/plain text/x-component; gzip_proxied no-cache no-store private expired auth; gzip_min_length 256; gunzip on; } }
Каталог .nginx используется для организации файлов конфигурации для Nginx. По большей части файл конфигурации не требует пояснений, стоит упомянуть лишь о двух моментах:
1. Инструкция try_files используется для определения запасных маршрутов, когда у сервера запрашивается неизвестный ему файл. Конфигурационные инструкции try_files обычно можно увидеть в веб-приложениях с паттерном Front Controller.
2. Во второй половине файла описывается gzip-сжатие и распаковка HTTP-ответов.
Мы также можем использовать другие параметры конфигурации, которые повысят производительность, безопасность и масштабируемость Nginx. К примеру, для реальных приложений в файле nginx.conf понадобится настроить SSL-сертификат. Чтобы узнать больше о конфигурациях Nginx, ознакомьтесь с руководством для новичков в Nginx.
Чтобы Nginx мог использовать настроенный файл nginx.conf, запускаем следующую команду:
run-nginx-docker-image.sh:
docker run -it -p 80:80 \ -v /$PWD/dist/angular-nginx-docker://usr/share/nginx/html:ro \ -v /$PWD/.nginx/nginx.conf://etc/nginx/nginx.conf:ro \ nginx:alpine
Эта команда подключает два read-only-тома к контейнеру Docker. Один том соответствует нашему приложению с контейнером, а другой – сопоставит файл nginx.conf в качестве файла конфигурации.
Как понять, что Nginx использует наш файл nginx.conf? Достаточно проверить заголовок ответа HTTP. На следующем скриншоте показано сравнение заголовков ответов при использовании конфигураций по умолчанию (слева) и пользовательской конфигурации (справа).
Хэдеры ответов при использовании конфигураций по умолчанию (слева) и кастомной конфигурации. Жёлтым выделены различия.
Когда Nginx использует кастомный файл конфигурации, HTTP-ответ скрывает номер версии Nginx и кодирует содержимое в режиме gzip.
Докеризация веб-приложения с Nginx
В предыдущем разделе мы научились работать с нашим одностраничным приложением, используя Nginx в контейнере. Мы также можем отправлять наше приложение вкупе с Nginx в виде Docker-образа в контейнеры других сред. Контейнерные приложения дают ряд преимуществ: простота обновления, локализация сбоев, простая смена технологии. Так что теперь мы создадим Docker-образ, содержащий и веб-приложение, и сервер Nginx, который при запуске Docker-образа будет его автоматически обслуживать.
Чтобы получить Docker-образ, создаём следующий Dockerfile:
Dockerfile:
# 1. Создаем приложение Angular FROM node:alpine as builder WORKDIR /app COPY package.json package-lock.json ./ ENV CI=1 RUN npm ci COPY . . RUN npm run build -- --prod --output-path=/dist # 2. Развертываем приложение Angular на NGINX FROM nginx:alpine # Заменяем дефолтную страницу nginx соответствующей веб-приложению RUN rm -rf /usr/share/nginx/html/* COPY --from=builder /dist /usr/share/nginx/html COPY ./.nginx/nginx.conf /etc/nginx/nginx.conf ENTRYPOINT ["nginx", "-g", "daemon off;"]
Этот Dockerfile включает две фазы создания Docker-образа. Сначала он стягивает образ node:alpine для создания приложения Angular, которое публикуется по выходному пути /dist. Далее он переключается на образ nginx:alpine, заменяет дефолтный корень приложения Nginx приложением Angular и копирует кастомный файл nginx.conf в систему Nginx.
В последней строке указывается точка входа образа для команды: nginx -g daemon off;. Это гарантирует, что Nginx останется «на переднем плане», так что Docker сможет правильно отслеживать процесс (в противном случае контейнер остановится сразу после запуска).
Чтобы избежать во время сборки копирование лишних файлов из папки node_modules, можно добавить файл .dockerignore.
С помощью описанного Dockerfile мы можем создать образ Docker и запустить его в контейнере, используя следующие команды:
docker build -t angular-nginx-docker . docker run -d -p 80:80 angular-nginx-docker
Готово! В этой статье мы продемонстрировали, как обслуживать простые веб-приложения с использованием Nginx в контейнере, кратко рассказали о файле конфигурации для Nginx, а также о том, как можно вместе упаковать в один образ и приложение сервера, и frontend-часть.