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.html
window.__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.html
window.__ENV__={}
на:
window.__ENV__={"PUBLIC_API_URL":"https://astral.ru"};
- Подменяет переменные для nginx на их значения. Читай об этом подробнее здесь
- Запускает nginx