12 мая 2020

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

Пишу, перевожу и иллюстрирую IT-статьи. На proglib написал 140 материалов. Увлекаюсь Python, вебом и Data Science. Открыт к диалогу – ссылки на соцсети и мессенджеры: https://matyushkin.github.io/links/ Если понравился стиль изложения, упорядоченный список публикаций — https://github.com/matyushkin/lessons
Инструкция по настройке совместной работы веб-приложения и сервера Nginx в Docker-контейнере, а также о том, как создать их общий Docker-образ для использования в других контейнерах.
Как запустить веб-приложение на Nginx в Docker 🐳👨🏽‍💻

Nginx – это хорошо известный высокопроизводительный HTTP-сервер и обратный прокси-сервер. Nginx предоставляет балансировку нагрузки, кэширование, поддержку SSL/TLS, журналирование и ротацию логов. В статье мы будем использовать Nginx для сервера одностраничного приложения (SPA). На этом примере мы поймём, как создавать и запускать Docker-образ с веб-приложением и NGINX.

Docker?
Если вы ещё не знакомы с Docker или у вас нет уверенности в своих знаниях, прочитайте нашу вводную статью «Что такое Docker, и как его использовать?»

Небольшие приготовления: создаём веб-приложение

Для начала нам нужно какое-то веб-приложение, которое будет обслуживаться сервером 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-часть.

***

Читайте также другие наши статьи о технологии Docker:

Больше полезной информации вы можете получить на нашем телеграм-канале «Библиотека программиста».

Источники

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик C++
Москва, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ