【问题标题】:Passing environment variables at runtime to Vue.js application with docker-compose使用 docker-compose 在运行时将环境变量传递给 Vue.js 应用程序
【发布时间】:2020-04-30 12:30:26
【问题描述】:

这是我关于这个特定问题的第二篇文章。我已经删除了这个问题,因为我找到了一种更好的方式来解释我到底想做什么。

本质上,我想将命令行参数传递给docker-compose up,并将它们设置为我的 Vue.js Web 应用程序中的环境变量。目标是能够更改环境变量,而无需每次都重新构建容器。

我遇到了几个问题。这是我的 docker 文件:

用于 Vue.js 应用程序的 Dockerfile。

FROM node:latest as build-stage
WORKDIR /app

# Environment variable.
ENV VUE_APP_FOO=FOO

COPY package*.json ./
RUN npm install
COPY ./ .
RUN npm run build

FROM nginx as production-stage
RUN mkdir /app
COPY --from=build-stage /app/dist /app
COPY nginx.conf /etc/nginx/nginx.conf

VUE_APP_FOO 通过 Node 的 process.env 对象存储和访问,并且似乎在构建时传入。

还有我的 docker-compose.yml:

version: '3.5'

services:
    ms-sql-server:
        image: mcr.microsoft.com/mssql/server:2017-latest-ubuntu
        ports: 
            - "1430:1433"
    api:
        image: # omitted (pulled from url)
        restart: always
        depends_on: 
            - ms-sql-server
        environment:
            DBServer: "ms-sql-server"
        ports:
            - "50726:80"
    client:
        image: # omitted(pulled from url)
        restart: always
        environment:
            - VUE_APP_BAR="BAR"
        depends_on: 
            - api
        ports:
            - "8080:80"

当我使用docker exec -it <container_name> /bin/bash SSH 进入客户端容器时,VUE_APP_BAR 变量的值是“BAR”。但该变量并未存储在我的 Vue 应用程序的 process.env 对象中。 Node 和它的环境变量似乎发生了一些奇怪的事情。这就像是忽略了容器环境。

我是否可以在我的 Vue.js 应用程序中访问 docker-compose.yml 中设置的容器级变量?此外,无论如何将这些变量作为参数传递给docker-compose up?如果您需要任何澄清/更多信息,请告诉我。

【问题讨论】:

  • 什么在为您的 Vue 应用程序提供服务? Nginx?
  • 是的,它是 Nginx。

标签: docker vue.js docker-compose


【解决方案1】:

index.html 中添加静态config.js 脚本。这个文件不是由 Webpack 处理的,而是逐字包含的。使用您的 docker-compose 文件或 kubernetes 清单或 AWS ECS 任务配置或类似文件在运行时覆盖该文件。

例如在我的Vue项目中:

public/config.js

// Run-time configuration. Override this in e.g. your Dockerfile, kubernetes pod or AWS ECS Task.
// Use only very simple, browser-compatible JS.
window.voxVivaConfig = {};

public/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- ... -->

    <!-- Allow injection of run-time config -->
    <script async src="config.js"></script>
    
    <!-- ... -->
</head>
<body>
<div id="app" aria-busy="true">
    <noscript><p>This app requires JavaScript.</p></noscript>
</div>
</body>
</html>

src/config.js

function getRunTimeConfig() {
  if (typeof window.voxVivaConfig === "object") {
    return window.voxVivaConfig || {};
  }

  return {};
}

export default Object.freeze({
  appTitle: "My App",

  backEndBaseUrl: process.env.VUE_APP_BACK_END_BASEURL || "https://example.com",
  whatever: process.env.VUE_APP_WHATEVER || "",

  /**
   * Allow config specified at run time to override everything above.
   */
  ...getRunTimeConfig(),
});

优势

这会将所有配置放在一个位置,并允许您选择应在编译时、构建时或运行时指定哪些配置值,只要您认为合适。

【讨论】:

    【解决方案2】:

    尽管问题标题说如何从 vue.js 端使用环境变量,但提问者的目标是能够在不重建 docker 映像的情况下动态配置后端 api 端点。

    我是通过反向代理实现的。

    1. 对于开发运行,在 vue.config.js 上配置反向代理,由 vue-cli Web 服务器使用。
    2. 对于 nginx 运行,在 nginx.conf 上配置反向代理。 You can use nginx template to read environment variables

    这种方法也消除了在 web-api 服务器端配置 CORS 的需要,因为 web api 是从 vue 应用的 web 服务器调用的,而不是直接从浏览器调用的。

    可以在this commit.找到更全面的工作示例

    vue.config.js:

    module.exports = {
      devServer: {
        proxy: {
          '/api': {
            target: 'http://host.docker.internal:5000',
          },
        },
      },
    };
    

    nginx.conf:

    ...
    http {
      ...
      include /etc/nginx/conf.d/*.conf;
    }
    

    nginx.default.conf.template:

      server {
        listen       80;
    ...
        location /api {
          proxy_pass ${WEBAPI_ENDPOINT};
        }
      }
    

    Dockerfile:

    ...
    COPY nginx.conf /etc/nginx/nginx.conf
    COPY nginx.default.conf.template /etc/nginx/templates/default.conf.template
    

    【讨论】:

      【解决方案3】:

      所以我想出了如何以一种非常适合我的用例的 hacky 方式执行此操作。快速回顾一下我想做的事情:能够通过 docker-compose 文件将环境变量传递给 Vue.js 应用程序,以允许不同的团队成员根据他们的分配测试不同的开发 API(如果在本地运行服务器,则为 localhost , api-dev, api-staging, api-prod)。

      第一步是在你的 VueJS 项目中的一个 JS 文件中声明你的变量(它可以在任何地方定义),格式如下:

      export const serverURL = 'VUE_APP_SERVER_URL'

      关于此字符串值的快速说明:它必须对您的整个项目完全唯一。如果您的应用程序中有任何其他字符串或变量名称与之匹配,它将被我们使用此方法传递的 docker 环境变量替换。

      接下来我们必须转到 docker-compose.yml 并在那里声明我们的环境变量:

      docker-compose.yml

      your_vuejs_client:
          build: /path/to/vuejs-app
          restart: always
          environment: 
              VUE_APP_SERVER_URL: ${SERVER_URL}
          ports:
              - "8080:80"
      

      现在,当您在终端中运行 docker-compose up 时,您应该会看到如下内容: WARNING: The SERVER_URL variable is not set. Defaulting to a blank string.

      在我们正确设置 docker-compose 后,我们需要在 VueJS 应用程序中创建一个入口点脚本,以便在通过 nginx 启动应用程序之前运行。为此,导航回您的 VueJS 目录并运行 touch entrypoint.sh 以创建一个空白 bash 脚本。打开它,这是我的:

      entrypoint.sh

      #!/bin/sh
      
      ROOT_DIR=/usr/share/nginx/html
      
      echo "Replacing env constants in JS"
      for file in $ROOT_DIR/js/app.*.js* $ROOT_DIR/index.html $ROOT_DIR/precache-manifest*.js;
      do
        echo "Processing $file ...";
      
        sed -i 's|VUE_APP_SERVER_URL|'${VUE_APP_SERVER_URL}'|g' $file
      
      done
      

      sed -i 's|VUE_APP_SERVER_URL|'${VUE_APP_SERVER_URL}'|g' $file 这一行横穿你的整个应用程序以获取字符串“VUE_APP_SERVER_URL”,并将其替换为来自 docker-compose.yml 的环境变量。

      最后,我们需要在我们的 VueJS 应用程序 Dockerfile 中添加一些行,告诉它在启动 nginx 之前运行我们刚刚创建的入口点脚本。因此,在 Dockerfile 中的 CMD ["nginx", "-g", "daemon off;"] 行之前,添加以下行:

      VueJS Dockerfile

      # Copy entrypoint script as /entrypoint.sh
      COPY ./entrypoint.sh /entrypoint.sh
      
      # Grant Linux permissions and run entrypoint script
      RUN chmod +x /entrypoint.sh
      ENTRYPOINT ["/entrypoint.sh"]
      

      之后,运行docker-compose run -e SERVER_URL=yourserverapi.com/api,我们一开始在JS文件中设置的serverURL常量将被替换为您在docker-compose命令中提供的任何内容。最终开始工作很痛苦,但我希望这可以帮助任何面临类似问题的人。很棒的是,您可以根据需要添加任意数量的环境变量,只需在 entrypoint.sh 文件中添加更多行并在 Vue.js 应用程序和 docker-compose 文件中定义它们。我使用的一些方法为 USPS API 提供不同的端点,具体取决于您是在本地运行还是托管在云中,根据实例是在生产还是开发中运行等提供不同的 Maps API 密钥。等等

      我真的希望这对某人有所帮助,如果有人有任何问题,请告诉我,希望我能提供一些帮助。

      【讨论】:

      • 嗨@jrend,这可以在谷歌云运行环境变量中工作吗?
      • @Ricardo 目前我们正在使用 Azure,但只要您可以将环境变量传递给 docker 容器,它就可以正常工作。
      • 还有另一种方法。 stackoverflow.com/questions/53010064/…
      【解决方案4】:

      正如我在这里发布的 https://stackoverflow.com/a/63097312/4905563,我开发了一个可以提供帮助的软件包。

      尝试使用npm install jvjr-docker-env 看看README.md 以查看一些使用示例。

      【讨论】:

        【解决方案5】:

        客户端应用程序在 Web 浏览器上运行,但环境变量在服务器上。客户端需要一种从服务器获取环境变量值的方法。

        为此,您有多种选择,包括:

        1. 利用 Nginx 为环境变量本身提供服务,使用如下方法之一:nginx: use environment variables。根据您的需要,这种方法可能更快、更动态或更静态,可能不太正式和优雅。或者

        2. 实现读取环境变量并通过AJAX 调用将其返回给客户端的服务器 API(Node.js?)。这种方法优雅、动态、以 API 为中心。或者

        3. 最后,如果每个部署的每个 nginx 实例的环境变量都是静态的,您可以在部署期间构建 Vue 应用程序的静态资产,并将环境变量硬编码在静态资产中。这种方法有点优雅,但会污染客户端代码和服务器细节,而且有点静态(只能在部署时更改)。

        【讨论】:

        • 我发现了一种 hacky 方法,稍后将作为解决方案发布。目的是能够通过 docker-compose 指定基本 API url,这样我们的测试人员就可以传入值,而不必每次都重新构建。
        猜你喜欢
        • 2019-06-18
        • 2023-01-30
        • 2020-11-07
        • 2021-05-23
        • 1970-01-01
        • 2018-12-18
        • 2019-12-26
        相关资源
        最近更新 更多