如何在 docker 构建过程中使用环境变量获取脚本?

Tho*_*lik 11 docker dockerfile

我正在创建一个具有类似问题的图像,如以下 docker 项目:

文件

FROM alpine:3.9.3

COPY ./env.sh /env.sh
RUN source /env.sh
CMD env
Run Code Online (Sandbox Code Playgroud)

环境文件

TEST=test123
Run Code Online (Sandbox Code Playgroud)

我用

docker build -t sandbox .
Run Code Online (Sandbox Code Playgroud)

并运行它

docker run --rm sandbox
Run Code Online (Sandbox Code Playgroud)

输出是

HOSTNAME=72405c43801b
SHLVL=1
HOME=/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
Run Code Online (Sandbox Code Playgroud)

我的环境变量丢失了。

在实际项目中,我必须为 IBM DB2 客户端的安装提供更长的复杂脚本,该脚本还设置了环境变量。如何在不阅读整个安装过程并ENV在 dockerfile 中设置所有变量的情况下实现它?

编辑:在实际项目中,该文件env.sh是作为安装过程的一部分创建的,并且不能从容器外部使用。环境变量的设置取决于它在其上执行的系统。如果我在主机上运行它,它会在来宾中设置错误的变量。

真实脚本的一部分是

if [ -f ${INST_DIR?}/tools/clpplus.jar ]; then
    AddRemoveString CLASSPATH ${INST_DIR?}/tools/clpplus.jar a
fi

if [ -f ${INST_DIR?}/tools/antlr-3.2.jar ]; then
    AddRemoveString CLASSPATH ${INST_DIR?}/tools/antlr-3.2.jar a
fi

if [ -f ${INST_DIR?}/tools/jline-0.9.93.jar ]; then
    AddRemoveString CLASSPATH ${INST_DIR?}/tools/jline-0.9.93.jar a
fi

if [ -f ${INST_DIR?}/java/db2jcc.jar ]; then
    AddRemoveString CLASSPATH ${INST_DIR?}/java/db2jcc.jar a
fi

if [ -f ${INST_DIR?}/java/db2jcc_license_cisuz.jar ]; then
    AddRemoveString CLASSPATH ${INST_DIR?}/java/db2jcc_license_cisuz.jar a
fi
Run Code Online (Sandbox Code Playgroud)

它检查安装并根据此设置变量。由于主机上没有安装 DB2,因此不会设置变量。

Dav*_*aze 17

Each Dockerfile RUN step runs a new container and a new shell. If you try to set an environment variable in one shell, it will not be visible later on. For example, you might experiment with this Dockerfile:

FROM busybox
ENV FOO=foo1
RUN export FOO=foo2
RUN export BAR=bar
CMD echo FOO is $FOO, BAR is $BAR
# Prints "FOO is foo1, BAR is "
Run Code Online (Sandbox Code Playgroud)

There are three good solutions to this. In order from easiest/best to hardest/most complex:

  1. Avoid needing the environment variables at all. Install software into “system” locations like /usr; it will be isolated inside the Docker image anyways. (Don’t use an additional isolation tool like Python virtual environments, or a version manager like nvm or rvm; just install the specific thing you need.)

  2. Use ENV. This will work:

    FROM busybox
    ENV FOO=foo2
    ENV BAR=bar
    CMD echo FOO is $FOO, BAR is $BAR
    # Prints "FOO is foo2, BAR is bar"
    
    Run Code Online (Sandbox Code Playgroud)
  3. Use an entrypoint script. This typically looks like:

    #!/bin/sh
    # Read in the file of environment settings
    . /opt/wherever/env
    # Then run the CMD
    exec "$@"
    
    Run Code Online (Sandbox Code Playgroud)

    COPY this script into your Dockerfile. Make it be the ENTRYPOINT; make the CMD be the thing you’re actually running.

    FROM busybox
    WORKDIR /app
    COPY entrypoint.sh .
    COPY more_stuff .
    ENTRYPOINT ["/app/entrypoint.sh"]
    CMD ["/app/more_stuff/my_app"]
    
    Run Code Online (Sandbox Code Playgroud)

    If you care about such things, environment variables you set via this approach won’t be visible in docker inspect or a docker exec debug shell; but if you docker run -it ... sh they will be visible. This is a useful and important enough pattern that I almost always use CMD in my Dockerfiles unless I’m specifically trying to do first-time setup like this.


Tuk*_*Tuk 5

我找到了一个我更喜欢的替代选项:

配置 ENTRYPOINT dockerfile 步骤,该步骤获取文件,然后运行参数接收的 CMD:

ENTRYPOINT ["sh", "-c", "source /env.sh && \"$@\"", "-s"]
Run Code Online (Sandbox Code Playgroud)

  • 理想情况下, `"$@"` (在任何入口点脚本的末尾)应该替换为 `exec "$@"` (2认同)

Haz*_*zok 5

尽管有一个很好的公认答案和建议,但还有其他方法可以实现这一目标,包括一种更接近问题原始意图的方法,即从 bash 脚本获取并使用ENV.

此外,如果存在需要跨多个图像维护一组通用值的用例,有人可能希望采用这种获取 bash 文件并将值注入环境的方法。当前的答案没有提供涵盖此用例并允许通过注入环境变量的解决方案ENV。通过注入值会阻止在同一 dockerfile 中的ENTRYPOINT后续命令中利用这些值的能力。RUN

方法 1 更适合问题的初衷,即从 bash 脚本中获取值,而方法 2 提供了利用通用 dockerfile 的类似方法。

方法 1 - 构建参数和脚本

很多时候,我倾向于使用构建脚本来包装我的 docker 构建,以帮助标准化映像构建(即在企业环境中),即使对于简单的用例也是如此。通常,我会添加一个--pull从移动标签(例如,等)中提取的 docker 构建lts,然后在适当的时候添加自定义构建参数(例如,改变Docker 映像构建的stable基础或)。FROM

当这样的构建脚本已经存在时,在某些情况下,利用传递到脚本中的构建参数,然后根据需要将环境变量设置为这些值可能更有意义。下面是一个简单的例子。

Dockerfile

FROM alpine:3.9.3

ARG test_val=
ENV TEST ${test_val}
CMD env
Run Code Online (Sandbox Code Playgroud)

环境变量

export TEST=test123
Run Code Online (Sandbox Code Playgroud)

构建.sh

. env.sh
docker build --pull --build-arg test_val=${TEST} -t sandbox .
Run Code Online (Sandbox Code Playgroud)

现在运行构建脚本来构建 docker 镜像:

$ bash build.sh
Sending build context to Docker daemon  7.168kB
Step 1/4 : FROM alpine:3.9.3
3.9.3: Pulling from library/alpine
Digest: sha256:28ef97b8686a0b5399129e9b763d5b7e5ff03576aa5580d6f4182a49c5fe1913
Status: Image is up to date for alpine:3.9.3
 ---> cdf98d1859c1
Step 2/4 : ARG test_val=
 ---> Running in 0e438f2b8a4b
Removing intermediate container 0e438f2b8a4b
 ---> a15edd0a5882
Step 3/4 : ENV TEST ${test_val}
 ---> Running in 16f83a6c6d8c
Removing intermediate container 16f83a6c6d8c
 ---> 28cdd3df03ec
Step 4/4 : CMD env
 ---> Running in 3057dd2682d6
Removing intermediate container 3057dd2682d6
 ---> e7afdb4eeff2
Successfully built e7afdb4eeff2
Successfully tagged sandbox:latest
Run Code Online (Sandbox Code Playgroud)

然后运行 ​​docker 镜像来查看环境变量设置为预期值:

$ docker run --rm sandbox
HOSTNAME=008e482ab3db
SHLVL=1
HOME=/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
TEST=test123
PWD=/
Run Code Online (Sandbox Code Playgroud)

方法 2 - 基础 Dockerfile

人们可以简单地创建一个“通用”dockerfile,在通用基础镜像中设置所有这些环境变量,而不是在 bash 脚本中维护这些值以在镜像中获取源。然后将其设置FROM为公共映像,而不是设置FROM为该公共基础映像。这是一个简单的例子:

Dockerfile.base

FROM alpine:3.9.3

ENV TEST test123
Run Code Online (Sandbox Code Playgroud)

Dockerfile1.frombase

FROM sandbox-base

# Some settings specific to this image.... example:
ENV MYIMAGE1 image1

CMD env
Run Code Online (Sandbox Code Playgroud)

Dockerfile2.frombase

FROM sandbox-base

# Some different settings specific to this image....
ENV MYIMAGE2 image2

CMD env
Run Code Online (Sandbox Code Playgroud)

现在构建所有图像:

docker build -f Dockerfile.base -t sandbox-base .
docker build -f Dockerfile1.frombase -t sandbox-image1 .
docker build -f Dockerfile2.frombase -t sandbox-image2 .
Run Code Online (Sandbox Code Playgroud)

然后运行两个目标图像进行比较:

$ docker run --rm sandbox-image1
HOSTNAME=6831172af912
SHLVL=1
HOME=/root
MYIMAGE1=image1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
TEST=test123
PWD=/

$ docker run --rm sandbox-image2
HOSTNAME=fab3c588e85a
SHLVL=1
HOME=/root
MYIMAGE2=image2
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
TEST=test123
PWD=/
Run Code Online (Sandbox Code Playgroud)