Docker中传递变量主要使用ARG和ENV,虽然功能相同,但是他们的作用范围是不一样的。下面我们结合SpringBoot启动的JVM参数来详细了解下。通过本文介绍,我们可以知道这两个命令的具体使用方式。

ARG传递变量

ARG只在Dockerfile中生效,且在docker build阶段生效,构建好的镜像内不存在此环境变量。意味着在容器启动后ARG定义的变量已经无效,如果想让其生效,需要将其赋值给ENV。

#Dockerfile

vim Dockerfile

From java

VOLUME /tmp

ARG JAR_FILE=*.jar

COPY ${JAR_FILE} helloworld.jar

ARG OPTS="-Xmx512m -Xms512m -Dspring.profiles.active=test"

ENV JAVA_OPTS=${OPTS}

ENTRYPOINT ["/bin/sh","-c","java $JAVA_OPTS -jar /helloworld.jar"]

#build

docker build -t helloworld:v1 .

#run

docker run -p 8080:8080 --name helloworld helloworld:v1

#docker exec -it 29a90e0be2b3 /bin/bash

root@29a90e0be2b3:/# ps -ef|grep java

root 1 0 0 13:22 ? 00:00:00 /bin/sh -c java $JAVA_OPTS -jar /helloworld.jar

root 6 1 10 13:22 ? 00:00:07 java -Xmx512m -Xms512m -Dspring.profiles.active=test -jar /helloworld.jar

root 49 39 0 13:23 pts/0 00:00:00 grep java

此时通过ARG定义的变量OPTS,已经成功通过ENV环境变量作为java启动参数。

当然我们也可以在docker build --build-arg 传递ARG变量,此时将覆盖dockerfile内部的ARG变量。

# --build-arg 定义变量

docker build --build-arg OPTS="-Xmx216m -Xms216m -Dspring.profiles.active=test" -t helloworld:v1 .

docker run -p 8080:8080 -d --name helloworld1 helloworld:v1

docker exec -it 9aed3266d244 /bin/bash

root@9aed3266d244:/# ps -ef|grep java

root 1 0 0 13:30 ? 00:00:00 /bin/sh -c java $JAVA_OPTS -jar /helloworld.jar

root 6 1 23 13:30 ? 00:00:07 java -Xmx216m -Xms216m -Dspring.profiles.active=test -jar /helloworld.jar

root 45 39 0 13:31 pts/0 00:00:00 grep java

此时可以看到"-Xmx216m -Xms216m -Dspring.profiles.active=test"已经覆盖dockerfile中的ARG变量。

ENV传递环境变量

Dockerfile中可以通过ENV来设置环境变量,并且可以在容器启动后使用。

#Dockerfile

vim Dockerfile

From java

VOLUME /tmp

ARG JAR_FILE=*.jar

COPY ${JAR_FILE} helloworld.jar

ENV JAVA_OPTS="-Xmx512m -Xms512m -Dspring.profiles.active=test"

ENTRYPOINT ["/bin/sh","-c","java $JAVA_OPTS -jar /helloworld.jar"]

#build

docker build -t helloworld:v1 .

#run

docker run -p 8080:8080 --name helloworld helloworld:v1

#sudo docker exec -it 301aced67bb4 /bin/bash

root@301aced67bb4:/# ps -ef|grep java

root 1 0 0 12:24 ? 00:00:00 /bin/sh -c java $JAVA_OPTS -jar /helloworld.jar

root 6 1 22 12:24 ? 00:00:10 java -Xmx512m -Xms512m -Dspring.profiles.active=test -jar /helloworld.jar

root 45 39 0 12:24 pts/0 00:00:00 grep java

通过以上我们可以看出容器启动后,在dockerfile中通过ENV定义的环境变量JAVA_OPTS已经被成功应用到java启动命令中。

docker run -e传递环境变量

我们也可以使用docker run 启动容器是,通过-e参数来传递变量,这时它会覆盖Dockfile内部通过ENV定义的环境变量。

1.ENV定义变量

#Dockerfile

vim Dockerfile

From java

VOLUME /tmp

ARG JAR_FILE=*.jar

COPY ${JAR_FILE} helloworld.jar

ENV JAVA_OPTS="-Xmx512m -Xms512m -Dspring.profiles.active=test"

ENTRYPOINT ["java","-jar","/helloworld.jar"]

#build

docker build -t helloworld:v1 .

#run

docker run -p 8080:8080 --name helloworld helloworld:v1

1.查看容器内部环境变量

#docker exec 3ca86b42c732 env

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

HOSTNAME=3ca86b42c732

JAVA_OPTS=-Xmx512m -Xms512m -Dspring.profiles.active=test

LANG=C.UTF-8

JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64

JAVA_VERSION=8u111

JAVA_DEBIAN_VERSION=8u111-b14-2~bpo8+1

CA_CERTIFICATES_JAVA_VERSION=20140324

HOME=/root

通过以上命令我们可以看到容器默认的环境变量,也可以通过"docker run -e"方式添加自定义环境变量:

#通过docker run -e 传递环境变量
docker run -e JAVA_OPTS='-Xmx256m -Xms256m -Dspring.profiles.active=test' -p 8080:8080 -d --name helloworld helloworld:v1

#docker exec fed826eb25fa env

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

HOSTNAME=fed826eb25fa

JAVA_OPTS='-Xmx256m -Xms256m -Dspring.profiles.active=test'

LANG=C.UTF-8

JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64

JAVA_VERSION=8u111

JAVA_DEBIAN_VERSION=8u111-b14-2~bpo8+1

CA_CERTIFICATES_JAVA_VERSION=20140324

HOME=/root

此时我们看到通过JAVA_OPTS自定义的环境变量为’-Xmx256m -Xms256m -Dspring.profiles.active=test’,已经覆盖了ENV定义的环境变量,剩下的工作就是在容器内部来使用JAVA_OPTS 变量。

#修改Dockerfile

vim Dockerfile

From java

VOLUME /tmp

ARG JAR_FILE=*.jar

COPY ${JAR_FILE} helloworld.jar

ENV JAVA_OPTS="-Xmx512m -Xms512m -Dspring.profiles.active=test"

ENTRYPOINT ["java","$JAVA_OPTS","-jar","/helloworld.jar"]

#build

docker build -t helloworld:v2 .

#run

docker run -e JAVA_OPTS='-Xmx256m -Xms256m -Dspring.profiles.active=test' -p 8080:8080 -d --name helloworld2 helloworld:v2

此时helloworld2容器并没有正常启动,报错如下:

#docker logs 335ac14921d1

Error: Could not find or load main class $JAVA_OPTS

看来JAVA_OPTS并没有接收到环境变量参数,而将其直接识别为字符串$JAVA_OPTS,因此会被java启动命令识别为class报错,这是为什么呢?

这就要从ENTRYPOINT的shell形式和exec形式说起,这两种形式的区别在于exec形式不像shell那样能够调用环境变量,因此我们必须使用shell的形式。

#vim Dockerfile

From java

VOLUME /tmp

ARG JAR_FILE=*.jar

COPY ${JAR_FILE} helloworld.jar

ENV JAVA_OPTS="-Xmx512m -Xms512m -Dspring.profiles.active=test"

ENTRYPOINT ["/bin/sh","-c","java $JAVA_OPTS -jar /helloworld.jar"]

#build

docker build -t helloworld:v2 .

#run

docker run -e JAVA_OPTS="-Xmx256m -Xms256m -Dspring.profiles.active=test" -p 8080:8080 -d --name helloworld2 helloworld:v2

#测试

curl 127.0.0.1:8080

Hello world

此时我们启动容器,可以看到正常访问。由此我们得知通过docker run -e传输环境变量不能使用ENTRYPOINT的exec形式。

总结

通过本文的讲解,我们熟悉了docker部署SpringBoot项目时如何传递JVM参数,同时触类旁通,帮我们更好的在以后深入学习docker时打好基础。