首页 > 解决方案 > Docker从文件中编写构建时间参数

问题描述

我知道可用的变量替换,我可以.env在项目的根目录中使用 a 并且可以这样做,但在这种情况下,我正在调整现有项目,.env预计现有文件位置并且我想防止必须在多个文件上有 var 条目!

有关更多信息,请参阅文档,所有代码都可以docker-support在 repo 的分支上作为 WIP 获得,但我将在下面简洁地描述项目和问题:

项目结构

|- root
|  |- .env # mongo and mongo-express vars (not on git!)
|  |- docker-compose.yaml # build and ups a staging env
|  |- docker-compose.prod.yaml # future wip
|  |- api # the saas-api service
|     |- Dockerfile # if 'docked' directly should build production
|     |- .env # api relative vars (not on git!)
|  |- app # the saas-app service
|     |- Dockerfile # if 'docked' directly should build production
|     |- .env # api relative vars (not on git!)

或者在这里查看整个内容,顺便说一下,它目前工作得很好,但是saas-app在构建一个我迄今为止可以识别的用于登台/制作的图像时存在一个问题。

问题

在构建时 Next.js 使用 webpack 构建页面的静态版本来完成process.env替换,因此它需要在 docker build 阶段包含实际最终运行的变量,因此 next.js 不需要在运行时再次重建,并且这样我就可以在流量需要时安全地生成多个实例!

我知道,如果在运行时没有发送相同的变量,它将不得不再次重建,这违背了本练习的重点,但这正是我在这里试图阻止的,如果发送了错误的值,它就在我们身上不是项目!

而且我还需要考虑 Next.js BUILD ID 管理,但这是另一个时间/问题。

尝试

我一直在测试在其Dockerfile上包含应用程序预期的每个变量的 ARG 和 ENV 声明,例如:

ARG GA_TRACKING_ID=
ENV GA_TRACKING_ID ${GA_TRACKING_ID}

这可以按预期工作,但是它迫使我在docker-compose.yml文件中手动声明它们,这并不理想:

  saas-app:
    build:
      context: app
      args:
        GA_TRACKING_ID: UA-xXxXXXX-X

我不能在这里使用变量替换,因为我的根.env不包含这个 var,它是 on ./app/.env,而且我还测试了将值留空但它没有从env_fileorenviroment定义中提取它,我相信这是预期的。

我已经使用存储库中的现有版本粘贴了完整输出:docker-compose config

理想情况下,我想:

  saas-app:
    build:
      args:
        LOG_LEVEL: notice
        NODE_ENV: development
        PORT: '3000'
      context: /home/pedro/src/opensource/saas-boilerplate/app
    command: yarn start
    container_name: saas-app
    depends_on:
    - saas-api
    environment:
      ...

成为:

  saas-app:
    build:
      args:
        LOG_LEVEL: notice
        NODE_ENV: development
        PORT: '3000'
        BUCKET_FOR_POSTS: xxxxxx
        BUCKET_FOR_TEAM_AVATARS: xxxxxx
        GA_TRACKING_ID: ''
        LAMBDA_API_ENDPOINT: xxxxxxapi
        NODE_ENV: development
        STRIPEPUBLISHABLEKEY: pk_test_xxxxxxxxxxxxxxx
        URL_API: http://api.saas.localhost:8000
        URL_APP: http://app.saas.localhost:3000
      context: /home/pedro/src/opensource/saas-boilerplate/app
    command: yarn start
    container_name: saas-app
    depends_on:
    - saas-api
    environment:
      ...

问题

如果可能的话,我将如何实现这一目标,但是:

  1. 无需将现有.env文件合并到单个根目录中,也无需在多个文件上复制 var。
  2. 无需手动声明撰写文件上的值,或者不必在命令上推断它们,例如docker-compose build --build-arg GA_TRACKING_ID=UA-xXxXXXX-X
  3. 无需在构建阶段COPY处理每个.env文件,因为它感觉不正确和/或不安全?
  4. 也许args_file对撰写团队的撰写build选项功能请求在我看来是有效的,你也会这么说吗?
  5. .env或者也许在撰写文件上有一个根选项,您可以在其中为变量替换设置多个文件?
  6. 或者也许我没有看到另一个解决方案?有任何想法吗?
  7. 我不介意将每个.env文件作为configsecret发送,这是比拆分 compose 文件更清洁的解决方案,是否有人在生产中运行这样的示例?

标签: dockerwebpackdocker-composeopen-sourcenext.js

解决方案


我已经设法实现了一种不影响任何现有开发工作流程的折衷方案,也不允许在没有环境变量的情况下构建应用程序(对于生产构建而言,这一要求更为重要)。

我基本上决定重用 docker 的内部能力来读取.env文件并在 compose 文件上使用变量替换,这是一个例子:

# compose
COMPOSE_TAG_NAME=stage

# common to api and app (build and run)
LOG_LEVEL=notice
NODE_ENV=development
URL_APP=http://app.saas.localhost:3000
URL_API=http://api.saas.localhost:8000
API_PORT=8000
APP_PORT=3000

# api (run)
MONGO_URL=mongodb://saas:secret@saas-mongo:27017/saas
SESSION_NAME=saas.localhost.sid
SESSION_SECRET=3NvS3Cr3t!
COOKIE_DOMAIN=.saas.localhost
GOOGLE_CLIENTID=
GOOGLE_CLIENTSECRET=
AMAZON_ACCESSKEYID=
AMAZON_SECRETACCESSKEY=
EMAIL_SUPPORT_FROM_ADDRESS=
MAILCHIMP_API_KEY=
MAILCHIMP_REGION=
MAILCHIMP_SAAS_ALL_LIST_ID=
STRIPE_TEST_SECRETKEY=
STRIPE_LIVE_SECRETKEY=
STRIPE_TEST_PUBLISHABLEKEY=
STRIPE_LIVE_PUBLISHABLEKEY=
STRIPE_TEST_PLANID=
STRIPE_LIVE_PLANID=
STRIPE_LIVE_ENDPOINTSECRET=

# app (build and run)
STRIPEPUBLISHABLEKEY=
BUCKET_FOR_POSTS=
BUCKET_FOR_TEAM_AVATARS=
LAMBDA_API_ENDPOINT=
GA_TRACKING_ID=

查看更新的 docker-compose.yml我还使用了扩展字段来确保在构建和运行时只发送正确和有效的变量。

它打破了问题的规则 1.,但我觉得这是一个足够好的折衷方案,因为它不再依赖于其他.env文件,无论如何这在大多数情况下都可能是开发密钥!

不幸的是,如果 vars 将来发生变化,我们将需要维护 compose 文件,并且必须将相同的.env文件用于生产构建,但由于这可能会在某些 CI/CD 上在外部完成,所以不用担心。

我正在发布此问题,但并未完全解决问题,如果其他人可以提出更好的想法,我将不胜感激。


推荐阅读