Dockerfile构建镜像后SDKMAN_DIR环境变量无法读取问题求助
我刚接触Dockerfile,遇到了一个问题。我使用Podman基于itext/dito-sdk:2.4.5基础镜像构建新镜像,用来设置额外权限。从基础镜像的构建步骤中可知,第8层执行了如下命令:
/bin/sh -c export SDKMAN_DIR="/home/javaUser/.sdkman" && curl -s "https://get.sdkman.io" | bash
该命令设置了SDKMAN_DIR变量为/home/javaUser/.sdkman,但我用自己构建的镜像启动容器后,exec进入容器执行echo $SDKMAN_DIR,结果为空——我原本以为新镜像会继承这个变量。
bash-5.1$ echo $SDKMAN_DIR
我使用的Dockerfile内容如下:
FROM docker.io/itext/dito-sdk:2.4.5 # Make available to rootless users USER root RUN chmod g+x /home/javaUser/.sdkman/bin/sdkman-init.sh # Become rootless user USER 2000 EXPOSE 8080 ENTRYPOINT /bin/bash -c "source /home/javaUser/.sdkman/bin/sdkman-init.sh && kotlin /opt/dito/startup-prepare.main.kts && java -jar /opt/dito/dito-sdk-docker.jar server /etc/opt/dito/config.yml"
后来我通过添加ENV指令解决了问题,但仍不清楚为何不显式定义ENV变量就无法读取该值,这是否与exec进入容器后使用的是2000用户而非root用户有关?
核心原因
基础镜像里的SDKMAN_DIR是在RUN命令中通过export声明的,这种变量仅在当前RUN步骤的shell会话中生效,不会被持久化到镜像的全局环境变量中,后续的构建步骤、容器运行时(包括exec进入的新shell)都无法继承这个变量。
这和你exec进入容器使用的用户无关——不管是root还是2000用户,该变量都没有被设置为镜像的环境变量,自然读取不到。
解决方式
你已经找到正确的解决方法:在Dockerfile中使用ENV指令显式声明该变量,示例如下:
FROM docker.io/itext/dito-sdk:2.4.5 # 显式设置全局环境变量 ENV SDKMAN_DIR="/home/javaUser/.sdkman" USER root RUN chmod g+x /home/javaUser/.sdkman/bin/sdkman-init.sh USER 2000 EXPOSE 8080 ENTRYPOINT /bin/bash -c "source /home/javaUser/.sdkman/bin/sdkman-init.sh && kotlin /opt/dito/startup-prepare.main.kts && java -jar /opt/dito/dito-sdk-docker.jar server /etc/opt/dito/config.yml"
ENV指令会将变量持久化到镜像的环境变量中,容器运行时(包括exec新开的shell会话)都会自动继承这个变量。
补充说明:你在ENTRYPOINT中通过source /home/javaUser/.sdkman/bin/sdkman-init.sh加载的初始化脚本,也会设置SDKMAN_DIR,但这个设置仅在ENTRYPOINT启动的进程会话中有效;exec进入容器是新开的独立shell,无法读取脚本中设置的变量——而通过ENV声明后,新开的shell可以直接获取到该变量。
内容的提问来源于stack exchange,提问作者M. Groenhout

