在Docker容器环境中配置Angular 2 Webpack应用程序

10

我们希望使用Docker镜像在不同的环境中(staging/test, production, ...)部署我们的Angular 2应用程序。

在本地开发时,我们通过http://localhost:8080连接到后端REST API,但是当我们在不同的环境中部署时,我们想使用相同的Docker镜像,并连接到不同的REST API端点

注入配置Docker容器运行时的首选方式是什么?

是否可以通过环境变量来实现这一点?

是否可以通过包含类似内容的纯文本文件来实现这一点

{
    "BASE_URL": "https://api.test.example.com"
}

你是使用 Angular CLI 还是自定义构建的? - Riscie
我们基于 https://github.com/AngularClass/angular2-webpack-starter/ 创建了这个项目。 - Thomas Einwaller
抱歉,我不熟悉如何在webpack-starter repo中使用此功能。我们正在使用的angular-cli具有您描述的选项(环境)。 - Riscie
就我所了解的angular-cli,这些环境是在构建时使用而不是在运行时使用的,我们正在寻找一种在运行时配置应用程序的方法。 - Thomas Einwaller
好的,明白了。抱歉我无法提供帮助。 - Riscie
显示剩余3条评论
3个回答

10

在这篇文章和Twitter上进行了一些讨论后,似乎没有简单的方法可以通过Webpack实现我想要的功能。这些文件仅在运行时作为静态文件提供,并且无法在构建时排除文件并在运行时包含它。

因此,我决定采用我所想到的解决方案/解决方法:在启动Docker容器时更改静态文件。

我创建Docker镜像的方式是:

npm run build:prod
docker build -t angularapp .

我正在使用官方的nginx docker镜像作为我的基础镜像,Dockerfile如下:

FROM nginx:1.11.1

COPY dist /usr/share/nginx/html
COPY run.sh /run.sh

CMD ["bash", "/run.sh"]

run.sh用于通过sed修改配置文件,并在此之后启动nginx:

#!/bin/sh

/bin/sed -i "s|http://localhost:8080|${BASE_URL}|" /usr/share/nginx/html/api.config.chunk.js

nginx -g 'daemon off;'

这使得我能够通过环境变量在我的docker-compose.yml文件中配置BASE_URL(简化版):
version: '2'

services:
  api:
    image: restapi
  frontend:
    image: angularapp
    environment:
      BASE_URL: https://api.test.example.com

通过这个解决方案/变通方法,我可以部署由我的Jenkins工作创建的Docker镜像,以便在所有环境(开发,预发布,生产)中都能使用REST API端点进行配置,只需在启动Docker容器时设置环境变量即可。


我尝试了您在这里提出的所有解决方案,但都失败了,所以您能与我分享您的代码吗?我也应该做同样的事情。 - Chandru

3
这里的最终解决方案完全取决于您的CI / CD工具链的外部依赖项,但是此解决方案可以塑造成几乎任何形式。
第一步:添加类似于https://github.com/motdotla/dotenv这样的内容到您的dependencies中,这将处理您的配置值。根据您的需求,还有其他选择,自己编写也很容易。
根据文档,在您的应用程序中尽早加载配置(全局app.module.ts是我的个人选择,因为我们希望这些配置在全局范围内可用)。
简单来说-基于process.env.NODE_ENV,您将根据堆栈加载不同的配置,并且为了使DX简单,我总是给配置值一个默认值,以便我的开发人员不必费心处理文件。
对于TESTING, STAGING, PRODUCTION - 例如,您要在所使用的任何CI提供程序的环境变量中设置BASE_URL_STAGINGBASE_URL_PRODUCTION
作为CI运行的一部分,并根据git分支,将您的配置值写入.env文件,然后将COPY添加到您的Dockerfile || docker-compose.yml中,以引入您在docker构建期间刚刚编写的环境文件。
在验证完成后,当您推送新的docker镜像时,.env是部署包的一部分,针对您需要的任何特定于环境的端点。

1
这并不是我想要的:我想要一个特定版本的Docker镜像,可以在不同配置的多个环境中部署。:( - Thomas Einwaller
1
传递环境变量的机制并不重要。像dotenv及其相关的webpack插件一样,只需将外部环境变量连接到您的应用程序即可。您可以轻松地将类似于BASE_URL_STAGING的内容传递到docker运行命令中docker run -e 或者在您的docker-compose文件中,像这样..environment: - BASE_URL=${BASE_URL_FROM_ENVIRONMENT} 这是一个由两部分组成的问题。dotenv处理将外部变量传递到您的应用程序中,设置这些变量是特定于工具链的。 - Joshua Wiens
2
我理解,但重要的是我不想为不同的环境(staging、production等)构建多个Docker镜像。我只想运行npm run build:prod一次,并将创建的文件打包到一个Docker镜像中。 - Thomas Einwaller

2
我会稍微改变一下你的sed-shellscript的方法,但在某种程度上类似。我喜欢你提到的“纯文本配置文件”的想法,怎么样:
从已知位置(例如./config)提供配置文件? 由于你的JS已经从当前站点加载,至少在这里你应该能够依赖相对路径。
步骤1:docker容器中启动的shell脚本将环境变量中的配置参数写入纯文本文件,并将其放置在打包后的Angular代码的同一个文件夹中。
步骤2:在加载应用程序的主HTML文件中,有一些初始化代码,如下所示:
$(function() {
  $.get('./config')
    .then(function(res) {
      window.appConfig = res.data;
      // your bootstrapping call
      angular.bootstrap(....);
    });
});

第三步:在您的Angular应用程序中使用全局变量,例如,如果您正在使用OpaqueToken:
import { OpaqueToken } from '@angular/core';

const CONFIG_TOKEN = new OpaqueToken('config');

export const THIRDPARTYLIBPROVIDERS = [
  { provide: CONFIG_TOKEN, useValue: window.appConfig }
];

是的,这仍然有点hacky,在你的开发环境中,用于提供ng-app的node-proxy还必须公开此类配置端点。此外,这需要一个额外的请求,但是可以通过稍微扩展init-code将数据缓存在localStorage中来轻松避免(假设这些数据不会随时间而改变)。
总的来说,我认为这比对一些你不知道它们如何布置的文件进行sed操作更易于维护。
让我知道你的想法!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接