Стены статики: NGINX
Просыпайтесь, работяги, время доставлять штимельки пользователям!
![](images/23202624-ddad-4715-97c7-936b06437699.png)
Не вижу смысла долго мариновать тебя статьями про обслуживание приложений, пока мы не умеем их разворачивать, поэтому сегодня поговорю про один из самых популярных серверов для отдачи статики — NGINX (произносится как Engine X, в простонародье НЖЫНКС).
Перед прочтением
- Это — первая статья из серии по NGINX. Здесь мы посмотрим на совсем простенькие примеры, которые помогут нам разворачивать фронтендовые приложения;
- По ходу статьи я буду использовать Docker. Если ты с ним ещё не знаком, то вот статья, которой будет достаточно, чтобы понимать происходящее;
- Nginx — далеко не единственный способ отдавать статику пользователям, однако это один из самых популярных и гибких инструментов. На другие инструменты мы тоже посмотрим.
Что вообще такое сервер статики и почему NGINX?
Если просто, сервер статики — это приложение, к которому подключается браузер пользователя, чтобы скачать index.html, а затем и сопутствующие ему зависимости. Сервер статики знает, как обработать эти запросы, где найти файлики, за которыми мы пришли, и как их отдать.
NGINX отлично подходит на роль сервера статики (хотя изначально позиционировал себя как реверс-прокси) по нескольким причинам:
- Он быстрый. Реально. Для того, чтобы отдать файл, используется минимальное количество логики, а его архитектура позволяет поддерживать десятки тысяч соединений одновременно;
- Он понятный: как-то повелось, что все девопсовые инструменты конфигурируются близким к английскому языком, и NGINX не стал исключением;
- У него огромное комьюнити: NGINX используется в огромном количестве приложений. Например, это :) А ещё я украл с официального сайта картинку с другими ребятами, которые его используют:
![](images/f6794bca-5321-4238-b6e9-14f2fdc7d57d.png)
Как установить?
Мы не будем устанавливать NGINX на ПК, а будем пользоваться им внутри докера. Докер поможет нам тем, что он хорошо умеет: создавать повторяемые окружения. Мы при желании сможем залезть внутрь контейнера, поломать там конфиги, а потом просто поднять новое, полностью рабочее приложение снова.
У NGINX есть официальный образ в Docker Hub, его и запустим с помощью docker run -p 80:80 nginx. Если мы теперь зайдем на localhost в браузере, то увидим, что NGINX работает, ура!
![](images/640c4c9e-effe-469c-85ef-0970e4d40031.png)
Где настроить?
Главный конфигурационный файл nginx лежит по пути /etc/nginx/nginx.conf, но для того, чтобы начать разбираться в синтаксисе, сначала посмотрим на другой конфиг, который он импортирует — /etc/nginx/conf.d/default.conf:
![](images/2c71db45-c3a6-4eda-bf3b-4bc17afe8330.png)
Конфигурация 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:
![](images/69df3571-b422-4f0f-9b75-8a4ee699c505.png)
Она сделает ровно то, что мы хотим: сначала попробует достать из root файл по uri, с которого мы пришли (например, для localhost/main.css — /usr/share/nginx/html/main.css), а если такого нет — отдаст index.html.
Как развернуть?
Алгоритм сборки нашего фронтового приложения следующий:
- Ставим зависимости с npm i;
- Собираем приложение, например, через npm run build;
- Кладем получившиеся файлы в образ с nginx;
- В этот же образ кладем конфиг, который мы написали выше.
Получается несложный докерфайл:
![](images/66a33b2f-930a-4e8c-a7c8-e51368a9347e.png)
Из того, что ты мог ещё не видеть, в нём используется две конструкции FROM: для ноды и для nginx. Рассказываю: в этом случае наш итоговый контейнер будет использовать образ, собранный из последнего FROM — nginx. Просто до перехода в него нам нужно было собрать приложение в окружении с установленной нодой.
После сборки и запуска нашего контейнера мы должны увидеть почти готовое к жизни в продакшене приложение! Улучшения вроде работы с HTTPS, победы над CORS и кеширования рассмотрим в следующих статьях 💛