I want to pass an argument to my dockerfile such that I should be able to use that argument in my run command but it seems I am not able to do so
I am using a simple bash file that will trigger the docker build and docker run
FROM openjdk:8 AS SCRATCH
WORKDIR /
ADD . .
RUN apt install unzip
RUN unzip target/universal/rule_engine-1.0.zip -d target/universal/rule_engine-1.0
COPY target/universal/rule_engine-1.0 .
ENV MONGO_DB_HOST="host.docker.internal"
ENV MONGO_DB_PORT="27017"
EXPOSE 9000
ARG path
CMD target/universal/rule_engine-1.0/bin/rule_engine -Dconfig.file=$path
above is my dockerfile
and below is my bash file which will access this dockerfile
#!/bin/bash
# change the current path to rule engine path
cd /Users/zomato/Documents/Intern_Project/rule_engine
sbt dist
ENVIR=$1
config=""
if [ $ENVIR == "local" ]
then
config=conf/application.conf
elif [ $ENVIR == "staging" ]
then
config=conf/staging.conf
else
config=conf/production.conf
fi
echo $config
docker build --build-arg path=$config -t rule_engine_zip .
docker run -i -t -p 9000:9000 rule_engine_zip
but when i access the dockerfile through bash script which will set config variable I am not able to set path variable in last line of dockerfile to the value of config.
ARG values won't be available after the image is built, so
a running container won’t have access to those values. To dynamically set an env variable, you can combine both ARG and ENV (since ENV can't be overridden):
ARG PATH
ENV P=${PATH}
CMD target/universal/rule_engine-1.0/bin/rule_engine -Dconfig.file=$P
For further explanation, I recommend this article, which explains the difference between ARG and ENV in a clear way:
As you can see from the above image, the ARG values are available only during the image build.
I've a Makefile with:
docker build --build-arg USER=${USER} --build-arg PASSWD=${PASSWD}
and I build it in a jenkins script with:
withCredentials([usernamePassword(credentialsId: 'my-credentials', passwordVariable: 'PASSWD', usernameVariable: 'USER')]) { (build_step) }
In a Dockerfile script, I have:
ARG USER
ARG PASSWD
curl -u ${USER}:${PASSWD}
Where USER/PASSWD pair is passed to repo/API.
What are necessary steps for USER / PASSWD to be passed properly? I've tried a lot of solutions/syntax options and RUN export. None seems to be working.
Is there a way to RUN echo variables from dockerfile? Jenkins log shows either a variable name or whitespaces for most of attempts.
You can pass the arguments to dockerile using --build-arg. So in this case you could run something like docker build --build-arg USER=${USER} --build-arg PASSWD=${PASSWD}
Command Used:
sudo docker run -it -p 80:9000 c822030cc576 sh /app/test/play/docker-entrypoint.sh /app/test/play/servers/server01/play/conf/override.conf /app/log/nflott/play true dev
My Parameters:
$2:/app/test/play/servers/server01/play/conf/override.conf
$3:/app/log/nflott/play
$4:true
$5:dev Which needs to be passed to the docker-entrypoint.sh Script
My entrypoint.sh Script Code:
echo "CONFIG:$2
echo "LOG_HOME:$3"
echo "SKIP_LOGFILE:$4"
echo "LOG_ENV:$5
/app/test/play/servers/server01/play/bin/apiserver \
-Dconfig.file=/app/test/play/servers/server01/play/conf/override.conf \
-Dpidfile.path=/app/test/play/servers/server01/RUNNING_PID \
-DLOG_HOME=$3 \
-DSKIP_LOGFILE=$4 \
-DLOG_ENV=$5 \
Error:
CONFIG:/app/test/play/servers/server01/play/conf/override.conf
LOG_HOME:/app/log/nflott/play
SKIP_LOGFILE:true
LOG_ENV:dev
Bad root server path: /app/nflott/play/docker-entrypoint.sh
Seems after the echo statement my parameters are not getting passed to the logic block and its failing with Bad Root server Path issue. How to resolve the Bad root serer path error and run my script?
IMHO they might be getting passed as args of the docker command. You can instead try -e docker option to pass them as some named env variables to the docker container and then access inside your script.
You may use special --entrypoint flag and pass params in cmd:
sudo docker run -it -p 80:9000 --entrypoint /app/test/play/docker-entrypoint.sh c822030cc576 /app/test/play/servers/server01/play/conf/override.conf /app/log/nflott/play true dev
I am having trouble creating and using variables in a Dockerfile - I build a Docker image via a Dockerfile with this command:
$ docker build --build-arg s=scripts/a.sh -t a .
(So because I use --build-arg, $s will be an available argument in the Dockerfile, and this part works)
The Dockerfile is like so:
ARG s
RUN echo $s
RUN useradd -ms /bin/bash newuser
USER newuser
WORKDIR /home/newuser
ENV fn=$(filename $s) # fails on this line
COPY $s .
ENTRYPOINT ["/bin/bash", "/home/newuser/$fn"]
The problem I have is that the Docker build is failing on the line indicated above.
Error response from daemon: Syntax error - can't find = in "$s)". Must be of the form: name=value
If I change that line to this:
RUN fn=$(filename $s)
I get this error:
Error: Command failed: docker build --build-arg s=scripts/a.sh -t a .
The command '/bin/sh -c fn=$(filename $s)' returned a non-zero code: 127
Anyone know the correct way to
Create a variable inside the docker file
Use string interpolation with that variable so that I can reference the variable in the ENTRYPOINT arguments like so:
ENTRYPOINT ["/bin/bash", "/home/newuser/$var"]
Even if I do this:
ARG s
ARG sname
RUN echo $s # works as expected
RUN echo $sname # works as expected
RUN useradd -ms /bin/bash newuser
USER newuser
WORKDIR /home/newuser
COPY $s . # works as expected (I believe)
ENTRYPOINT /bin/bash /home/newuser/$sname # does not work as expected
even though I am using the "non-JSON" version of ENTRYPOINT, it still doesn't seem to pick up the value for the $sname variable.
I would avoid using variable in ENTRYPOINT at all. It's tricky and requires a deep understanding of what is going on. And is easy to break it by accident. Just consider one of the following.
Create link with the known name to your start script.
RUN ln -s /home/newuser/$sname /home/newuser/docker_entrypoint.sh
ENTRYPOINT ["/home/newuser/docker_entrypoint.sh"]
or write standalone entrypoint script that runs what you need.
But if you want to know how and why solutions in your questions work just keep reading.
First some definitions.
ENV - is environment variable available during buildtime (docker build) and runtime (docker run)
ARG - is environment variable available only during buildtime
If you look at https://docs.docker.com/engine/reference/builder/#environment-replacement you see the list of dockerfile instructions that support those environment variables directly. This is why COPY "picks up the variable" as you said.
Please note that there is no RUN nor ENTRYPOINT. How does it work?
You need to dig into the documentation. First RUN (https://docs.docker.com/engine/reference/builder/#run). There are 2 forms. The first one executes command through the shell and this shell has access to buildtime environment variables.
# this works because it is called as /bin/sh -c 'echo $sname'
# the /bin/sh replace $sname for you
RUN echo $sname
# this does NOT work. There is no shell process to do $sname replacement
# for you
RUN ["echo", "$sname"]
Same thing applies to the ENTRYPOINT and CMD except only runtime variables are available during container start.
# first you need to make some runtime variable from builtime one
ENV sname $sname
# Then you can use it during container runtime
# docker runs `/bin/sh -c '/bin/bash /home/newuser/$sname'` for you
# and this `/bin/sh` proces interprets `$sname`
ENTRYPOINT /bin/bash /home/newuser/$sname
# but this does NOT work. There is no process to interpolate `$sname`
# docker runs what you describe.
ENTRYPOINT ["/bin/bash", "/home/newuser/$sname"]
edit 2017-04-03: updated links to the docker documentations and slight rewording to avoid confusion that I sense from other answers and comments.
I requested #Villem to answer, and his answer is much more definitive, but the following will work (just is not a stable solution). His answer is basically saying that this answer is not a good way to do it:
ARG s # passed in via --build-arg s=foo
ARG sname # passed in via --build-arg sname=bar
RUN echo $s
RUN echo $sname
ENV sname $sname # this is the key part
RUN useradd -ms /bin/bash newuser
USER newuser
WORKDIR /home/newuser
COPY $s .
ENTRYPOINT /bin/bash /home/newuser/$sname # works, but is not stable!
don't ask me why the COPY command picks up the variable that was declared via ARG, but that the ENTRYPOINT command does not seem to pick up the variable declared via ARG, but only picks up the variable declared via ENV. At least, this appears to be the case.
I spent a lot of time to find out that.
Don't works !
ARG install="bundle install --jobs=4"
FROM ruby:2.6.3-alpine
RUN eval $install
But this works...
FROM ruby:2.6.3-alpine
ARG install="bundle install --jobs=4"
RUN eval $install
Then to build the docker image:
docker build -t server --no-cache --build-arg install="bundle install --without development test" .`
I wanted both variable substitution and arguments passing.
Let's say our Dockerfile has:
ENV VARIABLE=replaced
And we want to run this:
docker run <image> arg1 arg2
I obviously wanted this output:
replaced arg1 arg2
I eventually found this one:
ENTRYPOINT [ "sh", "-c", "echo $VARIABLE $0 $#" ]
It works!!!
But I feel SOOOO dirty!
Obviously, in real life, I wanted to do something more useful:
docker run <image> --app.options.additional.second=true --app.options.additional.third=false
ENTRYPOINT [ "sh", "-c", "java -Xmx$XMX $0 $#", \
"-jar", \
"/my.jar", \
"--app.options.first=true" ]
Why a so complicated answer?
"ENTRYPOINT java..." would not pass docker arguments to the entrypoint => "ENTRYPOINT [..." is mandatory for that
"ENTRYPOINT [..." will NOT call the shell, so no variable substitution is done at all => "sh -c" is mandatory for that
"sh -c" only take the FIRST argument passed to it and split it in command+arguments => so everything must be in the first argument of "sh -c" for variables to be visible by the command
Docker arguments are passed as extra array entries of "ENTRYPOINT [..." => so the "$#" is necessary to "copy" the remainings arguments of the "sh -c" into the "echo ..." command to be executed (and as a bonus, we can reuse the additional array entries of ENTRYPOINT[] to place forced arguments in a readable way in the Dockerfile)
"$#" removes the first argument => so explicit "$0" must be used before "$#"
Fiou...
I added a comment to this issue, for Docker developers to see what we are forced to do and perhaps change their mind to make ENTRYPOINT[] replace environment variables: https://github.com/moby/moby/issues/4783#issuecomment-442466609
I'm trying to obtain the value of the first argument I pass to the Docker Entrypoint. I received an answer earlier on how to do this. Here is the link:
Referencing a dynamic argument in the Docker Entrypoint
So I setup an experiment to see if this works:
Here's my Dockerfile:
FROM alpine:3.3
MAINTAINER ole.ersoy#gmail.com
RUN apk add --update --no-cache --no-progress bash
COPY run.sh .
ENTRYPOINT /run.sh
And the run.sh entrypoint:
#!/bin/sh
echo The first argument is: $1
I then build this:
docker build -t test .
And run the image:
ole#MKI:~/docker-test$ docker run test one
The first argument is:
I was expecting:
ole#MKI:~/docker-test$ docker run test one
The first argument is: one
Thoughts?
TIA,
Ole
Change ENTRYPOINT to next:
ENTRYPOINT ["bash", "run.sh"]
It works for me. Read more about entrypoint args here https://docs.docker.com/engine/reference/builder/#entrypoint