フロントエンド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にバンドルされているという事実により、これはさらに困難になります。
これに取り組む方法はありますか?
これを解決した方法は次のとおりです。
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
おそらく、縮小されたファイルで通常の表現を使用する必要のない、より良いソリューションが存在しますが、これはうまく機能します。
環境変数を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アプリと同じことができます!
方法は?
悪臭を放つ/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>
このアプローチについて非常に強く感じているので、専用のWebサイトを作成しました: https://immutablewebapps.org 。他にも多くの利点があると思います!
~~~
これで、バージョン管理された静的アセット用とindex.html
(ルーティングを非常にシンプルにします:サーブindex.html
すべてのパスに対して)。あなたが提案しているようなコンテナの実行はまだしていません。コンテナを使用する場合は、建物と新しいアセットの公開を明確に分離し、新しいindex.html
。たぶん私はindex.html
コンテナの環境変数を含むテンプレートからオンザフライで。
このアプローチを選択した場合、どうなるか知りたいです!
私は同じ問題に苦労していましたが、構成値をdocker-composeレベルからAngularに渡す必要もありましたが、それは簡単ではありませんでした。
基本的に、私は同様のアプローチを取り、次のソリューションを提供しました:
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
Dockerfile
で変数としてマークする必要があります:ARG AUTH_SERVER_URL ARG GAME_SERVER_URL ARG GUI_SERVER_URL
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" };
出来上がり。これが誰かを助けることを願っています:)
静的なHTMLファイルでも同様の問題が発生しましたが、ここで解決したいことがあります。
私は他の答えを試しましたが、それらは上記に当てはまらないようです。これが私が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
...
私の解決策:実行時に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