我正在创建具有类似以下docker项目之类问题的映像:
FROM alpine:3.9.3
COPY ./env.sh /env.sh
RUN source /env.sh
CMD env
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,因此不会设置变量。
答案 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 "
对此有三个好的解决方案。从最容易/最好到最难/最复杂:
完全避免使用环境变量。将软件安装到/usr
之类的“系统”位置;无论如何,它将被隔离在Docker映像中。 (不要使用Python虚拟环境之类的其他隔离工具,也不能使用nvm
或rvm
之类的版本管理器;只需安装所需的特定工具。)
使用ENV
。此可以起作用:
FROM busybox
ENV FOO=foo2
ENV BAR=bar
CMD echo FOO is $FOO, BAR is $BAR
# Prints "FOO is foo2, BAR is bar"
使用入口点脚本。通常如下所示:
#!/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 inspect
或docker exec
调试外壳中显示;但是如果您docker run -it ... sh
,它们将可见。这是一个有用且足够重要的模式,除非我专门尝试进行此类初次设置,否则我几乎总是在Dockerfile中使用CMD
。
答案 1 :(得分:2)
尽管有一个很好的可接受的答案和建议,但还有其他方法可以实现这一点,包括一种方法,该方法在某种程度上更倾向于从bash脚本获取问题的原始意图并设置值与ENV
。
此外,如果存在用例需要跨多个图像维护一组通用值的情况,则有人可能希望采用这种方式来采购bash文件并将这些值注入到环境中。当前的答案并未提供涵盖此用例的解决方案,而是允许通过ENV
注入环境变量。通过ENTRYPOINT
注入值将无法在同一dockerfile中的后续RUN
命令中利用这些值。
方法1更适合于从bash脚本中获取值的问题的初衷,而方法2提供了利用公共dockerfile的类似方法。
通常,我倾向于用构建脚本来包装我的docker构建,以帮助标准化映像构建(即在企业环境中),即使是简单的用例也是如此。通常,我将--pull
添加到从移动标签(例如lts
,stable
等)中提取的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
与其在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)
希望有帮助!