Use build time environment variable in Docker CMD - docker

I have a Dockerfile:
...
COPY ${JAR_FILENAME}-*.jar /${JAR_FILENAME}.jar
...
CMD java -jar $(echo /${JAR_FILENAME}.jar)
And I am building the image with: docker build --build-arg JAR_FILENAME='some-file' -t some-name:tag
But when I run the container, I get:
Error: Unable to access jarfile /.jar
How do I pass the environment variables during the build time to my CMD command so that I can run java -jar some-file.jar?

You use an ENV, setting it to the value of the ARG. An ENV is nearly identical to an ARG, but gets saved to the image metadata and therefore persists to when you run the container.
...
ARG JAR_FILENAME=app.jar
ENV JAR_FILENAME=${JAR_FILENAME}
COPY ${JAR_FILENAME}-*.jar /${JAR_FILENAME}.jar
...
CMD java -jar /${JAR_FILENAME}.jar
Note that users of the image can alter ENV values, and it may be easier to just set the jar in the image to a well known name:
...
COPY ${JAR_FILENAME}-*.jar /app.jar
...
CMD java -jar /app.jar

Use the ARG construct in Docker file to pass in build time arguments from command line.
Read the docs at : https://docs.docker.com/engine/reference/builder/#arg

Related

How to concat arguments to an existing Dockerfile CMD?

Suppose we have a Dockerfile with some CMD and we produced and image from it. Now suppose that we write a docker-compose and one of the services is built from that image.
What I want to do is running the same command but concatenating my own new parameters.
As an example, suppose the original CMD is
java -jar app.jar --argumentA=valA
I want the command to be
java -jar app.jar --argumentA=valA --argumentB=valB
Is it possible?
I'm not entirely sure if this is what you would want to accomplish, but...
Dockerfile exposes both ENTRYPOINT and CMD for being able to execute commands. These also can be used in conjunction, but in this case the ENTRYPOINT will be the command what we want to execute and the CMD will represent some default arguments for the ENTRYPOINT (docs).
For example:
FROM openjdk:11
COPY . /target/app.jar
ENTRYPOINT ["java", "-jar", "app.jar", "--argumentA=valA"]
CMD ["--argumentB=valB"]
The --argumentB=valB will be appended to the java -jar app.jar --argumentA=valA, if we run the image like this:
docker build -t app .
docker run app # # the command executed will be java -jar app.jar --argumentA=valA --argumentB=valB
But the CMD part will be overridden if we provide other arguments when we run the docker image:
docker build -t app .
docker run app --argumentA=valC # the command executed will be java -jar app.jar --argumentA=valA --argumentB=valC
Also, we can commit the CMD and have the ENTRYPOINT only, if we don't require some defaults to be appended to the ENTRYPOINT.

Docker - pass env variable to replace Java max memory

I have a Dockerfile as follows.
ENV SPRING_ENV="local"
ENV APP_OPTS "-Xmx8144m"
RUN echo "/usr/lib/jvm/java-1.8-openjdk/bin/java ${APP_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /apps/demo/demo-fe.jar --spring.config.location=file:///apps/demo/conf/ump.properties -Dspring.profiles.active=${SPRING_ENV} &" > /apps/demo/entrypoint.sh
RUN chmod +x /apps/demo/entrypoint.sh
When I run the dockerfile, I see a file 'entrypoint.sh' with the java command that I specified in the Dockerfile.
But I want to change the java max memory depending on the environment. So I am running like this.
docker run -it <image_id> sh -e "APP_OPTS=-Xmx9144m" -e "SPRING_ENV=dev"
But when I run it, i check the entrypoint.sh, i don't see the environment variables replaced. Am I missing something?
Does it replace only on the fly when I actually run the container?
You need to escape the $ in ${APP_OPTS} (i.e., change it to \${APP_OPTS}) -- during docker build, the variable is getting replaced with the "current" environment variable, which would be whatever is in your env output (otherwise null). Calling docker run ... -e "APP_OPTS=-Xmx9144m" won't do anything at this point because ${APP_OPTS} has been replaced after the image was created.
Otherwise, you could try saving the entrypoint.sh file and put it in the same folder as your Dockerfile instead of having your Dockerfile create it (and use COPY instead to put it where you want it). That way, the ${APP_OPTS} environment variable won't get replaced during docker build
The Dockerfile (and the RUN command) are only executed when you build the image. SPRING_ENV and APP_UMPFE_OPTS are being evaluated only once and during the build.
When you run the image, the --env=KEY=VALUE are passed to the shell (!) running the process defined in the ENTRYPOINT or CMD (which you need but do not have).
You're missing a FROM ... statement near the top of the Dockerfile too.
You will need to define (recommend the shell-form of) ENTRYPOINT that invokes the java runtime, passes the environment variables and runs your code, perhaps (have not tried this):
FROM ???
ENV SPRING_ENV="local"
ENV APP_OPTS "-Xmx8144m"
ENTRYPOINT /usr/lib/jvm/java-1.8-openjdk/bin/java ${APP_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /apps/demo/demo-fe.jar --spring.config.location=file:///apps/demo/conf/ump.properties -Dspring.profiles.active=${SPRING_ENV}
Example:
FROM busybox
ENV DOG=Freddie
ENTRYPOINT echo ${DOG}
Then:
docker build --tag=58208029 --file=./Dockerfile .
docker run -it 58208029:latest
Freddie
docker run -it --env=DOG=Henry 58208029:latest
Henry
HTH!
The entrypoint.sh is being written when you build the image, so that RUN statement won't be executed again when you run the container. So the entrypoint.sh file itself will not be updated.
Another issue is that when you do the docker run, the -e options need to be before the image name and command:
docker run -it -e "APP_OPTS=-Xmx9144m" -e "SPRING_ENV=dev" <image_id> sh
Otherwise those are just being passed as arguments to the entrypoint/command
Also, in your Dockerfile, you probably want single quotes around your entrypoint script so that it doesn't interpolate the values at build time.
RUN echo '/usr/lib/jvm/java-1.8-openjdk/bin/java ${APP_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /apps/demo/demo-fe.jar --spring.config.location=file:///apps/demo/conf/ump.properties -Dspring.profiles.active=${SPRING_ENV} &' > /apps/demo/entrypoint.sh
Then when you run the container, the entrypoint script should read the variable values at run time from the environment.

ENV/ARG command not populating variables in Dockerfile

I'm trying to create a nanoserver w/ nodejs base image, but I can't seem to get the ARG (or ENV) command to work properly.
My docker file:
FROM microsoft/nanoserver
ENV NODE_VERSION=8.11.4
ADD https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-win-x64.zip C:\\build\\node-v${NODE_VERSION}-win-x64.zip
RUN powershell -Command Expand-Archive C:\build\node-v${NODE_VERSION}-win-x64.zip C:\; Rename-Item C:\node-v${NODE_VERSION}-win-x64 node
RUN SETX PATH C:\node
ENTRYPOINT C:\node\node.exe
Build command:
docker build . -t base-image:latest
It downloads the zip file, but when it tries to rename the downloaded file it throws an error:
Expand-Archive : The path 'C:\build\node-v-win-x64.zip' either does not exist
or is not a valid file system path.
According to the ENV documentation:
Environment variables are supported by the following list of
instructions in the Dockerfile:
ADD COPY ENV EXPOSE FROM LABEL STOPSIGNAL USER VOLUME WORKDIR as well
as:
ONBUILD (when combined with one of the supported instructions above)
Therefore it appears variables defined with ENV are not supported by the RUN directive.
However, you can instead replace the ENV directive with the ARG directive and NODE_VERSION will be availablein subsequent RUN directives.
Example:
FROM microsoft/nanoserver
ARG NODE_VERSION=8.11.4
ADD https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-win-x64.zip C:\\build\\node-v${NODE_VERSION}-win-x64.zip
RUN powershell -Command Expand-Archive C:\build\node-v${NODE_VERSION}-win-x64.zip C:\; Rename-Item C:\node-v${NODE_VERSION}-win-x64 node
RUN SETX PATH C:\node
ENTRYPOINT C:\node\node.exe
Additionally you can override the value of NODE_VERSION in your docker build command.
$ docker build -t base-image:latest --build-arg NODE_VERSION=10.0.0 .
Using the ARG directive will not make NODE_VERSION available in the environment of a running container. Depending on your use case you may also need to use an additional ENV definition.
Found an answer here:
https://github.com/docker/for-win/issues/542
Essentially - within the powershell commands the %VARIABLE_NAME% format has to be used:
FROM microsoft/nanoserver
ENV NODE_VERSION=8.11.4
ADD https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-win-x64.zip C:\\build\\node-v${NODE_VERSION}-win-x64.zip
RUN powershell -Command Expand-Archive C:\build\node-v%NODE_VERSION%-win-x64.zip C:\; Rename-Item C:\node-v%NODE_VERSION%-win-x64 node
RUN SETX PATH C:\node
ENTRYPOINT C:\node\node.exe

User-provided environment variable within docker CMD

I have successfully pushed my docker image to the swisscom app cloud (similar to this example: https://ict.swisscom.ch/2016/05/docker-and-cloudfoundry/).
Now I would like to use a user-provided environment variable within my docker CMD. Something like this:
ADD target/app.jar app.jar
CMD java -jar app.jar -S $USER_PROVIDED_ENV_VARIABLE
I also tried system-provided environment variables:
ADD target/app.jar app.jar
CMD java -jar app.jar -S $VCAP_APPLICATION
What am I doing wrong here?
If your Dockerfile is built like that, you'll simply need to pass the -e flag when running the image.
Example Dockerfile:
FROM ubuntu:16.10
ENV MY_VAR "default value" # Optional - set a default value.
CMD echo $MY_VAR
Build the image:
docker build -t my_image .
Run a container from the image:
docker run -e MY_VAR="my value here" my_image

Dockerfile placeholders not replaced during build

Using Docker for Windows, Community version 17.06.0-ce-win19 (12801), Windows 10
Dockerfile
FROM frolvlad/alpine-oraclejdk8:slim
ARG APP_NAME=client-default
RUN mkdir -p /client/
ADD build/libs/$APP_NAME.jar /client/$APP_NAME.jar
ENTRYPOINT ["java", "-jar", "/client/$APP_NAME.jar"]
Running
docker build --build-arg APP_NAME=client-1 -t test/client-1 .
Placeholders not replaced after build (tried ${APP_NAME}, %APP_NAME%)
ARG and ENV are not replaced by Docker in an ENTRYPOINT or a CMD when you use the EXEC form inside []. If the ENTRYPOINT or CMD use the shell form (string, not an array) the shell will be able to do the variable substitution for you.
ARG and ENV variables will be available in RUN commands in the container as environment variables.
Docker will also replace $VARIABLES in the Dockerfile in the following instructions:
ADD
COPY
ENV
EXPOSE
FROM
LABEL
STOPSIGNAL
USER
VOLUME
WORKDIR
Only ENV variables will become available in a CMD or ENTRYPOINT, only in the environment of the running container:
Using ARG
FROM frolvlad/alpine-oraclejdk8:slim
ARG APP_NAME=client-default
ENV APP_NAME=$APP_NAME
RUN mkdir -p /client/
ADD build/libs/$APP_NAME.jar /client/$APP_NAME.jar
ENTRYPOINT ["sh", "-c", "java -jar /client/$APP_NAME.jar"]
or with only ENV
FROM frolvlad/alpine-oraclejdk8:slim
ENV APP_NAME=client-default
RUN mkdir -p /client/
ADD build/libs/$APP_NAME.jar /client/$APP_NAME.jar
ENTRYPOINT ["sh", "-c", "java -jar /client/$APP_NAME.jar"]
Environment variables in Dockerfile are declared with the ENV statement.
In your case:
FROM frolvlad/alpine-oraclejdk8:slim
ENV APP_NAME client-default
RUN mkdir -p /client/
ADD build/libs/${APP_NAME}.jar /client/${APP_NAME}.jar
ENTRYPOINT ["java", "-jar", "/client/${APP_NAME}.jar"]
ARG is only available during the build of a Docker image (RUN etc), not after the image is created and containers are started from it (ENTRYPOINT OR CMD)
ARG variable doesn't get substituted in Dockerfile. ARG IS really an environment variable. During the build, docker "runs" the container using ARG variables as environment variables. Easy to prove (you can test it): Try RUN printenv on your Dockerfile and you'll see the ARG as environment variable
So, you've got two problems:
First:
The Steps in console DOESN'T substitute the ARG variable. So, your jar is really in the container, your line:
ADD build/libs/$APP_NAME.jar /client/$APP_NAME.jar
it's working. Try it.
Second:
Entrypoint is for the running container, not for the image, so you need to put your ARG into an ENV variable, and not to use the exec form. If your use the exec form of ENTRYPOINT does not invoke a command shell. This means that normal shell processing does not happen. If you want shell processing then either use the shell form or execute a shell directly, for example:
ENTRYPOINT cat ${APP_NAME_RUN}
So, try this working example:
FROM ubuntu
ARG APP_NAME=client
RUN mkdir -p /client/
ADD ./$APP_NAME.txt /client/$APP_NAME.txt
ENV APP_NAME_RUN="/client/${APP_NAME}.txt"
RUN echo $APP_NAME_RUN
ENTRYPOINT cat ${APP_NAME_RUN}

Resources