Русский
Русский
English
Статистика
Реклама

Солидные фронтенды конфигурация

Манифест 12-факторных приложений внес весомый вклад в процесс разработки и эксплуатации веб-приложений, но это по-большей части коснулось бекендов, и обошло стороной фронтенды. Большенство пунктов манифеста или не применимы к фронтендам, или выполняются сами собой, но с номером 3 конфигурация есть вопросики.

В оригинальном манифесте сказано: Сохраняйте конфигурацию в среде выполнения. На практике это означает, что конфигурацию нельзя хранить внутри исходного кода или финального артефакта. Ее нужно передавать в приложение по время запуска. У этого правила есть практические применения, вот парочка:

  1. Приложение в разных окружениях должно обращаться к разным бекендам. На продакшене к продакшн API, на стейдженге к стейджинг API, а при запуске интеграционных тестов к специальному мок-серверу.

  2. Для е2е тестов нужно снижать время ожидания реакции пользователя. Например, на если на сайте после 10 минут бездействия что-то происходит, то для тестового сценария можно уменьшить этот интервал до минуты.

SSR

Если фронтенд-приложение содержит в себе SSR, то задача становится чуточку легче. Конфигурацию передают как переменные окружения в приложение на сервере, при рендере она попадает в ответ клиенту как глобальные переменные, объявленные в <script> в самом начале страницы. На клиенте же достаточно эти переменные подхватывать из глобальной области видимости и использовать.

Недавно мы в Авиасейлс делали приложение для серверного рендеринга кусочков сайта и столкнулись с этой задачей. Результат мой тиммейт заопенсорсил isomorphic-env-webpack-plugin.

Прекрасный Next.js умеет так из коробки, ничего специально делать не нужно.

CSR

Другое дело, если приложение набор статических файлов. Переменные окружения нельзя передать на клиент, ведь мы не контролируем сервер, а все файлы уже заранее сгенерированы и лежат на диске. В таком случае, на этот фактор часто забивают.

Два самых популярных способа хранить конфигурацию фронтенд приложения с клиентским рендерингом:

  1. Положить к исходным текстам завести в коде файлик config.js, и сложить в него параметры. Это работает, но некоторые крайние случаи могут сильно испортить жизнь. Часто в таких файлах появляется проверка если текущий хост такой-то, то ходить на дев-бекенд, если хост другой ходить на прод-бекенд. Такой подход плохо дружит с переменным количеством окружений когда на каждый PR поднимается новый стенд.

  2. Передавать при сборке артефакта. Обычно это делают через DefinePlugin для Webpack. Этот подход лучше первого для каждого окружения можно собирать отдельный артефакт и добавлять туда специфичные параметры. Возникает проблема на продашкн поедет не тот же артефакт, что тестировался. Технически, нет гарантии, что в разных окружениях версия будет работать одинаково. Иногда это приводит к печальным последствиям.

Есть несколько способов получше.

Прокси-сервер

Подавляющее большенство фронтенд-приложений подчиняются двум критериям:

  1. Файлы ресурсов раздаются через nginx, либо перед приложением стоит nginx как реверс-прокси. Тут nginx можно заменить на любой аналог.

  2. Единственная конфигурация, необходимая приложению адреса разных API.

Для таких приложений проблему конфигурации можно решить так в клиентском коде все запросы отправить на текущий домен, а на стороне nginx роутить эти запросы в конкретные бекенды. Будем отправлять запросы на /user-api/path вместо https://user.my-service.io/path, на /auth-api/path вместо https://auth.other-service.io/path и так далее.

Дальше инструкция специфична для nginx в Docker-контейнере

Начиная с версии 1.19 официальный Docker-образ nginx умеет использовать переменные окружения в конфигурационных файлах. Для этого нужно создать файл конфигурации с суффиксом .template и поместить его в директорию /etc/nginx/templates. При старте сервер подхватит переменные окружения, пройдёт по шаблонам и создаст финальные файлы конфигурации.

Типичная конфигурация nginx для SPA будет выглядеть так:

server {  listen   8080;  root /srv/www;  index index.html;  server_name _;  location /user-api {    proxy_pass ${USER_API_URL};  }  location /auth-api {    proxy_pass ${AUTH_API_URL};  }  location / {    try_files $uri /index.html;  }}

Dockerfile в этом случае будет примерно таким:

FROM node:14.15.0-alpine as buildWORKDIR /app# сборка фронтенд ассетов# ...FROM nginx:1.19-alpineCOPY ./default.conf.template /etc/nginx/templates/default.conf.templateCOPY --from=build /app/public /srv/wwwEXPOSE 8080

Теперь достаточно запустить контейнер и передать переменные окружения, на основе которых nginx создаст конфигурационные файлы.

Так, косвенным образом, фронтенд приложение получит параметры в момент запуска.

Живой пример можно посмотреть в этом проекте.

Такую схему можно реализовать и с другими серверами. Caddy поддерживает переменные окружения в конфигурационных файлах из коробки, а Traefik умеет в динамические конфигурации.

Если в приложении конфигурация не исчерпывается путями до API, вариант с прокси-сервером для хранения параметров не подходит.

Генерация файла с конфигурацией

Проделав этот путь можно пойти дальше и генерировать конфигурационные файлы не для прокси-сервера, а напрямую для фронтенда. Это позволит передавать любые параметры, не зависеть от способа раздачи статических файлов и места их хранения.

При старте приложения можно подхватывать переменные окружения и записывать их в JS-файл:

window.__ENV__ = {  USER_API_URL: 'https://user.my-service.io/',  AUTH_API_URL: 'https://auth.other-service.io/',};

А потом раздавать этот файл тем же способом, что и остальные статические файлы. На клиенте забирать параметры из этой глобальной переменной и использовать в приложении. Дополнительно нужно будет добавить <script> в HTML-страницу с приложением.

Дальше инструкция специфична для nginx в Docker-контейнере

Важно отметить, что отправлять все переменные окружения на клиент может быть опасно, часто в них хранится приватная информация ключи доступа до API, пароли и токены. Поэтому, лучше явно перечислить имена переменных, которые будут отправлены в браузер в файле env.dict:

BACK_URLGOOGLE_CLIENT_ID

Теперь простым Bash-скриптом generate_env.sh будем доставать значения из окружения и складывать в JS-файл:

#!/bin/bashfilename='/etc/nginx/env.dict'# Начало JS-файлаconfig_str="window._env_ = { "# Конкатенируем переменную в JS-файлwhile read line; dovariable_str="${line}: \"${!line}\""config_str="${config_str}${variable_str}, "done < $filename# Конец JS-файлаconfig_str="${config_str} };"# Сохраняем файл на дискecho "Creating config-file with content: \"${config_str}\""echo "${config_str}" >> /srv/www/config.env.js# Добавляем <script> в конец всех HTML-файловsed -i '/<\/body><\/html>/ i <script src="http://personeltest.ru/aways/habr.com/confit.env.js"></script>' *.html

Я не большой знаток Bash, вероятно получилось странно. Этот скрипт призван показать общую идею, а не использоваться в проекте напрямую.

Теперь при старте контейнера вместо запуска nginx нужно выполнить этот скрипт, а потом уже запустить nginx. Заведём точку входа cmd.sh, которая сделает это:

#!/bin/bashbash /etc/nginx/generate_env.shnginx -g "daemon off;"

И теперь немного подправим Dockerfile:

FROM node:14.15.0-alpine as buildWORKDIR /app# сборка фронтенд ассетов# ...FROM nginx:1.19-alpine# В стандартной поставке Alpine нет Bash, установим егоRUN apk add bashCOPY ./default.conf /etc/nginx/conf.d/COPY --from=build /app/public /srv/wwwCOPY ./cmd.sh /etc/nginx/cmd.shCOPY ./generate_env.sh /etc/nginx/generate_env.shCOPY ./env.dict /etc/nginx/env.dict EXPOSE 8080CMD ["bash", "/etc/nginx/cmd.sh"]

После этих манипуляций можно передавать на фронтенд любые параметры через переменные окружения нужно отметить переменную в env.dict и передать ее при запуске контейнера.

Живой пример можно посмотреть в этом проекте.

Если внимательно посмотреть на этот вариант, станет понятно, что он почти не отличается от варианта с SSR приведённого в начале статьи. Для удобства можно воспользоваться isomorphic-env-webpack-plugin, и дописать пару скриптов: генерации файла и вставки ссылки на него в HTML.

В этой схеме есть еще один небольшой эдж-кейс обычно в имена файлов с ресурсами добавляют хеш содержимого, чтобы в браузере без проблем кешировать все файлы навсегда по имени. В таком случае нужно немного усложнить скрипт генерации файла с переменными, хешировать содержимое и добавлять результат в имя файла.

Заключение

Правильная работа с параметрами фронтенд-приложения помогает создавать надежные и удобные в эксплуатации системы. Достаточно отделить конфигурацию от приложения и перенести ее в среду выполнения, чтобы радикально улучшить комфорт членов команды и снизить количество потенциальных ошибок.

А как вы передаёте конфиги в клиентские приложения?

Источник: habr.com
К списку статей
Опубликовано: 08.02.2021 10:22:45
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Разработка веб-сайтов

Javascript

Nginx

Фронтенд

Конфигурация

Сайт

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru