CSR + Nginx. Динамические env
В данном разделе описана инстру кция загрузки env переменных при использовании CSR и Nginx для раздачи статики.
Пример
Пример реализован в vite-boilerplate.
Проблема получения env в build time
Получение env в build time - это классический подход.
Работает следующим образом:
- Сборщик при запуске получает
- Либо помещает полученные env в
import.meta.env(vite), либо делает replaceprocess.envпеременных на их значения непосредственно в js коде (webpack) - Переменные доступны в браузере при выполнении js
Проблемы:
- Для изменения значений env необходимо заново пересобирать приложение
- Если в приложении есть поддержка white label, то для каждого бренда необходимо собирать отдельный docker image. Это сильно увеличивает время сборки приложения и количество ресурсов, необходимое для поддержки и развертывания
Для решения описанных проблем необходимо делать inject env в html при запуске docker контейнера.
Генерация index.html и inject env
Для того чтобы при изменении env переменных не приходилось заново делать сборку, необходимо при запуске docker контейнера генерировать index.html и инжектировать в html env переменные.
Генерация index.html из index.template.html
В репозитории должен находиться только index.template.html - это шаблон html, на основе которого будет построен index.html.
index.html должен быть добавлен в .gitignore.
Содержимое index.template.html
index.template.html содержит всю необходимую для приложения разметку и <script> в <head>:
<script>
<!-- При запуске dev команды или запуске docker контейнера в __ENV__ подставятся значения из окружения -->
window.__ENV__={};
</script>
Зачем нужен index.template.html и почему index.html в .gitignore?
При запуске dev-server должно происходить inject env переменных в index.html,
если файл не будет в .gitignore, то в репозиторий могут случайно попасть изменения, которых разработчик не делал.
Для генерации и inject env используются bash скрипты.
Bash используется по причине того, что для CSR в компании используется Nginx. Это означает, что в итоговом docker контейнере нет nodejs, а если добавить в образ nodejs, то он вырастет в разы по объему, что повлияет на скорость сборок и оптимизацию хранилища.
Dev Mode
Генерация index.html. generateEnv.dev.sh скрипт
В режиме разработки необходимо, чтобы перед каждым запуском dev-server выполнялся bash скрипт, который:
- Парсит все переменные из файла
.env.local(или.env.dev) с префиксамиPUBLIC_ - Создает в директории для раздачи статики
index.htmlс копией содержимого изindex.template.html. Для vite директория для раздачи статики - это корень приложения. Для webpack - public директория - Заменяет в
index.htmlwindow.__ENV__={}на:
window.__ENV__={"PUBLIC_API_URL":"https://astral.ru"};
Prod Mode
Генерация index.html
Dockerfile
При запуске docker контейнера необходимо запускать bash скрипт, который сгенерирует index.html файл и инжектирует в него env.
Для этого в Dockerfile необходимо добавить команду выполнения скрипта при запуске контейнера:
FROM node:22-alpine AS build
WORKDIR /usr/src/app
COPY package.json package-lock.json* ./
COPY . .
RUN npm i --production
RUN npm run build
FROM fholzer/nginx-brotli:v1.19.1
COPY .nginx/nginx.conf.template /etc/nginx/nginx.conf.template
COPY --from=build /usr/src/app/dist /usr/share/nginx/html
# Запускаем контейнер при помощи exec в shell оболочке, чтобы иметь доступ к env
ENTRYPOINT ["sh", "/usr/share/nginx/html/scripts/startup.prod.sh"]
Скрипт startup.prod.sh
Скрипт startup.prod.sh выполняет следующие действия:
- Достает из текущего окружения все env переменные с префиксом
PUBLIC_ - Создает
index.htmlс копией содержимого изindex.template.html - Заменяет в
index.htmlwindow.__ENV__={}на:
window.__ENV__={"PUBLIC_API_URL":"https://astral.ru"};
- Подменяет переменные для nginx на их значения. Читай об этом подробнее здесь
- Запускает nginx