Docker - pass env variable to replace Java max memory - docker

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.

Related

How use ENV variables in Dockerfile ENTRYPOINT command

I'm brand new user of Docker...
I'm tring use Enviroments variables on my Dockerfile...
It's like that:
FROM openjdk:11-jdk-slim-buster
ENV JAVA_APP my-app
EXPOSE 8080
COPY target/$JAVA_APP-*.jar /app/$JAVA_APP.jar
CMD java -jar /app/$JAVA_APP.jar
The result is that: the COPY command gets the value of JAVA_APP variable. But the CMD command doesn't.
Is there some another way to use ENV variables?
If I make this super simple Dockerfile
FROM ubuntu
ENV JAVA_APP my-app
CMD echo $JAVA_APP
and build and run it with
docker build -t test .
docker run --rm test
docker run --rm -e JAVA_APP=Hello test
It prints 'my-app' and 'Hello'. So it does work. If it still doesn't work for you, can you expand your post with the command you use to run the container?

Date is not being read in Dockerfile ENTRYPOINT

The problem for first time is works fine but, for next it fails to write the file with same name, So I tried adding pod_id to make it unique but seems pod id to remains same when it restarts after failing, so tried adding timestamp to the filename, but date function is being treated as string, so again the file name remains same for every restarts and heap dump file doesn't get generated next time.
ENTRYPOINT ["java","-XX:+HeapDumpOnOutOfMemoryError","-XX:HeapDumpPath=mountpath-heapdump/$MY_POD_ID$(date +'%Y-%m-%d_%H-%M-%S')_heapdump.bin","-jar", "/app.jar"])
I also without double quotes:
ENTRYPOINT java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=mountpath-heapdump/$MY_POD_ID$(date +'%Y-%m-%d_%H-%M-%S')_heapdump.bin -jar app.jar
working fine in POC but for my project application, environment variables are not reflecting, spring-profile was not picked. Even other environments variable too was not picked. So I am trying the first approach only I need help to append timestamp in the filename.
Even though the exec form is recommended, in this case, since the format is complicated, a separate shell script was created and executed.
The test environment does not include the java environment, and is configured to pass arguments to exec form when running the container.
[Dockerfile]
FROM ubuntu
COPY ./entrypoint.sh /
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
[entrypoint.sh]
#!/bin/bash
echo java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=mountpath-heapdump/$POD_ID$(date +$DATE_FORMAT)_heapdump.bin -jar /app.jar
[docker build & run]
docker build -t test .
docker run --rm -e POD_ID='POD_ID' -e DATE_FORMAT='%Y-%m-%d_%H-%M-%S' test
output is...
root#DESKTOP-6Q87NVH:/tmp/test# docker run --rm -e POD_ID='POD_ID' -e DATE_FORMAT='%Y-%m-%d_%H-%M-%S' test
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=mountpath-heapdump/POD_ID2021-11-03_15-02-22_heapdump.bin -jar /app.jar
Answer provided by #rzlvmp worked for me.
You may try to run java inside shell ENTRYPOINT ["bash", "-c", "java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=mountpath-heapdump/$MY_POD_ID$(date +'%Y-%m-%d_%H-%M-%S')_heapdump.bin -jar /app.jar"]

Dockerfile Entrypoint no such file or directory: OCI not found

I have a Dockerfile with Entrypoint where I specify the config file variable and the executable file but it looks like Docker or Entrypoint doesn't recognize it. My main.py has to be executed with the config file.
ENTRYPOINT ["CONFIG_FILE=path/to/config.file ./main.py"]
always reproduce no such file or directory: OCI not found
Note: I have copied all the files in the current work directory already. main.py is an executable file. So I guess the problem is the config variable appended before the executable file. Does anyone know what is going on there? Also changing from ENTRYPOINT to CMD does not help as well.
Dockerfile
FROM registry.fedoraproject.org/fedora:34
WORKDIR /home
COPY . /home
ENTRYPOINT ["CONFIG_FILE=path/to/config.file ./main.py"]
If you just need to set an environment variable to a static string, use the Dockerfile ENV directive.
ENV CONFIG_FILE=/path/to/config.file
CMD ["./main.py"]
The Dockerfile ENTRYPOINT and CMD directives (and also RUN) have two forms. You've used the JSON-array form; in that form, there is not a shell involved and you have to manually split out words. (You are trying to run a command CONFIG_FILE=... ./main.py, where the executable file itself needs to include the = and space.) If you don't use the JSON-array form you can use the shell form instead, and this form should work:
CMD CONFIG_FILE=/path/to/config.file ./main.py
In general you should prefer CMD to ENTRYPOINT. There's a fairly standard pattern of using ENTRYPOINT to do first-time setup and then execute the CMD. For example, if you expected the configuration file to be bind-mounted in, but want to set the variable only if it exists, you could write a shell script:
#!/bin/sh
# entrypoint.sh
#
# If the config file exists, set it as an environment variable.
CONFIG_FILE=/path/to/config.file
if [ -f "$CONFIG_FILE" ]; then
export CONFIG_FILE
else
unset CONFIG_FILE
fi
# Run the main container CMD.
exec "$#"
Then you can specify both the ENTRYPOINT (which sets up the environment variables) and the CMD (which says what to actually do)
# ENTRYPOINT must be JSON-array form for this to work
ENTRYPOINT ["./entrypoint.sh"]
# Any valid CMD syntax is fine
CMD ["./main.py"]
You can double-check the environment variable setting by providing an alternate docker run command
# (Make sure to quote things so the host shell doesn't expand them first)
docker run --rm my-image sh -c 'echo $CONFIG_FILE'
docker run --rm -v "$PWD:/path/to" my-image sh -c 'echo $CONFIG_FILE'
If having the same environment in one-off debugging shells launched by docker exec is important to you, of these approaches, only Dockerfile ENV will make the variable visible there. In the other cases the environment variable is only visible in the main container process and its children, but the docker exec process isn't a child of the main process.

Pass ARGs in docker ENTRYPOINT

I'm running my app on Azure App Services, so I don't have access to docker run command. Now, I want to pass some variables into ENTRYPOINT for which I'm trying to use ARGs during build time itself. Here is how it looks,
docker build -t $IMAGE_NAME --build-arg env=dev --build-arg amplify_key=xxxxxxxxxxxxxx .
In my Dockerfile,
ARG env
ARG amplify_key
ENTRYPOINT ["/bin/bash", "-c", "init.sh $env $amplify_key"]
But this doesn't seem to be working. Please let me know the issue.
$variable references can be either expanded in the Dockerfile or by the shell when a command gets run. Only some Dockerfile commands perform variable expansions; for RUN, CMD, and ENTRYPOINT, it is only done by a shell. When a shell does the expansion it's not aware of Docker-specific ARGs, only environment variables, so you need to copy the argument to an ENV. There's an example of this in the Dockerfile documentation.
ARG env
ARG amplify_key
ENV env=$env amplify_key=$amplify_key
CMD init.sh $env $amplify_key
Assuming init.sh is your own script, though, once you have those values in environment variables, you can just access them directly, without passing them through positional parameters.
#!/bin/sh
echo "Running in $env environment"
curl -H "Authorization: Bearer $amplify_key" ...
ARG env
ARG amplify_key
ENV env=$env amplify_key=$amplify_key
CMD ["init.sh"]
This lets you do things like override the variable values at startup time more easily, and if you need to manually supply parameters to the command, the set of mandatory options is much smaller. If you're using the pattern of ENTRYPOINT doing some first-time setup and then running exec "$#" to run the CMD, this also works much better (ENTRYPOINT and sh -c have some tricky interactions).

Docker run command in dockerfile executes only if dont specifiy a command on cli

Say I have a Dockerfile:
.
.
RUN echo 'source /root/script.sh' >> /etc/bash.bashrc
(The script adds some env variables)
If I:
1) Do this:
docker run -it -v /home/user/script.sh:/root/script.sh image
It takes me to shell where if I call "env" I see the variable set by the script
But if I:
2) Do this:
docker run -it -v /home/user/script.sh:/root/script.sh image env
It prints out env and exits and my variable is missing
What am I missing? I need the variable to exists even if I specify a command/script like "env" at the end of the docker run command
When you run a command like
docker run ... image command
Docker directly runs the command you give; it doesn’t launch any kind of shell, and there’s no opportunity for a .bashrc or similar file to be read.
I’d suggest two things here:
If your program does need environment variables set in some form, set them directly using Dockerfile ENV directives. Don’t try to edit .bashrc or /etc/profile or any other shell dotfile; they won’t reliably get run.
As much as you can install things in places so that you don’t need to change environment variables. For instance, Python supports a “virtual environment” concept that allows an isolated library environment, which requires changing $PATH and similar things; but Docker provides the same isolation on its own, so just install things into the “global” package space.
If you really can’t manage either of these things, then you can write an entrypoint script that sets environment variables and then launches the container’s command. This might look like
#!/bin/sh
. /root/script.sh
exec "$#"
And then you could include this in your Dockerfile like
...
COPY entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/app/myapp"]
(If you need to use docker exec to get a debugging shell in the container, that won’t be a child process of the entrypoint and won’t get its environment variables.)

Resources