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

ibrsph3r  于 2022-11-10  发布在  Shell
关注(0)|答案(3)|浏览(153)
project
└───app
│   │   ...
│   │   Dockerfile
│   │
└───prod.env
└───docker-compose.yml

我的码头组合看起来是这样的:

services:
   app:
      build:
         context: .\app
         args:
            ARG1: val1
            ARG2: val2
      env_file:
         - prod.env

但我也试过这个:

services:
   app:
      build:
         context: .\app
         args:
            ARG1: ${ARG1}
            ARG2: ${ARG2}
      env_file:
         - prod.env

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

ARG1 = 'val1'
ARG2 = 'val2'

但我也试过这个:

ARG1=val1
ARG2=val2

我希望将args的值或prod.env文件中的值传递给dockerfile。
这就是我想要得到的:

ARG ARG1
ARG ARG2

RUN echo ${ARG1}
RUN echo ${ARG2}
ENV ARG1 ${ARG1}
ENV ARG2 ${ARG2}

RUN echo ${ARG1}
RUN echo ${ARG2}
ENV ARG1 "new val2"
ENV ARG2 "new val2"

RUN echo ${ARG1}
RUN echo ${ARG2}

它始终以空值结束。
任何帮助都将不胜感激。当我尝试的时候,我觉得其他帖子的答案都没有奏效。
为了构建,我使用了docker-compose --env-file prod.env build
谢谢

更新塞尔吉奥·圣地亚哥问我是否可以运行docker-compose config并显示结果。

以下是我在此测试中使用的最终文件。
Docker-撰写:

services:
   app:
      build:
         context: .\app
         args:
            ARG1: val1
            ARG2: val2
      env_file:
         - prod.env

Prod.env:

ARG3 = 'val3'
ARG4 = 'val4'

下面是docker-compose --env-file prod.env config的输出

networks:
  demo-net: {}
services:
  app:
    build:
      args:
        ARG1: val1
        ARG2: val2
      context: C:\project\app
    environment:
      ENV: prod.env
      ARG3: val3
      ARG4: val4

我想明确地补充一点,从这里将变量从.env文件获取到docker-compose文件不是问题。我还在容器上运行了一个FASK应用程序,并且通过os.environ它能够使用.env文件中的变量。我就是想不出怎么才能给Dockerfile同样的访问权限。

更新2关于ErikMD答案的更多具体信息

Prod.env

DOMAIN = 'actualdomain.com'
ENV = 'prod.env'
ENV_NUM = 1
ARG1 = 'value1'

Dev.env

DOMAIN = 'localhost'
ENV = 'dev.env'
ENV_NUM = 0
ARG1 = 'value1'

请注意,ARG1的值相同,但其他值不同。
Docker-compose.yml

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

这些圆点代表了其他与Home格式相同的应用程序。
扩展坞文件

FROM python:3.10.3

ARG ARG1="default"

ENV ARG1="${ARG1}"

WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

RUN echo "This is argument 1 -> ${ARG1}"

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

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

然后我运行docker-compose --env-file prod.env builddocker-compose --env-file dev.env build
构建的输出

Step 9/23 : RUN echo "This is argument 1 -> ${ARG1}"
 ---> Running in 5142850de365
This
is
argument
1
->
Removing intermediate container 5142850de365

现在,我在命令和实际文件中传递env_file,因为其中有我的docker组成文件需要的变量和flaskapp需要的变量。而且肯定有重叠。
从prod.env或dev.env文件中获取值以进行docker-compose不是问题。把它放到我的FASK应用程序上也不是。问题是如何将这些值保存到dockerfile中。

yyhrrdl8

yyhrrdl81#

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

ARG ARG1
RUN echo $env:ARG1

这看起来很合适,特别是因为在Windows服务器上使用Windows容器不是我的第一选择,所以如果您有环境文件之类的问题,请查看@ErikMD的答案。

vq8itlhq

vq8itlhq2#

我发布了一个新的答案,以强调与OP的问题相关的各种假设,特别是".env"唯一文件名和*.env文件(env_file:的参数)之间存在细微差异的事实。
但除了这一微妙之处,将参数从docker-compose.yml传递到docker build -f Dockerfile .和/或docker run -e …的过程很简单,如下面的综合示例所示。

最小工作示例

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

services:
  demo-1:
    image: demo-${ENV_NUM}
    build:
      context: .
      args:
        ARG1: "demo-1/${ARG1}"
        ARG3: "demo-1/${ARG3}"
  demo-2:
    image: demo-2${ENV_FILE_NUM}
    build:
      context: .
      args:
        ARG1: "demo-2/${ARG1}"
        ARG3: "demo-2/${ARG3}"
    env_file:
      - var.env

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

文件.env

KEY="some value"
ENV_NUM=1
ARG1=.env/ARG1
ARG2=.env/ARG2
ARG3=.env/ARG3

文件var.env

ENV_FILE_NUM="some number"
ARG1=var.env/ARG1
ARG2=var.env/ARG2
ARG3=var.env/ARG3
ARG4=var.env/ARG4

文件Dockerfile

FROM debian:10

# Read build arguments (default value if omitted at CLI)

ARG ARG1="default 1"
ARG ARG2="default 2"
ARG ARG3="default 3"

# the build args are exported at build time

RUN echo "ARG1=${ARG1}" | tee /root/arg1.txt
RUN echo "ARG2=${ARG2}" | tee /root/arg2.txt
RUN echo "ARG3=${ARG3}" | tee /root/arg3.txt

# Export part of these args at runtime also

ENV ARG1="${ARG1}"
ENV ARG2="${ARG2}"

# exec-form is mandatory for ENTRYPOINT/CMD

CMD ["/bin/bash", "-c", "echo ARG1=\"${ARG1}\" ARG2=\"${ARG2}\" ARG3=\"${ARG3}\"; echo while at build time:; cat /root/arg{1,2,3}.txt"]

实验一

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

$ docker-compose config

WARN[0000] The "ENV_FILE_NUM" variable is not set. Defaulting to a blank string. 
name: docker
services:
  demo-1:
    build:
      context: /home/debian/docker
      dockerfile: Dockerfile
      args:
        ARG1: demo-1/.env/ARG1
        ARG3: demo-1/.env/ARG3
    image: demo-1
    networks:
      default: null
  demo-2:
    build:
      context: /home/debian/docker
      dockerfile: Dockerfile
      args:
        ARG1: demo-2/.env/ARG1
        ARG3: demo-2/.env/ARG3
    environment:
      ARG1: var.env/ARG1
      ARG2: var.env/ARG2
      ARG3: var.env/ARG3
      ARG4: var.env/ARG4
      ENV_FILE_NUM: some number
    image: demo-2
    networks:
      default: null
networks:
  default:
    name: docker_default

在这里,正如警告所指出的,我们看到,尽管var.env提到了这个变量,但是对ENV_FILE_NUM进行内插存在问题。原因是**env_file s行只是为底层的docker run -e …命令添加了新的环境变量,但没有在docker-compose.yml中插入任何内容**。
相反,人们可以注意到,取自**".env"**的值ARG1=.env/ARG1被内插在docker-compose.ymlargs:字段内。输出行:

args:
  ARG1: demo-1/.env/ARG1
  …

官方文档的这一页描述了".env"env_file的这种截然不同的语义。

实验二

接下来,让我们运行:

$ docker-compose up --build

WARN[0000] The "ENV_FILE_NUM" variable is not set. Defaulting to a blank string.                                                                     
[+] Building 10.4s (13/13) FINISHED
 => [demo-1 internal] load build definition from Dockerfile
 => => transferring dockerfile: 609B
 => [demo-2 internal] load build definition from Dockerfile
 => => transferring dockerfile: 609B
 => [demo-1 internal] load .dockerignore
 => => transferring context: 2B
 => [demo-2 internal] load .dockerignore
 => => transferring context: 2B
 => [demo-2 internal] load metadata for docker.io/library/debian:10
 => [demo-2 1/4] FROM docker.io/library/debian:10@sha256:ebe4b9831fb22dfa778de4ffcb8ea0ad69b5d782d4e86cab14cc1fded5d8e761
 => => resolve docker.io/library/debian:10@sha256:ebe4b9831fb22dfa778de4ffcb8ea0ad69b5d782d4e86cab14cc1fded5d8e761
 => => sha256:85bed84afb9a834cf090b55d2e584abd55b4792d93b750db896f486680638344 50.44MB / 50.44MB
 => => sha256:ebe4b9831fb22dfa778de4ffcb8ea0ad69b5d782d4e86cab14cc1fded5d8e761 1.85kB / 1.85kB
 => => sha256:40dd1c1b1c36eac161ab63b6ce3a57d56ad79a667a37717a31721bac3f30aaf9 529B / 529B
 => => sha256:26a2b081e03207d26a105340161109ba0f00e857cbb0ff85aaeeeadd46b709c5 1.46kB / 1.46kB
 => => extracting sha256:85bed84afb9a834cf090b55d2e584abd55b4792d93b750db896f486680638344
 => [demo-2 2/4] RUN echo "ARG1=demo-2/.env/ARG1" | tee /root/arg1.txt
 => [demo-1 2/4] RUN echo "ARG1=demo-1/.env/ARG1" | tee /root/arg1.txt
 => [demo-1 3/4] RUN echo "ARG2=default 2" | tee /root/arg2.txt
 => [demo-2 3/4] RUN echo "ARG2=default 2" | tee /root/arg2.txt
 => [demo-2 4/4] RUN echo "ARG3=demo-2/.env/ARG3" | tee /root/arg3.txt
 => [demo-1 4/4] RUN echo "ARG3=demo-1/.env/ARG3" | tee /root/arg3.txt
 => [demo-2] exporting to image
 => => exporting layers
 => => writing image sha256:553f294a410ceeb3c0ac9d252d443710c804d3f7437ad7fffa586967517f5e7a
 => => naming to docker.io/library/demo-1
 => => writing image sha256:84bb2bd0ffae67ffed0e74efbf9253b6d634a6f37c6f99bc4eedea81846a9352
 => => naming to docker.io/library/demo-2

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
[+] Running 3/3              
 ⠿ Network docker_default     Created
 ⠿ Container docker-demo-1-1  Created
 ⠿ Container docker-demo-2-1  Created
Attaching to docker-demo-1-1, docker-demo-2-1

docker-demo-1-1  | ARG1=demo-1/.env/ARG1 ARG2=default 2 ARG3=
docker-demo-1-1  | while at build time:
docker-demo-1-1  | ARG1=demo-1/.env/ARG1
docker-demo-1-1  | ARG2=default 2
docker-demo-1-1  | ARG3=demo-1/.env/ARG3

docker-demo-2-1  | ARG1=var.env/ARG1 ARG2=var.env/ARG2 ARG3=var.env/ARG3
docker-demo-2-1  | while at build time:
docker-demo-2-1  | ARG1=demo-2/.env/ARG1
docker-demo-2-1  | ARG2=default 2
docker-demo-2-1  | ARG3=demo-2/.env/ARG3

docker-demo-1-1 exited with code 0
docker-demo-2-1 exited with code 0

在这里,我们可以再次看到".env"file_env: [ filename.env ]的值扮演着不同的角色,这些角色不会重叠。
此外:

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

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

docker-compose --env-file选项用例备注

正如OP所提到的,docker-compose还享有一个有用的CLI选项--env-file(其名称与完全不同的env-file:字段的命名方式完全相同,这很不幸,但没关系)。
此选项支持以下用例(摘自OP的代码):
文件docker-compose.yml

services:
  home:
    image: home-${ENV_NUM}
    build:
      args:
        ARG1: "${ARG1}"
      ...
    labels:
      - traefik.http.routers.home.rule=Host(`${DOMAIN}`)
      ...
    env_file:
      - ${ENV}

文件prod.env

DOMAIN = 'actualdomain.com'
ENV = 'prod.env'
ENV_NUM = 1
ARG1 = 'value 1'

文件dev.env

DOMAIN = 'localhost'
ENV = 'dev.env'
ENV_NUM = 0
ARG1 = 'value 1'

然后运行:

  • docker-compose --env-file prod.env build
  • docker-compose --env-file dev.env build

顺便说一句,即使到目前为止这个答案的大部分内容都相当于说明".env"文件名和env_file:文件享有非常不同的语义…确实,按照操作人员的建议,它们也可以以这种方式“很好地”结合在一起,以实现这个用例。
顺便说一句,**docker-compose config**也适用于对编写规范进行调试:

  • docker-compose --env-file prod.env config
  • docker-compose --env-file dev.env config

现在,关于最后一个问题:
prod.envdev.env文件中的值获取到docker-compose不是问题。问题是如何将这些值传递给Dockerfile
首先可以注意到,有两种不同的情况:
1.两个不同的部署环境(prod.envdev.env)可以共享同一个镜像,所以区别只在于运行时环境变量(而不是docker构建参数)。
1.或者,根据为--env-file传递的文件,图像应该不同(然后确实需要docker-compose --env-file … build)。
似乎在大多数情况下,情况1.是可以实现的(在问题的配置中也是如此,因为prod.envdev.env中的ARG1值是相同的),并且出于重现性的原因可以被视为更有趣(因为我们确信“prod”图像将与“dev”图像相同)。
然而,有时不可能做到这一点,而我们是“在情况2”。例如,如果Dockerfile有一个特定的步骤,可能与测试或其他相关,则必须启用该步骤(Resp.禁用)。
那么现在,让我们假设我们是在案例2中。我们如何才能传递从--env-fileDockerfile的“一切”呢?只有一个解决方案,即扩展docker-compose.ymlargs:Map,并包含您感兴趣的每个变量,例如:

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

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

退款?

我看到的唯一缺点是在运行时可能会有不需要的额外环境变量(从docker-compose到底层的docker run -e …),比如ENV=prod.env
如果这是一个问题,您可能希望拆分".env"文件,如下所示:
文件prod.env

DOMAIN = 'actualdomain.com'
ENV = 'prod-run.env'
ENV_NUM = 1
ARG1 = 'value 1'

文件prod-run.env

DOMAIN = 'actualdomain.com'
ENV_NUM = 1

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

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

args:
  DOMAIN: "${DOMAIN}"
  ENV_NUM: "${ENV_NUM}"

写入Dockerfile

ARG DOMAIN
ARG ENV_NUM

# ... and in the end:

ENV DOMAIN="${DOMAIN}"
ENV ENV_NUM="${ENV_NUM}"

我已经在**“实验会话2”一节中给出了这些Dockerfile指令的示例。**
(很抱歉,这个答案太长了,BTW:)

lb3vh1jj

lb3vh1jj3#

您可以使用docker Compose中的.env文件,以便使用您在服务定义中定义的相同时间:

services:
    app:
         build:
            context: .\app
            args:
               ARG1: ${ARG3}
               ARG2: ${ARG4}
         env_file:
            - .env

通过这种方式,两者可以共享相同的env文件,但仍然存在将变量重新定义为占位符的缺点。
这是一个建议,选择一个更适合你的

相关问题