Стены статики: NGINX
Просыпайтесь, работяги, время доставлять штимельки пользователям!
Не вижу смысла долго мариновать тебя статьями про обслуживание приложений, пока мы не умеем их разворачивать, поэтому сегодня поговорю про один из самых популярных серверов для отдачи статики — NGINX (произносится как Engine X, в простонародье НЖЫНКС).
Перед прочтением
- Это — первая статья из серии по NGINX. Здесь мы посмотрим на совсем простенькие примеры, которые помогут нам разворачивать фронтендовые приложения;
- По ходу статьи я буду использовать Docker. Если ты с ним ещё не знаком, то вот статья, которой будет достаточно, чтобы понимать происходящее;
- Nginx — далеко не единственный способ отдавать статику пользователям, однако это один из самых популярных и гибких инструментов. На другие инструменты мы тоже посмотрим.
Что вообще такое сервер статики и почему NGINX?
Если просто, сервер статики — это приложение, к которому подключается браузер пользователя, чтобы скачать index.html, а затем и сопутствующие ему зависимости. Сервер статики знает, как обработать эти запросы, где найти файлики, за которыми мы пришли, и как их отдать.
NGINX отлично подходит на роль сервера статики (хотя изначально позиционировал себя как реверс-прокси) по нескольким причинам:
- Он быстрый. Реально. Для того, чтобы отдать файл, используется минимальное количество логики, а его архитектура позволяет поддерживать десятки тысяч соединений одновременно;
- Он понятный: как-то повелось, что все девопсовые инструменты конфигурируются близким к английскому языком, и NGINX не стал исключением;
- У него огромное комьюнити: NGINX используется в огромном количестве приложений. Например, это :) А ещё я украл с официального сайта картинку с другими ребятами, которые его используют:
Как установить?
Мы не будем устанавливать NGINX на ПК, а будем пользоваться им внутри докера. Докер поможет нам тем, что он хорошо умеет: создавать повторяемые окружения. Мы при желании сможем залезть внутрь контейнера, поломать там конфиги, а потом просто поднять новое, полностью рабочее приложение снова.
У NGINX есть официальный образ в Docker Hub, его и запустим с помощью docker run -p 80:80 nginx. Если мы теперь зайдем на localhost в браузере, то увидим, что NGINX работает, ура!
Где настроить?
Главный конфигурационный файл nginx лежит по пути /etc/nginx/nginx.conf, но для того, чтобы начать разбираться в синтаксисе, сначала посмотрим на другой конфиг, который он импортирует — /etc/nginx/conf.d/default.conf:
Конфигурация NGINX состоит из двух видов конструкций (директив) — простых и блочных.
Простые директивы — это ключевые слова вместе с опциональными аргументами после них. Заканчиваются они точками с запятой. В нашем файлике это, например, listen 80;, root /usr/share/nginx/html и тому подобное.
Блочные директивы (или контексты) — это ключевые слова, после которых идет блок из инструкций внутри фигурных скобок. У нас это server и два location.
Давай прочитаем наш конфиг. Стартуем мы в контексте server. Директивы listen в начале говорят о том, что мы слушаем подключения по 80 порту (вторая нужна для того, чтобы слушать и ipv6). Директива server_name указывает домен, к которому привязан контекст server. Можно его не указывать, и тогда он будет слушать запросы вне зависимости от хоста.
Ещё внутри server у нас есть два блока location: первый раздает файлы из директории /usr/share/nginx/html:
- на запрос localhost без указания пути отдаст оттуда то, что указано в директиве index — index.html или index.htm, если не найдет html;
- на запрос localhost/something.html отдаст /usr/share/nginx/html/something.html или 404, если такого файла нет
А второй блок location за счет префикса = работает только если мы пойдем по пути localhost/50x.html и отдаст нам /usr/share/nginx/html/50x.html. Это условие попадает и под первый блок, так что я, честно говоря, не знаю, зачем в стандартном конфиге их вынесли в два разных.
Между ними затесалась директива error_page. Можно догадаться, что она делает: для ошибок 500, 502 – 504 переадресовывает нас как раз на 50x.html.
Теперь на файл /etc/nginx/nginx.conf можете посмотреть сами, а неизвестные директивы подглядеть в документации. Ещё важный момент: в главном конфиге есть простые директивы, которые не относятся ни к какой блочной, но на самом деле относятся к главному (main) контексту.
Как настроить?
Если мы имеем SPA (например react/anguar/vue/svelte + любой роутер), то нам нужно отдавать js/css чанки при прямых запросах за ними, а на остальные запросы отдавать index — дальше разрулит роутер.
В nginx это поведение можно реализовать при помощи try_files:
Она сделает ровно то, что мы хотим: сначала попробует достать из root файл по uri, с которого мы пришли (например, для localhost/main.css — /usr/share/nginx/html/main.css), а если такого нет — отдаст index.html.
Как развернуть?
Алгоритм сборки нашего фронтового приложения следующий:
- Ставим зависимости с npm i;
- Собираем приложение, например, через npm run build;
- Кладем получившиеся файлы в образ с nginx;
- В этот же образ кладем конфиг, который мы написали выше.
Получается несложный докерфайл:
Из того, что ты мог ещё не видеть, в нём используется две конструкции FROM: для ноды и для nginx. Рассказываю: в этом случае наш итоговый контейнер будет использовать образ, собранный из последнего FROM — nginx. Просто до перехода в него нам нужно было собрать приложение в окружении с установленной нодой.
После сборки и запуска нашего контейнера мы должны увидеть почти готовое к жизни в продакшене приложение! Улучшения вроде работы с HTTPS, победы над CORS и кеширования рассмотрим в следующих статьях 💛