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

时间:2019-04-30 13:23:24

标签: docker dockerfile

我正在创建具有类似以下docker项目之类问题的映像:

Dockerfile

FROM alpine:3.9.3

COPY ./env.sh /env.sh
RUN source /env.sh
CMD env

env.sh

TEST=test123

我用

构建图像
docker build -t sandbox .

并运行

docker run --rm sandbox

输出为

HOSTNAME=72405c43801b
SHLVL=1
HOME=/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/

我的环境变量丢失。

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

编辑: 在实际项目中,文件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

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

5 个答案:

答案 0 :(得分:3)

每个Dockerfile RUN步骤都运行一个新容器和一个新shell。如果尝试在一个shell中设置环境变量,则以后将看不到该变量。例如,您可以尝试以下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 "

对此有三个好的解决方案。从最容易/最好到最难/最复杂:

  1. 完全避免使用环境变量。将软件安装到/usr之类的“系统”位置;无论如何,它将被隔离在Docker映像中。 (不要使用Python虚拟环境之类的其他隔离工具,也不能使用nvmrvm之类的版本管理器;只需安装所需的特定工具。)

  2. 使用ENV可以起作用:

    FROM busybox
    ENV FOO=foo2
    ENV BAR=bar
    CMD echo FOO is $FOO, BAR is $BAR
    # Prints "FOO is foo2, BAR is bar"
    
  3. 使用入口点脚本。通常如下所示:

    #!/bin/sh
    # Read in the file of environment settings
    . /opt/wherever/env
    # Then run the CMD
    exec "$@"
    

    COPY将此脚本放入您的Dockerfile中。使其成为ENTRYPOINT;使CMD成为您实际运行的东西。

    FROM busybox
    WORKDIR /app
    COPY entrypoint.sh .
    COPY more_stuff .
    ENTRYPOINT ["/app/entrypoint.sh"]
    CMD ["/app/more_stuff/my_app"]
    

    如果您关心此类事情,则通过此方法设置的环境变量将不会在docker inspectdocker exec调试外壳中显示;但是如果您docker run -it ... sh,它们将可见。这是一个有用且足够重要的模式,除非我专门尝试进行此类初次设置,否则我几乎总是在Dockerfile中使用CMD

答案 1 :(得分:2)

尽管有一个很好的可接受的答案和建议,但还有其他方法可以实现这一点,包括一种方法,该方法在某种程度上更倾向于从bash脚本获取问题的原始意图并设置值与ENV

此外,如果存在用例需要跨多个图像维护一组通用值的情况,则有人可能希望采用这种方式来采购bash文件并将这些值注入到环境中。当前的答案并未提供涵盖此用例的解决方案,而是允许通过ENV注入环境变量。通过ENTRYPOINT注入值将无法在同一dockerfile中的后续RUN命令中利用这些值。

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

方法1-构建Arg和脚本

通常,我倾向于用构建脚本来包装我的docker构建,以帮助标准化映像构建(即在企业环境中),即使是简单的用例也是如此。通常,我将--pull添加到从移动标签(例如ltsstable等)中提取的docker构建中,然后在适当时添加自定义构建args(例如,更改基数或{ {1}}的docker镜像版本。

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

Dockerfile

FROM

env.sh

FROM alpine:3.9.3

ARG test_val=
ENV TEST ${test_val}
CMD env

build.sh

export TEST=test123

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

. env.sh
docker build --pull --build-arg test_val=${TEST} -t sandbox .

然后运行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

方法2-基本Dockerfile

与其在bash脚本中维护这些值以获取映像中的资源,不如简单地创建一个“公共” dockerfile,该文件在公共基础映像中设置所有这些环境变量。然后,将$ 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=/ 设置为公共图像,而不是将FROM设置为该公共基本图像。这是一个简单的示例:

Dockerfile.base

FROM

Dockerfile1.frombase

FROM alpine:3.9.3

ENV TEST test123

Dockerfile2.frombase

FROM sandbox-base

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

CMD env

现在构建所有图像:

FROM sandbox-base

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

CMD env

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

docker build -f Dockerfile.base -t sandbox-base .
docker build -f Dockerfile1.frombase -t sandbox-image1 .
docker build -f Dockerfile2.frombase -t sandbox-image2 .

答案 2 :(得分:0)

我最终在bash脚本中对dockerfile进行了多步构建。

步骤1.设置Dockerfile,使其包含所有内容,直到需要为环境变量提供文件的源为止。

步骤2。在docker文件源中,环境变量将环境回显到文件中。

运行源$(pwd)/buildstepenv_rhel72_64.sh &&源/ opt / rh / devtoolset-8 / enable && env |排序-u> /tmp.env“

第3步使用标签构建图像。

docker build -t $ {image} _dev。

第4步,使用标签运行映像以获取环境变量并将其添加到docker文件的末尾

docker run --rm $ {image} _dev cat /tmp.env | sed's / $ /“ /; s / = / =” /; s / ^ / ENV /'>> logs / docker / Dockerfile。$ {step}

第5步构建您的dockerfile的其余部分

答案 3 :(得分:0)

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

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

ENTRYPOINT ["sh", "-c", "source /env.sh && \"$@\"", "-s"]

答案 4 :(得分:-1)

这应该可以解决问题:

RUN export $(cat env.sh | xargs)

希望有帮助!