Docker-Compose构建环境变量

91
TL;DR : 如何在使用docker-compose构建镜像时传递环境变量,并使docker run image command识别它们?

我有这个Dockerfile:

FROM mhart/alpine-node:10
ADD . /app
WORKDIR /app
RUN apk --no-cache add --virtual builds-deps build-base python &&\
    yarn global add nodemon &&\
    yarn &&\
    apk del builds-deps build-base python

还有这个docker-compose.yml文件:

version: "3.3"
services:

  api:
    build:
      context: .
      dockerfile: Dockerfile-preprod
    image: registry.git.louis-girones.fr:4567/make-your-night/back:preprod
    environment:
      - NODE_ENV=development
    env_file:
      - .env
    volumes:
      - .:/app
    ports:
      - "${PORT_PREPROD}:${PORT_PREPROD}"
    command: sh -c "mkdir -p dist && touch ./dist/app.js && yarn run start"

  mongo:
    image: mongo:4.0
    ports:
      - "${MONGO_PREPROD}"
    command: mongod
    volumes:
      - ./data:/data/db

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.1.1
    volumes:
      - ./esdata:/usr/share/elasticsearch/data
    environment:
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - discovery.type=single-node
    ports:
      - "9300:9300"
      - "9200:9200"

volumes:
  esdata:

通过这个.env文件(在根目录中,就像docker-compose.yml和Dockerfile一样):

#!/usr/bin/env bash

NODE_ENV=development
PORT=9000
SECRET_SESSION=superSecr3t
APP_NAME=Night Vision
API_VERSION=/api/v0/
DEFAULT_TZ=Europe/Paris
ASSETS_URI=http://localhost:9000/public/img/
BCRYPT_WORKFACTOR=1
ES_PORT=9200
ES_LOG_LEVEL=trace

而这段代码是在Node服务器启动时执行的:

// Export the config object based on the NODE_ENV
// ==============================================
const config: IConfig = commonConfig

if (commonConfig.env === 'development') {
    _.merge(config, developmentConfig)
} else if (commonConfig.env === 'test') {
    _.merge(config, testConfig)
} else if (commonConfig.env === 'preproduction') {
    _.merge(config, preproductionConfig)
} else if (commonConfig.env === 'production') {
    _.merge(config, productionConfig)
} else {
    throw new Error('Please set an environment')
}
当我运行docker-compose build命令时,一切都很好,但是例如,如果我尝试docker run myimage yarn run test,会抛出错误“请设置环境变量”。
我期望
env_file:
  - .env
这使得此文件的环境变量可以在我的镜像中访问,但实际情况并非如此,这就是为什么我尝试添加的原因。
 environment:
  - NODE_ENV=development

但仍然没有成功,我也尝试在构建时将我的环境变量作为命令行参数传递:

docker-compose build --build-arg NODE_ENV=development api

但是它给了我这个消息:

[Warning] One or more build-args [NODE_ENV] were not consumed
Successfully built 9b14dd5abc3f

我更喜欢使用第一种或第二种方法。

docker版本:18.06.1-ce docker-compose版本:1.19.0


打开你的容器并输入 echo $NODE_ENV,查看结果。它是 development 吗? - Kevin Kopf
@alex-karshin(不确定如何在评论中标记用户...)不,这是一个空白行。 - L. Faros
好的,请尝试从第一行中删除“#!/usr/bin/env bash”。我认为这可能是您的问题。 - Kevin Kopf
实际上有人回答我说要添加 ENV NODE_ENV=development,这个方法确实有效,但是我没有时间接受它,而且我更喜欢像 ENV NODE_ENV=${NODE_ENV} 这样的方法,但似乎不起作用。 - L. Faros
即使使用了 #!/usr/bin/env bash,echo $NODE_ENV 仍显示一行空白,虽然感谢您的帮助。 - L. Faros
2个回答

150

文档中有一则注意事项:

注意:如果您的服务指定了构建选项,则环境中定义的变量在构建期间不会自动显示。请使用构建的args子选项来定义构建时环境变量。

这个也在这里有描述。首先(如上所述),您需要在Dockerfile中指定ARG

FROM mhart/alpine-node:10
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
ADD . /app
WORKDIR /app
RUN apk --no-cache add --virtual builds-deps build-base python &&\
    yarn global add nodemon &&\
    yarn &&\
    apk del builds-deps build-base python

然后编辑您的 docker-compose 文件,在构建过程中包含该参数,如下所示:

build:
  context: .
  dockerfile: Dockerfile-preprod
  args:
    - NODE_ENV=development

甚至或者更好

build:
  context: .
  dockerfile: Dockerfile-preprod
  args:
    - NODE_ENV=${NODE_ENV}

12
我们如何对环境文件进行同样的操作? - Adharsh M
6
很抱歉,docker-compose不支持这个功能。你唯一能做的就是 source .env && docker-compose ...。我知道我来晚了,但这可能有所帮助。更多信息请参考https://github.com/docker/compose/issues/5600。 - ccvhd
2
Docker Compose文件中的构建参数args不应该被指定为NODE_ENV: development或者NODE_ENV: ${NODE_ENV}吗? - Gregor Eesmaa
1
@GregorEesmaa 这是一个yaml文件,你可以使用linter来查看两种方式都可以。 - Kevin Kopf
9
我花了几个小时才意识到Docker Compose中的“环境变量”是用于运行时的。 - Michel Feldheim
显示剩余4条评论

22

根据build args文档。

在指定构建参数时,可以省略值,此时在构建时该参数的值为Compose运行环境中的值。

args:
  NODE_ENV:

此外,它将使用变量替换中所述的.env文件。

您可以使用一个名为.env的文件设置环境变量的默认值,Compose会自动查找该文件,而在shell环境中设置的值将覆盖在.env文件中设置的值。

因此,您可以创建一个如下的.env文件。

NODE_ENV=production
BASE_URL=/foo/bar/

然后只需在compose文件中将它们列为build.args下的项目,以便在构建时使它们可用,或列为environment下的项目,以便在运行时使它们可用。

version: '3.9'
services:
  app:
    build:
      context: .
      # these are available on build
      args:
        NODE_ENV:
    # these are available on run
    environment:
      BASE_URL:

这很有用。然而,这种方法带来的问题是,如果变量没有从shell或env文件中设置,Dockerfile中的默认值将被覆盖为空值。

如果想要保留某种默认值,可以使用以下方法。

version: '3.9'
services:
  app:
    build:
      context: .
      args:
        NODE_ENV: ${NODE_ENV:-dev}
    environment:
      BASE_URL: ${BASE_URL:-http://localhost:8080}

这里使用了posix参数扩展。如果变量没有设置,它将使用:-后面的值。所以在上面的示例中,它将默认使用NODE_ENV=devBASE_URL=http://localhost:8080,如果它们没有被设置。
可以通过.env文件或设置shell变量(例如:export NODE_ENV=prod)来覆盖它们。

如果您想更改正在使用的环境文件,则可以使用--env-file标志进行更改。

docker compose --env-file .my-env up

我认为环境应该是这样的。BASE_URL=${BASE_URL:-http://localhost:8080} - nk18
@nk18,环境有两种语法形式。映射和数组。我这里展示的是映射。你可以使用数组,但需要在前面加上一个破折号“-”。 - The Fool
这个无法与构建配合使用。 - undefined
@mike01010,到底是什么出了问题?据我所知,这里写的就是它的正常运作方式。事实上,我已经做过无数次了。 - undefined
@TheFool 不用理会 - 我在使用 --env-file 参数时,docker-compose build(而不是 'up')遇到了一些问题。我想现在它已经可以正常工作了。 - undefined
@mike01010,--env-file 是一个全局标志。它适用于所有的compose命令。或者至少应该是这样的。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接