Docker 从文件中编写构建时间参数
Docker compose build time args from file
我知道可用的变量替换,我可以在项目的根目录使用 .env
并且可以完成,但在这种情况下,我正在调整现有项目,其中现有 .env
个文件位置是预期的,我想避免在多个文件上有 var 条目!
有关详细信息,请参阅 documentation,并且所有代码都可以在回购的 docker-support
分支上以 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!)
或者查看整个 here,顺便说一句,目前效果很好,但是 saas-app
在为 staging/production 构建图像时存在一个问题,我可以识别到目前为止。
问题
在构建时 Next.js 使用 webpack 构建页面的静态版本来完成 process.env
替换,因此它需要实际最终的 运行 变量包含在docker 构建阶段,因此 next.js 不需要在运行时再次重建,这样我就可以在流量需要时安全地生成多个实例!
我知道如果在运行时没有发送相同的变量,它将不得不再次重建,违背了这个练习的要点,但这正是我在这里试图防止的,如果错误的值发送给我们而不是项目!
我还需要考虑 Next.js BUILD ID 管理,但那是另一个 time/question。
尝试次数
我一直在测试,包括应用程序预期的每个变量的 ARG 和 ENV 声明 Dockerfile,例如:
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
不包含这个变量,它在 ./app/.env
上,我还测试了将值留空但它没有从env_file
或 enviroment
定义,我相信这符合预期。
我已经 pastbinned 完整输出了 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:
...
问题
如果可能的话,我怎样才能做到这一点,但是:
- 无需将现有的
.env
文件合并到一个根目录中,也无需在多个文件上复制变量。
- 无需在撰写文件中手动声明值,也无需在命令中推断它们,例如
docker-compose build --build-arg GA_TRACKING_ID=UA-xXxXXXX-X
?
- 在 build stage 期间不必
COPY
每个 .env
文件,因为它感觉不对 and/or 安全?
- 也许
args_file
对撰写团队的撰写 build
选项功能请求在我看来是有效的,你也这么说吗?
- 或者也许在撰写文件上有一个根选项,您可以在其中设置多个
.env
文件用于变量替换?
- 或者我没有看到的另一种解决方案?有什么想法吗?
- 我不介意将每个
.env
文件作为 config or secret 发送,这是比拆分撰写文件更干净的解决方案,有人 运行 这样的生产示例吗?
与其尝试在多个 .env 中传递和合并值,不如考虑制作一个主 .env 并让 API 和 APP 服务继承相同的根 .env?
我已经设法达成妥协,不会影响任何现有的开发工作流程,也不允许 app 在没有环境变量的情况下构建(这一要求将对于生产构建更为重要)。
我基本上决定重用 docker 的内部能力来读取 .env
文件并在撰写文件的变量替换中使用这些能力,这是一个例子:
# 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 I've also made use of Extension fields 以确保在构建和 运行.
中只发送正确且有效的变量
它打破了问题中的规则 1.,但我觉得这是一个足够好的折衷方案,因为它不再依赖于其他 .env
文件,而这些文件在大多数情况下可能是开发密钥!
不幸的是,如果 vars 将来发生变化,我们将需要维护 compose 文件,并且相同的 .env
文件必须用于生产构建,但由于这可能会在某些外部完成CI/CD,那个不用太担心。
我发布了这个但并没有完全结束这个问题,如果其他人可以提出更好的想法,我将不胜感激。
我知道可用的变量替换,我可以在项目的根目录使用 .env
并且可以完成,但在这种情况下,我正在调整现有项目,其中现有 .env
个文件位置是预期的,我想避免在多个文件上有 var 条目!
有关详细信息,请参阅 documentation,并且所有代码都可以在回购的 docker-support
分支上以 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!)
或者查看整个 here,顺便说一句,目前效果很好,但是 saas-app
在为 staging/production 构建图像时存在一个问题,我可以识别到目前为止。
问题
在构建时 Next.js 使用 webpack 构建页面的静态版本来完成 process.env
替换,因此它需要实际最终的 运行 变量包含在docker 构建阶段,因此 next.js 不需要在运行时再次重建,这样我就可以在流量需要时安全地生成多个实例!
我知道如果在运行时没有发送相同的变量,它将不得不再次重建,违背了这个练习的要点,但这正是我在这里试图防止的,如果错误的值发送给我们而不是项目!
我还需要考虑 Next.js BUILD ID 管理,但那是另一个 time/question。
尝试次数
我一直在测试,包括应用程序预期的每个变量的 ARG 和 ENV 声明 Dockerfile,例如:
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
不包含这个变量,它在 ./app/.env
上,我还测试了将值留空但它没有从env_file
或 enviroment
定义,我相信这符合预期。
我已经 pastbinned 完整输出了 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:
...
问题
如果可能的话,我怎样才能做到这一点,但是:
- 无需将现有的
.env
文件合并到一个根目录中,也无需在多个文件上复制变量。 - 无需在撰写文件中手动声明值,也无需在命令中推断它们,例如
docker-compose build --build-arg GA_TRACKING_ID=UA-xXxXXXX-X
? - 在 build stage 期间不必
COPY
每个.env
文件,因为它感觉不对 and/or 安全? - 也许
args_file
对撰写团队的撰写build
选项功能请求在我看来是有效的,你也这么说吗? - 或者也许在撰写文件上有一个根选项,您可以在其中设置多个
.env
文件用于变量替换? - 或者我没有看到的另一种解决方案?有什么想法吗?
- 我不介意将每个
.env
文件作为 config or secret 发送,这是比拆分撰写文件更干净的解决方案,有人 运行 这样的生产示例吗?
与其尝试在多个 .env 中传递和合并值,不如考虑制作一个主 .env 并让 API 和 APP 服务继承相同的根 .env?
我已经设法达成妥协,不会影响任何现有的开发工作流程,也不允许 app 在没有环境变量的情况下构建(这一要求将对于生产构建更为重要)。
我基本上决定重用 docker 的内部能力来读取 .env
文件并在撰写文件的变量替换中使用这些能力,这是一个例子:
# 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 I've also made use of Extension fields 以确保在构建和 运行.
中只发送正确且有效的变量它打破了问题中的规则 1.,但我觉得这是一个足够好的折衷方案,因为它不再依赖于其他 .env
文件,而这些文件在大多数情况下可能是开发密钥!
不幸的是,如果 vars 将来发生变化,我们将需要维护 compose 文件,并且相同的 .env
文件必须用于生产构建,但由于这可能会在某些外部完成CI/CD,那个不用太担心。
我发布了这个但并没有完全结束这个问题,如果其他人可以提出更好的想法,我将不胜感激。