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).
Related
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.
I've got the following line in Dockerfile:
ARG COOL_ID
...
ENTRYPOINT ["java", "-jar", "/usr/share/java/${COOL_ID}/app.jar"]
but when I run it there's an error:
Error: Unable to access jarfile /usr/share/java//app.jar
and I can see that my ${COOL_ID} argument was not formatted correctly.
How can I fix it?
It will not substitute the variable, as Docker treat ENTRYPOINT and CMD as a command so processing variables like shell will not work. Try to change the CMD to run it as a shell and then you will able to process variable the same as the shell.
Also, you can not use ARG in CMD to treat them as an environment variable, you just use them inside Dockerfile, to use them as an environment variable you must assign them to some ENV.
ARG COOL_ID
ENV COOL_ID=$COOL_ID
I will also suggest to verify and check COOL_ID in Docker build time, if not set then it should print the warning or error to the user, see below example if ARG not passed to builds params then it will print an error message to the user.
ARG COOL_ID
#see ARG is for build time
RUN if [ -z $COOL_ID ];then \
>&2 echo "\n****************Warning!!!!*************\n"; \
>&2 echo "COOL_ID seems empty!" ;\
fi
ENV COOL_ID=$COOL_ID
# ENV is for run time
CMD ["sh", "-c", "java -jar /usr/share/java/${COOL_ID}/app.jar"]
Now build the docker with --build-arg
docker build --build-arg COOL_ID=myid -t myjava .
If you missed passing COOL_ID you will be notified.
I've a dockerfile where I use a custom entrypoint.sh. In this file I want to use the ARGS which I pass from docker-compose to the dockerfile.
The problem is that I don't get the content of the variable to the dockerfile I just get the variable name.
For example:
ARGS ENVIROMENT=production
ENTRYPOINT ["/var/www/html/entrypoint.sh"]
CMD ["${ENVIROMENT}"]
entrypoint.sh
#!/bin/sh
cd /var/www/html
composer update
echo $1;
The echo $1 show "${Enviroment}" instead of "production" what I expect.
Ouch ! You've hit a sensible point of Docker with this question.
But first, let me clarify some point here :
First of all, you have a typo in your example. It's ARG not ARGS
ARG allows you to define a build-time variable. Meaning that this variable will only be usefull when doing a docker image build command. You'll then be able to override it with --build-arg. For example :
docker image build --build-arg ENVIROMENT=integration ...`
At the opposite, ENV allows you to define an environment variable which can be used during runtime.
You can find all the info you need in the official documentation for env and arg
Now, back to the point...
To make it simple:
Do not use both ENTRYPOINT and CMD when you want to pass some environment variable to your entrypoint from your cmd. It's just a pain. Really.
When you want to use a environment variable inside CMD, then you'll have to either use bash format, or to prefix the command with sh -c for exec format :
CMD ["sh", "-c", "echo ${GREETINGS}"]
#or
CMD echo ${GREETINGS}
Here is a Dockerfile that works with both syntax (just uncomment the CMD you want to use) :
FROM debian:8
ENV GREETINGS="hello world"
#CMD ["sh", "-c", "echo ${GREETINGS}"]
#CMD echo ${GREETINGS}
You can find more detailled info on those issues :
Issue 5509
Issue 34772
I would like to complete Marc abouchacra's answer.
What is still missing is how to use the ARG command.
A possible solution could be:
ARG ENVIRONMENT=production
ENV ENVIRONMENT=$ENVIRONMENT
CMD exec /var/www/html/entrypoint.sh $ENVIRONMENT
The exec is there to make sure your entrypoint.sh is the process with the PID=1.
Say I have this in a Dockerfile:
ARG FOO=1
ENTRYPOINT ["docker.r2g", "run"]
where I build the above with:
docker build -t "$tag" --build-arg FOO="$(date +%s)" .
is there a way to do something like:
ENTRYPOINT ["docker.r2g", "run", ARG FOO] // something like this
I guess the argument could also be passed with docker run instead of during the docker build phase?
You could combine ARG and ENV in your Dockerfile, as I mention in "ARG or ENV, which one to use in this case?"
ARG FOO
ENV FOO=${FOO}
That way, you docker.r2g can access the ${FOO} environment variable.
I guess the argument could also be passed with docker run instead of during the docker build phase?
That is also possible, if it makes more sense to give FOO a value at runtime:
docker run -e FOO=$(...) ...
This simple technique works for me:
FROM node:9
# ...
ENTRYPOINT dkr2g run "$dkr2g_run_args"
then we launch the container with:
docker run \
-e dkr2g_run_args="$run_args" \
--name "$container_name" "$tag_name"
there might be some edge case issues with spreading an env variable into command line arguments, but should work for the most part.
ENTRYPOINT can work either like so:
ENTRYPOINT ["foo", "--bar", "$baz"] # $baz will not be interpreted
or like so:
ENTRYPOINT foo --bar $baz
not sure why the latter is not preferred - but env variable interpolation/interpretation is only possible using the latter. See: How do I use Docker environment variable in ENTRYPOINT array?
However, a more robust way of passing arguments is to use $# instead of an env variable. So what you should do then is override --entrypoint using the docker run command, like so:
docker run --entrypoint="foo" <tag> --bar $#
To learn the correct syntax of how to properly override entrypoint, you have to look that up, to be sure, but in general it's weird - you have to put --entrypoint="foo" before the tag name, and the arguments to --entrypoint, after the tag name. weird.
In my case I needed this to be set on build time, meaning I didn't have the control over the docker run command so I really struggled with it because it didn't work to use it as ARG or ENV directives in the Dockerfile. So below was is my solution and it worked like a charm:
ENTRYPOINT export $(grep -v '^#' .env | xargs -d '\n') \
&& your_command_passing_the_variable ${FOO}
Basically what I did was copy the variables into a file and then export the values in the same bash instance created by the ENTRYPOINT directive. The value is captured and passed correctly to the command. Hopefully, this helps.
Note: If you need to put secrets in that file, do not add the file to the version control system (e.g. git), instead create the file during your pipeline and be sure to clean up any sensitive information.
I am working on a task that involves building a docker image with centOs as its base using a Dockerfile . One of the steps inside the dockerfile needs http_proxy and https_proxy ENV variables to be set in order to work behind the proxy.
As this Dockerfile will be used by multiple teams having different proxies, I want to avoid having to edit the Dockerfile for each team. Instead I am looking for a solution which allows me to pass ENV variables at build time, e.g.,
sudo docker build -e http_proxy=somevalue .
I'm not sure if there is already an option that provides this. Am I missing something?
Containers can be built using build arguments (in Docker 1.9+) which work like environment variables.
Here is the method:
FROM php:7.0-fpm
ARG APP_ENV=local
ENV APP_ENV=${APP_ENV}
RUN cd /usr/local/etc/php && ln -sf php.ini-${APP_ENV} php.ini
and then build a production container:
docker build --build-arg APP_ENV=prod .
For your particular problem:
FROM debian
ENV http_proxy=${http_proxy}
and then run:
docker build --build-arg http_proxy=10.11.24.31 .
Note that if you build your containers with docker-compose, you can specify these build-args in the docker-compose.yml file, but not on the command-line. However, you can use variable substitution in the docker-compose.yml file, which uses environment variables.
So I had to hunt this down by trial and error as many people explain that you can pass ARG -> ENV but it doesn't always work as it highly matters whether the ARG is defined before or after the FROM tag.
The below example should explain this clearly. My main problem originally was that all of my ARGS were defined prior to FROM which resulted all the ENV to be undefined always.
# ARGS PRIOR TO FROM TAG ARE AVAIL ONLY TO FROM for dynamic a FROM tag
ARG NODE_VERSION
FROM node:${NODE_VERSION}-alpine
# ARGS POST FROM can bond/link args to env to make the containers environment dynamic
ARG NPM_AUTH_TOKEN
ARG EMAIL
ARG NPM_REPO
ENV NPM_AUTH_TOKEN=${NPM_AUTH_TOKEN}
ENV EMAIL=${EMAIL}
ENV NPM_REPO=${NPM_REPO}
# for good measure, what do we really have
RUN echo NPM_AUTH_TOKEN: $NPM_AUTH_TOKEN && \
echo EMAIL: $EMAIL && \
echo NPM_REPO: $NPM_REPO && \
echo $HI_5
# remember to change HI_5 every build to break `docker build`'s cache if you want to debug the stdout
..... # rest of whatever you want RUN, CMD, ENTRYPOINT etc..
I faced the same situation.
According to Sin30's answer pretty solution is using shell,
CMD ["sh", "-c", "cd /usr/local/etc/php && ln -sf php.ini-$APP_ENV php.ini"]