web-dev-qa-db-ja.com

環境変数をフロントエンドWebアプリケーションに渡す方法は?

フロントエンドWebアプリケーションをコンテナ化しようとしていますが、環境変数を渡す方法を見つけるのに苦労しています。アプリケーションはAngularアプリケーションなので、100%クライアント側です。

典型的なバックエンドサービスでは、すべてが同じホストで実行されているため、環境変数を渡すのは簡単です。したがって、バックエンドサービスは環境変数を簡単に選択できます。ただし、フロントエンドアプリケーションでは、これは異なります。アプリケーションはクライアントのブラウザーで実行されます。

環境変数を使用してアプリケーションを構成すると、デプロイメントがはるかに簡単になります。すべての構成はdocker-compose.ymlで行うことができ、可能な環境ごとに1つずつ、複数のイメージを維持する必要はありません。不変のイメージは1つだけです。 https://12factor.net/config にあるように、これは12因子アプリケーションの哲学に従っています。

次のようにアプリケーションイメージを構築しています。

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を環境変数に置き換える必要があると思います。私は不変のDockerイメージを好むので(ビルド中にこの置換を行いたくないので)、ここでどのように進行するのか非常に困惑しています。 nginxプロキシを起動する前に、実行時にapp/config.tsの変更をサポートする必要があると思います。ただし、このファイルが縮小されてwebpackにバンドルされているという事実により、これはさらに困難になります。

これに取り組む方法はありますか?

20
dockernodejs

これを解決した方法は次のとおりです。

1. enviroment.prod.tsの値を一意で識別可能な文字列で設定します。

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

2. entryPoint.shを作成します。このentryPointは、コンテナーのdocker runを実行するたびに実行されます。

#!/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. CMDの前にdockerfileにentrypoint.shを追加します(実行権限が必要です):

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

おそらく、縮小されたファイルで通常の表現を使用する必要のない、より良いソリューションが存在しますが、これはうまく機能します。

21
Daniel Caldera

環境変数をindex.html !!

私を信じて、あなたがどこから来たのか知っています!環境固有の変数をmy Angular appのビルドフェーズに組み込むと、移植性と懸念の分離について学んだことすべてに反します。

ちょっと待って!一般的な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と同じです。

  • バージョン管理された不変資産
  • 環境変数
  • アプリケーションバインディング
  • 環境メタデータ
  • 異なるバンドルでさえ、ドッカー画像のレイヤーのように感じますよね?
    • runtimeは、めったに変更しないベースイメージのようなものです。
    • polyfillsは、必要なベースイメージに含まれていない必要なものです。
    • mainは、リリースごとにほとんど変化する実際のアプリです。

フロントエンドアプリでもDockerアプリと同じことができます!

  1. 不変アセットのビルド、バージョン化、公開(jsバンドル/ Dockerイメージ)
  2. 展開マニフェストをステージングに公開します(index.html/docker-compose.yml)
  3. ステージングでテストする
  4. 展開マニフェストを運用環境に公開します。テストしたばかりの同じアセットを参照します。すぐに!原子的に!

方法は?

悪臭を放つ/src/environments/environment.prod.tswindowオブジェクト。

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>

このアプローチについて非常に強く感じているので、専用のWebサイトを作成しました: https://immutablewebapps.org 。他にも多くの利点があると思います!

~~~

これで、バージョン管理された静的アセット用とindex.html(ルーティングを非常にシンプルにします:サーブindex.htmlすべてのパスに対して)。あなたが提案しているようなコンテナの実行はまだしていません。コンテナを使用する場合は、建物と新しいアセットの公開を明確に分離し、新しいindex.html。たぶん私はindex.htmlコンテナの環境変数を含むテンプレートからオンザフライで。

このアプローチを選択した場合、どうなるか知りたいです!

5
Gene C

私は同じ問題に苦労していましたが、構成値を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" };

出来上がり。これが誰かを助けることを願っています:)

2

静的なHTMLファイルでも同様の問題が発生しましたが、ここで解決したいことがあります。

  • env変数の数はスケーリングできます
  • env変数は、ビルド時ではなく起動時に設定できます
  • 他のテキストと衝突しないように変数置換の形式を維持することを心配しない

私は他の答えを試しましたが、それらは上記に当てはまらないようです。これが私がenvsubstを使用することになったものです

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
    ...
2
tsuz

私の解決策:実行時にdockerボリュームを使用して、特定のjs構成ファイルをenv.jsとしてマウントします。

Devとprodのdocker composeファイルがあります。

Dev.env.jsとprod.env.jsがあります。

私のhtmlファイルはenv.jsを参照しています。

Docker-compose.ymlで、いずれかの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

そして、私のprodの構成:

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