docker 如何将环境变量传递给前端Web应用程序?

dba5bblo  于 2023-03-29  发布在  Docker
关注(0)|答案(6)|浏览(157)

我正在尝试容器化一个前端web应用程序,我遇到了麻烦,弄清楚如何传递环境变量。该应用程序是一个Angular应用程序,所以它是100%客户端的。
在一个典型的后端服务中,传递环境变量很容易,因为所有的东西都在同一个主机上运行,所以环境变量可以很容易地被后端服务选择。然而,在前端应用程序中,这是不同的:应用程序在客户端的浏览器中运行。
我想通过环境变量配置我的应用程序,因为这使部署更加容易。所有配置都可以在docker-compose.yml中完成,并且不需要维护多个映像,每个可能的环境都有一个映像。只有一个不可变的映像。这遵循了12因素应用程序哲学,可以在https://12factor.net/config上找到。
我正在构建我的应用程序映像如下:

FROM node:alpine as builder
COPY package.json ./
RUN npm i && mkdir /app && cp -R ./node_modules ./app
WORKDIR /app
COPY . .
RUN $(npm bin)/ng build

FROM nginx:alpine
COPY nginx/default.conf /etc/nginx/conf.d/
RUN rm -rf /usr/share/nginx/html/*
COPY --from=builder /app/dist /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]

app/config.ts中,我有:

export const config = {
    REST_API_URL: 'http://default-url-to-my-backend-rest-api'
};

理想情况下,我想在我的docker-compose.yml中做这样的事情:

backend:
  image: ...
frontend:
  image: my-frontend-app
  environment:
    - REST_API_URL=http://backend:8080/api

所以我认为我应该修改这个app/config.ts,用环境变量替换REST_API_URL。(所以我不想在构建过程中做这个替换),我很困惑如何在这里进行。我相信我应该支持在nginx代理启动之前在运行时更改app/config.ts。然而,事实上,这个文件是缩小和webpack-bundled,使这更加diffucult。
有什么办法解决这个问题吗?

ruyhziif

ruyhziif1#

我解决这个问题的方法如下:
1.在environment.prod.ts中使用唯一且可识别的String设置值:

export const environment = {
  production: true,
  REST_API_URL: 'REST_API_URL_REPLACE',
};

2.创建一个entryPoint.sh,每当您对容器执行Docker运行时,都会执行此entryPoint。

#!/bin/bash
set -xe
: "${REST_API_URL_REPLACE?Need an api url}"

sed -i "s/REST_API_URL_REPLACE/$REST_API_URL_REPLACE/g" /usr/share/nginx/html/main*bundle.js

exec "$@"

如您所见,此入口点获取'REST_API_URL_REPLACE'参数,并将其替换(在本例中)到main*bundle.js文件中,以获取var的值。
3.在entrypoint.shdockerfile中的CMD之前添加www.example.com(需要执行权限):

FROM node:alpine as builder
COPY package.json ./
RUN npm i && mkdir /app && cp -R ./node_modules ./app
WORKDIR /app
COPY . .
RUN $(npm bin)/ng build --prod

FROM nginx:alpine
COPY nginx/default.conf /etc/nginx/conf.d/
RUN rm -rf /usr/share/nginx/html/*
COPY --from=builder /app/dist /usr/share/nginx/html

# Copy the EntryPoint
COPY ./entryPoint.sh /
RUN chmod +x entryPoint.sh

ENTRYPOINT ["/entryPoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

4.使用env启动镜像或使用docker-compose(斜杠必须转义):

docker run -e REST_API_URL_REPLACE='http:\/\/backend:8080\/api'-p 80:80 image:tag

可能存在一个更好的解决方案,不需要在缩小的文件中使用常规表达式,但这工作得很好。

tvokkenx

tvokkenx2#

将您的环境变量放入index.html!!

相信我,我知道你从哪里来的!在我的Angular应用程序的构建阶段烘焙特定于环境的变量违背了我所学到的关于可移植性和关注点分离的一切。
但是等等!仔细看看常见的Angular index.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>mysite</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="stylesheet" href="https://assets.mysite.com/styles.3ff695c00d717f2d2a11.css">
  <script>
  env = {
    api: 'https://api.mysite.com/'
  }
  </script>
</head>
<body>
  <app-root></app-root>
  <script type="text/javascript" src="https://assets.mysite.com/runtime.ec2944dd8b20ec099bf3.js"></script>
  <script type="text/javascript" src="https://assets.mysite.com/polyfills.20ab2d163684112c2aba.js"></script>
  <script type="text/javascript" src="https://assets.mysite.com/main.b55345327c564b0c305e.js"></script>
</body>
</html>

这是全部配置!!!

它就像你用来维护Docker应用的docker-compose.yml一样:

  • 版本化不可变资源
  • 环境变数
  • 应用程序绑定
  • 环境元数据
  • 即使是不同的捆绑包感觉像一个 Docker 形象排序层,不是吗?
    • runtime就像基本映像,您很少更改。*
    • polyfills是您需要的那些东西,它们没有包含在您需要的基础映像中。
    • main是您的实际应用程序,几乎每次发布都会发生变化。
      您可以在前端应用中执行与Docker应用相同的操作!

1.构建、版本化和发布不可变资产(js bundles / Docker镜像)
1.将部署清单发布到staging(index. html/ docker-compose.yml)
1.分期试验
1.将部署清单发布到生产环境..引用您刚刚测试的相同资产!立即!原子化!
怎么会
只需将发臭的/src/environments/environment.prod.ts指向window对象。

export const environment = (window as any).env;
// or be a rebel and just use window.env directly in your components

并使用环境变量**WHERE THEY BELONG!**将脚本添加到index.html中:

<script>
  env = { api: 'https://api.myapp.com' }
</script>

我对这种方法非常感兴趣,我创建了一个专门的网站:https://immutablewebapps.org.我想你会发现还有很多其他的好处!

现在,我已经使用两个AWS S3 Buckets成功地做到了这一点:一个用于版本化的静态资产,另一个仅用于`index.html`(这使得路由超级简单:为每个路径提供`index.html`)。我还没有像你建议的那样运行容器。如果我使用容器,我希望在构建和发布新资产以及发布新的`index.html`之间进行清晰的分离。也许我会使用容器的环境变量从模板中动态渲染`index.html`。
如果你选择这种方法,我很想知道结果如何!
rekjcdws

rekjcdws3#

我在处理静态HTML文件时遇到了类似的问题,下面是我想要解决的问题:

  • 环境变量的数量可以缩放
  • 可以在启动时而不是在构建时设置环境变量
  • 不用担心维护变量替换的格式,这样它就不会与其他文本冲突

我尝试了其他答案,但似乎它们不符合上面的要求。
Dockerfile

FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY . /usr/share/nginx/html
EXPOSE 80

# awkwardly replace env variables
COPY ./replaceEnvVars.sh /
RUN chmod +x replaceEnvVars.sh
ENTRYPOINT ["./replaceEnvVars.sh"]
CMD ["nginx", "-g", "daemon off;"]

replaceEnvVars.sh

#!/bin/sh

envsubst < /usr/share/nginx/html/index.tmpl.html > /usr/share/nginx/html/index.html && nginx -g 'daemon off;' || cat /usr/share/nginx/html/index.html

index.tmpl.html

<html>
 ...
 <script>
 gtag('config', '${GA_CODE}');
 </script>
 ...
 <a href="${BASE_URL}/login" class="btn btn-primary btn-lg btn-block">Login</a>
</html>

docker-compose.yml

version: '3'
services:
  landing:
    build: .
    ...
    environment:
      - BASE_URL=https://dev.example.com
      - GA_CODE=UA-12345678-9
    ...
7kjnsjlb

7kjnsjlb4#

我也在努力解决同样的问题,但还需要将配置值从docker-compose级别传递到Angular,我觉得这并不简单。
基本上,我采取了类似的方法,并提出了以下解决方案:
1.我使用compose ARGS将所需的值从docker-compose.yml传递到Dockerfile。因此在docker-compose.yml中我有:
magicsword.core.web: build: args: - AUTH_SERVER_URL=http://${EXTERNAL_DNS_NAME_OR_IP}:55888/ - GAME_SERVER_URL=http://${EXTERNAL_DNS_NAME_OR_IP}:55889/ - GUI_SERVER_URL=http://${EXTERNAL_DNS_NAME_OR_IP}:55890/ # =self
1.现在必须在Dockerfile中将这些标记为变量:
ARG AUTH_SERVER_URL ARG GAME_SERVER_URL ARG GUI_SERVER_URL
1.由于在构建过程中这些变量变成了正常的环境变量,最后一步是在目标文件中进行实际的替换,例如使用一些神奇的一行程序。我做了如下(只是一个宠物项目,所以不需要是最佳的):
RUN apt-get update && apt-get install -y gettext RUN envsubst < ./src/environments/environment.ts > ./src/environments/environment.ts.tmp && mv ./src/environments/environment.ts.tmp ./src/environments/environment.ts
替换前的environment.ts,供参考:
export const environment = { production: true, GAME_SERVER_URL: "$GAME_SERVER_URL", GUI_SERVER_URL: "$GUI_SERVER_URL", AUTH_SERVER_URL: "$AUTH_SERVER_URL" };
瞧。希望这对某人有帮助:)

qvsjd97n

qvsjd97n5#

我的解决方案:在运行时使用docker卷来挂载特定的js配置文件作为env.js。
我有一个docker compose文件给dev和prod。
我有dev.env.js和prod.env.js。
我的html文件引用env.js。
在docker-compose.yml中,I卷将env文件挂载为env.js。
例如,我的开发人员组成:

web:
    image: nginx
    ports:
      - 80:80
    volumes:
      - ../frontend:/usr/share/nginx/html
      - ../frontend/dev.env.js:/usr/share/nginx/html/env.js

我的刺组成:

web:
    image: nginx
    ports:
      - 80:80
    volumes:
      - ../frontend:/usr/share/nginx/html
      - ../frontend/prod.env.js:/usr/share/nginx/html/env.js
dy1byipe

dy1byipe6#

我只是在使用Docker和DotEnv webpack插件时遇到了这个问题。我想在多个环境中使用相同的镜像,所以我不得不延迟webpacking过程,直到镜像使用环境特定的ENV运行。
我通过创建start_app. sh并在入口点中运行它来做到这一点:
start_app.sh:

#!/bin/bash
npm run build
npm start

exec "$@"

dockerfile结束:

# Use ENV on docker run
RUN chmod +x start-app.sh
ENTRYPOINT ["./start-app.sh"]

docker命令:

docker run --env REST_API_URL=testURL  -p 9900:9900 app:0.0.0

相关问题