通过 docker-compose 将变量从 .env 文件传递​​到 dockerfile

cee*_*pie 8 windows powershell docker dockerfile docker-compose

project\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80app\n\xe2\x94\x82   \xe2\x94\x82   ...\n\xe2\x94\x82   \xe2\x94\x82   Dockerfile\n\xe2\x94\x82   \xe2\x94\x82\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80prod.env\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80docker-compose.yml\n
Run Code Online (Sandbox Code Playgroud)\n

我的 docker-compose 看起来像这样:

\n
services:\n   app:\n      build:\n         context: .\\app\n         args:\n            ARG1: val1\n            ARG2: val2\n      env_file:\n         - prod.env\n
Run Code Online (Sandbox Code Playgroud)\n

但我也尝试过这个:

\n
services:\n   app:\n      build:\n         context: .\\app\n         args:\n            ARG1: ${ARG1}\n            ARG2: ${ARG2}\n      env_file:\n         - prod.env\n
Run Code Online (Sandbox Code Playgroud)\n

我的 prod.env 文件如下所示:

\n
ARG1 = 'val1'\nARG2 = 'val2'\n
Run Code Online (Sandbox Code Playgroud)\n

但我也尝试过这个:

\n
ARG1=val1\nARG2=val2\n
Run Code Online (Sandbox Code Playgroud)\n

我希望将 args 的值或 prod.env 文件中的值传递给 dockerfile。

\n

这就是我试图得到的:

\n
ARG ARG1\nARG ARG2\n\nRUN echo ${ARG1}\nRUN echo ${ARG2}\n
Run Code Online (Sandbox Code Playgroud)\n
ENV ARG1 ${ARG1}\nENV ARG2 ${ARG2}\n\nRUN echo ${ARG1}\nRUN echo ${ARG2}\n
Run Code Online (Sandbox Code Playgroud)\n
ENV ARG1 "new val2"\nENV ARG2 "new val2"\n\nRUN echo ${ARG1}\nRUN echo ${ARG2}\n
Run Code Online (Sandbox Code Playgroud)\n

它总是以空白值结尾。

\n

任何帮助将不胜感激。当我尝试时,我觉得其他帖子的答案都不起作用。

\n

为了构建我使用docker-compose --env-file prod.env build

\n

谢谢

\n

更新\nSergio Santiago 询问我是否可以运行docker-compose config并显示结果。

\n

这是我用于此测试的最终文件。

\n

docker-撰写:

\n
services:\n   app:\n      build:\n         context: .\\app\n         args:\n            ARG1: val1\n            ARG2: val2\n      env_file:\n         - prod.env\n
Run Code Online (Sandbox Code Playgroud)\n

产品环境:

\n
ARG3 = 'val3'\nARG4 = 'val4'\n
Run Code Online (Sandbox Code Playgroud)\n

这是输出docker-compose --env-file prod.env config

\n
networks:\n  demo-net: {}\nservices:\n  app:\n    build:\n      args:\n        ARG1: val1\n        ARG2: val2\n      context: C:\\project\\app\n    environment:\n      ENV: prod.env\n      ARG3: val3\n      ARG4: val4\n
Run Code Online (Sandbox Code Playgroud)\n

我想补充一点,从这里显然将变量从 .env 文件获取到 docker-compose 文件不是问题。我还有一个在容器上运行的 Flask 应用程序,通过 os.environ 它可以使用 .env 文件中的变量。我只是不知道如何授予对 Dockerfile 的相同访问权限。

\n

更新 2 \n与 ErikMD 的答案相关的更多具体信息

\n

产品环境

\n
DOMAIN = 'actualdomain.com'\nENV = 'prod.env'\nENV_NUM = 1\nARG1 = 'value1'\n
Run Code Online (Sandbox Code Playgroud)\n

开发环境

\n
DOMAIN = 'localhost'\nENV = 'dev.env'\nENV_NUM = 0\nARG1 = 'value1'\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,ARG1 的值相同,但其他值不同。

\n

docker-compose.yml

\n
version: "3.7"\nservices:\n  home:\n    image: home-${ENV_NUM}\n    build: \n      context: .\\home\n      args:\n        ARG1: "${ARG1}"\n    networks:\n      - demo-net\n    env_file:\n      - ${ENV}\n    labels:\n      - traefik.enable=true\n      - traefik.http.routers.home.rule=Host(`${DOMAIN}`)\n      - traefik.http.routers.home.entrypoints=web\n    volumes:\n      - g:\\:c:\\sharedrive\n...\n...\n  reverse-proxy:\n    restart: always\n    image: traefik:v2.6.1-windowsservercore-1809\n    command:\n      - --api.insecure=true\n      - --providers.docker=true\n      - --entrypoints.web.address=:80\n      - --providers.docker.endpoint=npipe:////./pipe/docker_engine\n    ports:\n      - 80:80\n      - 443:443\n      - 8080:8080\n    networks:\n     - demo-net\n    volumes:\n      - source: \\\\.\\pipe\\docker_engine\\\n        target: \\\\.\\pipe\\docker_engine\\\n        type: npipe\nnetworks:\n  demo-net:\n
Run Code Online (Sandbox Code Playgroud)\n

这些点代表其他应用程序的格式与主页相同。

\n

docker文件

\n
FROM python:3.10.3\n\nARG ARG1="default"\n\nENV ARG1="${ARG1}"\n\nWORKDIR /app\n\nENV PYTHONDONTWRITEBYTECODE 1\nENV PYTHONUNBUFFERED 1\n\nRUN echo "This is argument 1 -> ${ARG1}"\n
Run Code Online (Sandbox Code Playgroud)\n

的输出docker-compose --env-file prod.env config

\n
networks:\n  demo-net: {}\nservices:\n  home:\n    build:\n      args:\n        ARG1: value1\n      context: C:\\MIS-Web-App\\home\n    environment:\n      DOMAIN: actualdomain.com\n      ENV: prod.env\n      ENV_NUM: '1'\n      ARG1: value1\n    image: home-1\n    labels:\n      traefik.enable: "true"\n      traefik.http.routers.home.entrypoints: web\n      traefik.http.routers.home.rule: Host(`mis.canaras.net`)\n    networks:\n      demo-net: null\n    volumes:\n    - g:\\:c:\\sharedrive:rw\n...\n...\n
Run Code Online (Sandbox Code Playgroud)\n

然后我运行docker-compose --env-file prod.env build或者docker-compose --env-file dev.env build

\n

构建的输出

\n
Step 9/23 : RUN echo "This is argument 1 -> ${ARG1}"\n ---> Running in 5142850de365\nThis\nis\nargument\n1\n->\nRemoving intermediate container 5142850de365\n
Run Code Online (Sandbox Code Playgroud)\n

现在我在命令和实际文件中调用传递 env_file ,因为其中有我的 docker-compose 文件需要的变量和我的 Flask 应用程序需要的变量。并且肯定存在重叠。

\n

将 prod.env 或 dev.env 文件中的值获取到 docker-compose 不是问题。也没有将它添加到我的烧瓶应用程序中。问题是将这些值获取到 dockerfile。

\n

Eri*_*kMD 7

我正在发布一个新答案,以强调与OP问题相关的各种假设,特别是".env"唯一文件名和*.env文件( 的参数env_file:)之间存在细微差别的事实。

\n

docker-compose.yml但除了这种微妙之处之外,从到docker build -f Dockerfile .和/或传递参数的过程docker run -e \xe2\x80\xa6很简单,如下面的综合示例所示。

\n

最小工作示例

\n

让我们考虑给定目录中的以下文件,例如./docker.

\n

文件docker-compose.yml

\n
services:\n  demo-1:\n    image: demo-${ENV_NUM}\n    build:\n      context: .\n      args:\n        ARG1: "demo-1/${ARG1}"\n        ARG3: "demo-1/${ARG3}"\n  demo-2:\n    image: demo-2${ENV_FILE_NUM}\n    build:\n      context: .\n      args:\n        ARG1: "demo-2/${ARG1}"\n        ARG3: "demo-2/${ARG3}"\n    env_file:\n      - var.env\n
Run Code Online (Sandbox Code Playgroud)\n

备注:即使我们使用字段,也添加一个字段来自动标记构建的图像build:似乎是一个好主意;image:但请注意,这些图像名称必须成对不同。

\n

文件.env

\n
KEY="some value"\nENV_NUM=1\nARG1=.env/ARG1\nARG2=.env/ARG2\nARG3=.env/ARG3\n
Run Code Online (Sandbox Code Playgroud)\n

文件var.env

\n
ENV_FILE_NUM="some number"\nARG1=var.env/ARG1\nARG2=var.env/ARG2\nARG3=var.env/ARG3\nARG4=var.env/ARG4\n
Run Code Online (Sandbox Code Playgroud)\n

文件Dockerfile

\n
FROM debian:10\n\n# Read build arguments (default value if omitted at CLI)\nARG ARG1="default 1"\nARG ARG2="default 2"\nARG ARG3="default 3"\n\n# the build args are exported at build time\nRUN echo "ARG1=${ARG1}" | tee /root/arg1.txt\nRUN echo "ARG2=${ARG2}" | tee /root/arg2.txt\nRUN echo "ARG3=${ARG3}" | tee /root/arg3.txt\n\n# Export part of these args at runtime also\nENV ARG1="${ARG1}"\nENV ARG2="${ARG2}"\n\n# exec-form is mandatory for ENTRYPOINT/CMD\nCMD ["/bin/bash", "-c", "echo ARG1=\\"${ARG1}\\" ARG2=\\"${ARG2}\\" ARG3=\\"${ARG3}\\"; echo while at build time:; cat /root/arg{1,2,3}.txt"]\n
Run Code Online (Sandbox Code Playgroud)\n

实验1

\n

首先,正如 @SergioSantiago 在评论中所建议的docker-compose.yml,在插值后预览有效文件的一个非常方便的命令是docker-compose config

\n
$ docker-compose config\n\nWARN[0000] The "ENV_FILE_NUM" variable is not set. Defaulting to a blank string. \nname: docker\nservices:\n  demo-1:\n    build:\n      context: /home/debian/docker\n      dockerfile: Dockerfile\n      args:\n        ARG1: demo-1/.env/ARG1\n        ARG3: demo-1/.env/ARG3\n    image: demo-1\n    networks:\n      default: null\n  demo-2:\n    build:\n      context: /home/debian/docker\n      dockerfile: Dockerfile\n      args:\n        ARG1: demo-2/.env/ARG1\n        ARG3: demo-2/.env/ARG3\n    environment:\n      ARG1: var.env/ARG1\n      ARG2: var.env/ARG2\n      ARG3: var.env/ARG3\n      ARG4: var.env/ARG4\n      ENV_FILE_NUM: some number\n    image: demo-2\n    networks:\n      default: null\nnetworks:\n  default:\n    name: docker_default\n
Run Code Online (Sandbox Code Playgroud)\n

在这里,正如警告所示,我们看到插值存在问题ENV_FILE_NUM,尽管 提到了这个变量var.env。原因是env_files行只是为底层docker run -e \xe2\x80\xa6命令添加新的环境变量,但不会docker-compose.yml.

\n

ARG1=.env/ARG1相反,我们可以注意到,从 中获取的值".env"是在args:的字段内插值的docker-compose.yml,参见 1。输出线:

\n
args:\n  ARG1: demo-1/.env/ARG1\n  \xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n

".env"与s的这种非常不同的语义在官方文档的本页env_file中进行了描述

\n

实验2

\n

接下来,让我们运行:

\n
$ docker-compose up --build\n                                                                                         \nWARN[0000] The "ENV_FILE_NUM" variable is not set. Defaulting to a blank string.                                                                     \n[+] Building 10.4s (13/13) FINISHED\n => [demo-1 internal] load build definition from Dockerfile\n => => transferring dockerfile: 609B\n => [demo-2 internal] load build definition from Dockerfile\n => => transferring dockerfile: 609B\n => [demo-1 internal] load .dockerignore\n => => transferring context: 2B\n => [demo-2 internal] load .dockerignore\n => => transferring context: 2B\n => [demo-2 internal] load metadata for docker.io/library/debian:10\n => [demo-2 1/4] FROM docker.io/library/debian:10@sha256:ebe4b9831fb22dfa778de4ffcb8ea0ad69b5d782d4e86cab14cc1fded5d8e761\n => => resolve docker.io/library/debian:10@sha256:ebe4b9831fb22dfa778de4ffcb8ea0ad69b5d782d4e86cab14cc1fded5d8e761\n => => sha256:85bed84afb9a834cf090b55d2e584abd55b4792d93b750db896f486680638344 50.44MB / 50.44MB\n => => sha256:ebe4b9831fb22dfa778de4ffcb8ea0ad69b5d782d4e86cab14cc1fded5d8e761 1.85kB / 1.85kB\n => => sha256:40dd1c1b1c36eac161ab63b6ce3a57d56ad79a667a37717a31721bac3f30aaf9 529B / 529B\n => => sha256:26a2b081e03207d26a105340161109ba0f00e857cbb0ff85aaeeeadd46b709c5 1.46kB / 1.46kB\n => => extracting sha256:85bed84afb9a834cf090b55d2e584abd55b4792d93b750db896f486680638344\n => [demo-2 2/4] RUN echo "ARG1=demo-2/.env/ARG1" | tee /root/arg1.txt\n => [demo-1 2/4] RUN echo "ARG1=demo-1/.env/ARG1" | tee /root/arg1.txt\n => [demo-1 3/4] RUN echo "ARG2=default 2" | tee /root/arg2.txt\n => [demo-2 3/4] RUN echo "ARG2=default 2" | tee /root/arg2.txt\n => [demo-2 4/4] RUN echo "ARG3=demo-2/.env/ARG3" | tee /root/arg3.txt\n => [demo-1 4/4] RUN echo "ARG3=demo-1/.env/ARG3" | tee /root/arg3.txt\n => [demo-2] exporting to image\n => => exporting layers\n => => writing image sha256:553f294a410ceeb3c0ac9d252d443710c804d3f7437ad7fffa586967517f5e7a\n => => naming to docker.io/library/demo-1\n => => writing image sha256:84bb2bd0ffae67ffed0e74efbf9253b6d634a6f37c6f99bc4eedea81846a9352\n => => naming to docker.io/library/demo-2\n                                     \nUse \'docker scan\' to run Snyk tests against images to find vulnerabilities and learn how to fix them\n[+] Running 3/3              \n \xe2\xa0\xbf Network docker_default     Created\n \xe2\xa0\xbf Container docker-demo-1-1  Created\n \xe2\xa0\xbf Container docker-demo-2-1  Created\n
Run Code Online (Sandbox Code Playgroud)\n
Attaching to docker-demo-1-1, docker-demo-2-1\n\ndocker-demo-1-1  | ARG1=demo-1/.env/ARG1 ARG2=default 2 ARG3=\ndocker-demo-1-1  | while at build time:\ndocker-demo-1-1  | ARG1=demo-1/.env/ARG1\ndocker-demo-1-1  | ARG2=default 2\ndocker-demo-1-1  | ARG3=demo-1/.env/ARG3\n\ndocker-demo-2-1  | ARG1=var.env/ARG1 ARG2=var.env/ARG2 ARG3=var.env/ARG3\ndocker-demo-2-1  | while at build time:\ndocker-demo-2-1  | ARG1=demo-2/.env/ARG1\ndocker-demo-2-1  | ARG2=default 2\ndocker-demo-2-1  | ARG3=demo-2/.env/ARG3\n\ndocker-demo-1-1 exited with code 0\ndocker-demo-2-1 exited with code 0\n
Run Code Online (Sandbox Code Playgroud)\n

在这里,我们再次看到,".env"价值观和价值观file_env: [ filename.env ]扮演着不同的角色,并不重叠。

\n

此外:

\n
    \n
  • 由于缺少 Dockerfile 命令行ENV ARG3="${ARG3}",因此 build-arg 的值ARG3不会在运行时传播(请参阅ARG3=上面输出中的行)。
  • \n
  • environment:但是,如果在文件中的或env_file:部分中定义/覆盖了该值,则无论如何都可以在运行时导出该值docker-compose.yml(请参阅ARG3=var.env/ARG3上面输出中的行)。
  • \n
\n

有关更多详细信息,请参阅该ARG指令的文档。

\n

关于docker-compose --env-file选项用例的备注

\n

正如OP所提到的,docker-compose还享有一个有用的CLI选项--env-file(它的命名方式与非常不同的字段完全相同env-file:,这是不幸的,但没关系)。

\n

此选项允许以下用例(摘自 OP 的代码):

\n

文件docker-compose.yml

\n
Attaching to docker-demo-1-1, docker-demo-2-1\n\ndocker-demo-1-1  | ARG1=demo-1/.env/ARG1 ARG2=default 2 ARG3=\ndocker-demo-1-1  | while at build time:\ndocker-demo-1-1  | ARG1=demo-1/.env/ARG1\ndocker-demo-1-1  | ARG2=default 2\ndocker-demo-1-1  | ARG3=demo-1/.env/ARG3\n\ndocker-demo-2-1  | ARG1=var.env/ARG1 ARG2=var.env/ARG2 ARG3=var.env/ARG3\ndocker-demo-2-1  | while at build time:\ndocker-demo-2-1  | ARG1=demo-2/.env/ARG1\ndocker-demo-2-1  | ARG2=default 2\ndocker-demo-2-1  | ARG3=demo-2/.env/ARG3\n\ndocker-demo-1-1 exited with code 0\ndocker-demo-2-1 exited with code 0\n
Run Code Online (Sandbox Code Playgroud)\n

文件prod.env

\n
services:\n  home:\n    image: home-${ENV_NUM}\n    build:\n      args:\n        ARG1: "${ARG1}"\n      ...\n    labels:\n      - traefik.http.routers.home.rule=Host(`${DOMAIN}`)\n      ...\n    env_file:\n      - ${ENV}\n
Run Code Online (Sandbox Code Playgroud)\n

文件dev.env

\n
DOMAIN = \'actualdomain.com\'\nENV = \'prod.env\'\nENV_NUM = 1\nARG1 = \'value 1\'\n
Run Code Online (Sandbox Code Playgroud)\n

然后运行:

\n
    \n
  • docker-compose --env-file prod.env build,
  • \n
  • 或者docker-compose --env-file dev.env build
  • \n
\n

顺便说一句,即使到目前为止这个答案的大部分内容都说明了".env"文件名和env_file:文件具有非常不同的语义\xe2\x80\xa6,但确实它们也可以按照建议的方式“很好地”组合由OP来实现这个用例。

\n

顺便请注意,这docker-compose config也适用于“调试”Compose 规范:

\n
    \n
  • docker-compose --env-file prod.env config,
  • \n
  • 或者docker-compose --env-file dev.env config
  • \n
\n

现在关于最后一个问题:

\n
\n

prod.env从或dev.env文件获取值docker-compose不是问题。问题是将这些值传递到Dockerfile.

\n
\n

首先可以注意到有两种不同的情况:

\n
    \n
  1. 两个不同的部署环境(prod.envdev.env)可以共享相同的映像,因此差异仅在于运行时环境变量(而不是docker build args)。
  2. \n
  3. 或者,根据传递给 的文件--env-file,图像应该不同(然后 adocker-compose --env-file \xe2\x80\xa6 build确实是必要的)。
  4. \n
\n

看来大多数时候,情况 1. 是可以实现的(问题的配置也是如此,因为和ARG1中的值相同)并且可以被视为更有趣可重复性(因为我们确信“prod”图像与“dev”图像相同)。prod.envdev.env

\n

然而,有时这是不可能的,我们处于“情况 2”,例如,如果Dockerfile有一个特定的步骤,可能与测试等相关,则必须在生产模式下启用(或禁用)。

\n

现在,让我们假设我们处于情况 2 中。我们如何将“所有内容”从 传递--env-fileDockerfile?只有一种解决方案,即扩展args:的映射docker-compose.yml并包含您感兴趣的每个变量,例如:

\n
DOMAIN = \'localhost\'\nENV = \'dev.env\'\nENV_NUM = 0\nARG1 = \'value 1\'\n
Run Code Online (Sandbox Code Playgroud)\n

即使没有其他解决方案可以在构建时传递参数(从docker-compose底层docker build -f Dockerfile \xe2\x80\xa6),这也具有“声明性”的优点(只有 中提到的变量args:才会实际传递给Dockerfile)。

\n

退税?

\n

我看到的唯一缺点是您可能在运行时有不需要的额外环境变量(从底层docker-compose到底层docker run -e \xe2\x80\xa6),例如ENV=prod.env.

\n

如果这是一个问题,您可能需要".env"像这样分割文件:

\n

文件prod.env

\n
services:\n  home:\n    image: home-${ENV_NUM}\n    build: \n      context: .\\home\n      args:\n        DOMAIN: "${DOMAIN}"\n        ENV_NUM: "${ENV_NUM}"\n        ARG1: "${ARG1}"\n    networks:\n      - demo-net\n    env_file:\n      - ${ENV}\n    labels:\n      - traefik.enable=true\n      - traefik.http.routers.home.rule=Host(`${DOMAIN}`)\n      - traefik.http.routers.home.entrypoints=web\n    volumes:\n      - g:\\:c:\\sharedrive\n
Run Code Online (Sandbox Code Playgroud)\n

文件prod-run.env

\n
DOMAIN = \'actualdomain.com\'\nENV = \'prod-run.env\'\nENV_NUM = 1\nARG1 = \'value 1\'\n
Run Code Online (Sandbox Code Playgroud)\n

(假设您只想在运行时导出这两个环境变量)。

\n

或者,为了更好地遵循通常的“不要重复自己”规则,请删除prod-run.env,然后将这些值作为docker-compose构建参数传递,如前所述:

\n
DOMAIN = \'actualdomain.com\'\nENV_NUM = 1\n
Run Code Online (Sandbox Code Playgroud)\n

写在Dockerfile

\n
args:\n  DOMAIN: "${DOMAIN}"\n  ENV_NUM: "${ENV_NUM}"\n
Run Code Online (Sandbox Code Playgroud)\n

Dockerfile我已经在“实验会话 2”部分中给出了这些指令的示例

\n

(顺便说一句,很抱歉这个答案很长:)

\n


cee*_*pie 3

我的解决方案很烦人,这就是为什么我花了这么长时间才弄清楚的原因。我的 dockerfile 在 Windows 服务器上使用 powershell,因此我必须对每个参数执行此操作:

ARG ARG1
RUN echo $env:ARG1
Run Code Online (Sandbox Code Playgroud)

这看起来相当合适,特别是因为在 Windows 服务器上使用 Windows 容器不是我的第一选择,所以如果您遇到 env 文件等问题,请查看 @ErikMD 的答案。