dockerfile: get environment variable from host - docker

I'm familiar with ARG, which allows for arguments to be passed into a dockerfile, like so:
Dockerfile
FROM ubuntu:latest
ARG foo
RUN echo $foo
$ docker build --build-arg foo=foo .
Sending build context to Docker daemon 2.048 kB
Step 1/3 : FROM ubuntu:latest
---> 00fd29ccc6f1
Step 2/3 : ARG foo
---> Running in 8f6ddda3254d
---> 9c658744762b
Removing intermediate container 8f6ddda3254d
Step 3/3 : RUN echo $foo
---> Running in 37bcbf3c5052
foo
---> 0e162e793204
Removing intermediate container 37bcbf3c5052
Successfully built 0e162e793204
However, what I want is to forward an environment variable from the host into the Dockerfile, without the need for the user to specify the --build-arg. So, for example, I want them to be able to execute this:
$ export foo=foo
$ docker build .
And get the same result.
Is this possible?

The easiest way to do this is to use docker-compose to build, with a docker-compose file like the following:
my-awesome-service:
build:
context: .
dockerfile: Dockerfile
args:
- FOO=${FOO}
Then your user can run docker-compose build and the FOO variable will be forwarded into the Dockerfile. See: https://docs.docker.com/compose/compose-file/#args

Related

Detecting username in Dockerfile

I need to run a cmd that will create my home folder within a docker container. So, if my username in my linux box is josecz then I could use it from within a Dockerfile to run a cmd like:
RUN mkdir /home/${GetMyUsername}
and get the folder /home/josecz after the Dockerfile is processed.
The only way just as commented by folks: use ARG, next gives you a workable minimal example:
Dockerfile:
FROM alpine:3.14.0
ARG GetMyUsername
RUN echo ${GetMyUsername}
RUN mkdir -p /home/${GetMyUsername}
Execution:
cake#cake:~/3$ docker build --build-arg GetMyUsername=`whoami` -t abc:1 . --no-cache
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM alpine:3.14.0
---> d4ff818577bc
Step 2/4 : ARG GetMyUsername
---> Running in 4d87a0970dbd
Removing intermediate container 4d87a0970dbd
---> 8b67912b3788
Step 3/4 : RUN echo ${GetMyUsername}
---> Running in 2d68a7e93715
cake
Removing intermediate container 2d68a7e93715
---> 100428a1c526
Step 4/4 : RUN mkdir -p /home/${GetMyUsername}
---> Running in 938d10336daa
Removing intermediate container 938d10336daa
---> 939729b76f09
Successfully built 939729b76f09
Successfully tagged abc:1
Explaination:
When docker build, you could use whoami to get the username who run the docker build, then pass to args GetMyUsername. Then, in Dockerfile, you could use ${GetMyUsername} to get the value.

DOCKER ASSIGN a shell value to ENV in DOCKERFILE

I'm drastically looking for the solution of passing the value to the ENV variable in DOCKERFILE.
EX:
FROM alphine
COPY domainname.txt .
ENV NAME=$(cat /domainname.txt)
If you insist to get NAME value when docker build, you could use next:
Dockerfile:
FROM alpine
ARG NAME
ENV NAME=${NAME}
domainname.txt:
hello
Execution:
$ docker build --build-arg NAME=$(cat ./domainname.txt) -t abc:1 . --no-cache
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM alpine
---> d4ff818577bc
Step 2/3 : ARG NAME
---> Running in f1c58e816499
Removing intermediate container f1c58e816499
---> dc4d5894440b
Step 3/3 : ENV NAME=${NAME}
---> Running in 9b8f98a3857c
Removing intermediate container 9b8f98a3857c
---> 2b0c68ddc994
Successfully built 2b0c68ddc994
Successfully tagged abc:1
$ docker run --rm -it abc:1 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=2155d7368c3e
TERM=xterm
NAME=hello
HOME=/root
You could see above NAME be assigned as hello.
Simple explanation:
NAME=$(cat ./domainname.txt) assign the hello to NAME
--build-arg pass the hello to docker build's ARG NAME
ENV NAME=${NAME} get the hello from ARG NAME

Passing environment variable to docker image at build time with docker-compose

Everything I tried following Dockerfile and docker compose references to pass an environment variable to the Docker image did not work.
I want to make this env var available during docker build when using docker-compose.
On the Docker host I have:
export BUILD_VERSION=1.0
app.js
console.log('BUILD_VERSION: ' + process.env.BUILD_VERSION);
Dockerfile:
FROM node
ADD app.js /
ARG BUILD_VERSION
ENV BUILD_VERSION=$BUILD_VERSION
RUN echo Build Time: $BUILD_VERSION
RUN node /app.js
CMD echo Run Time: $BUILD_VERSION
docker-compose.yml:
version: '3'
services:
app:
build:
context: .
args:
- BUILD_VERSION
If I build the image directly, the env var is passed fine:
docker build -t test --no-cache --build-arg BUILD_VERSION .
and is also available at run-time:
$ docker run --rm test
Run Time: 1.0
$ docker run --rm test node /app
BUILD_VERSION: 1.0
but not with docker compose.
docker-compose up --build
...
Step 5/7 : RUN echo Build Time: $BUILD_VERSION
---> Running in 6115161f33bf
Build Time:
---> c691c619018a
Removing intermediate container 6115161f33bf
Step 6/7 : RUN node /app.js
---> Running in f51831cc5e1e
BUILD_VERSION:
It's only available at run-time:
$ docker run --rm test
Run Time: 1.0
$ docker run --rm test node /app
BUILD_VERSION: 1.0
I also tried using environment in docker-compose.yml like below which again only makes it available at run-time but not build-time:
version: '3'
services:
app:
build:
context: .
environment:
- BUILD_VERSION
Please advise, how can I make it work in the least convoluted way?
Your example is working for me.
Have you tried deleting the images and building again? Docker won't re-build your image despite environment variables changed if the image is in cache.
You can delete them with:
docker-compose down --rmi all
Edit, I show here how it is working for me at build time:
$ cat Dockerfile
FROM alpine
ARG BUILD_VERSION
ENV BUILD_VERSION=$BUILD_VERSION
RUN echo Build Time: $BUILD_VERSION
$ cat docker-compose.yml
version: '3'
services:
app:
build:
context: .
args:
- BUILD_VERSION
Build:
$ export BUILD_VERSION=122221
$ docker-compose up --build
Creating network "a_default" with the default driver
Building app
Step 1/4 : FROM alpine
latest: Pulling from library/alpine
8e3ba11ec2a2: Pull complete
Digest: sha256:7043076348bf5040220df6ad703798fd8593a0918d06d3ce30c6c93be117e430
Status: Downloaded newer image for alpine:latest
---> 11cd0b38bc3c
Step 2/4 : ARG BUILD_VERSION
---> Running in b0a1a79967a0
Removing intermediate container b0a1a79967a0
---> 9fa331d63f6d
Step 3/4 : ENV BUILD_VERSION=$BUILD_VERSION
---> Running in a602c27689a5
Removing intermediate container a602c27689a5
---> bf2181423c93
Step 4/4 : RUN echo Build Time: $BUILD_VERSION <<<<<< (*)
---> Running in 9d828cefcfab
Build Time: 122221
Removing intermediate container 9d828cefcfab
---> 2b3afa3d348c
Successfully built 2b3afa3d348c
Successfully tagged a_app:latest
Creating a_app_1 ... done
Attaching to a_app_1
a_app_1 exited with code 0
As the other answer mentioned, you can use docker-compose build --no-cache, and you can avoid mentioning "app" if you have multiple services, so docker-compose will build all the services. What you can do to handle different build versions in the same docker-compose build is to use different env vars, like:
$ cat docker-compose
version: '3'
services:
app1:
build:
context: .
args:
- BUILD_VERSION=$APP1_BUILD_VERSION
app2:
build:
context: .
args:
- BUILD_VERSION=$APP2_BUILD_VERSION
Export:
$ export APP1_BUILD_VERSION=1.1.1
$ export APP2_BUILD_VERSION=2.2.2
Build:
$ docker-compose build
Building app1
Step 1/4 : FROM alpine
latest: Pulling from library/alpine
8e3ba11ec2a2: Pull complete
Digest: sha256:7043076348bf5040220df6ad703798fd8593a0918d06d3ce30c6c93be117e430
Status: Downloaded newer image for alpine:latest
---> 11cd0b38bc3c
Step 2/4 : ARG BUILD_VERSION
---> Running in 0b66093bc2ef
Removing intermediate container 0b66093bc2ef
---> 906130ee5da8
Step 3/4 : ENV BUILD_VERSION=$BUILD_VERSION
---> Running in 9d89b48c875d
Removing intermediate container 9d89b48c875d
---> ca2480695149
Step 4/4 : RUN echo Build Time: $BUILD_VERSION
---> Running in 52dec27874ec
Build Time: 1.1.1
Removing intermediate container 52dec27874ec
---> 1b3654924297
Successfully built 1b3654924297
Successfully tagged a_app1:latest
Building app2
Step 1/4 : FROM alpine
---> 11cd0b38bc3c
Step 2/4 : ARG BUILD_VERSION
---> Using cache
---> 906130ee5da8
Step 3/4 : ENV BUILD_VERSION=$BUILD_VERSION
---> Running in d29442339459
Removing intermediate container d29442339459
---> 8b26def5ef3a
Step 4/4 : RUN echo Build Time: $BUILD_VERSION
---> Running in 4b3de2d223e5
Build Time: 2.2.2
Removing intermediate container 4b3de2d223e5
---> 89033b10b61e
Successfully built 89033b10b61e
Successfully tagged a_app2:latest
You need to set argument in docker-compose.yml as shown which will then be overriden to passed env variable -
version: '3'
services:
app:
build:
context: .
args:
- BUILD_VERSION
Next export environment variable you need to pass.
$ export BUILD_VERSION=1.0
Now build the image using command
$ docker-compose build --no-cache --build-arg BUILD_VERSION=$BUILD_VERSION app
You can pass in args to build, from the docker-compose file to the docker build. It is surprising the env vars aren't used for run and build.
// docker-compose.yml
version: '3'
services:
app:
build:
context: .
environment:
- BUILD_VERSION
args:
- BUILD_VERSION=${BUILD_VERSION}
volumes:
...
// Dockerfile
FROM node
ADD app.js /
ARG BUILD_VERSION
ENV BUILD_VERSION=$BUILD_VERSION
RUN echo Build Time: $BUILD_VERSION
RUN node /app.js
CMD echo Run Time: $BUILD_VERSION

Is there a better way than repeating --build-arg on the command line when building a Dockerfile?

My single docker build command line is getting quite long. I wonder if there's a better way than settings all the ARG's in the Dockerfile and then --build-arg through each of them on the docker build command.
Unfortunately, there are no built-in possibility to specify file with build args to use docker build yet.
But anyway we can use simple bash wrapper for that:
1. We create file with build args. Something like args_list:
month=March
day=26
year=2018
2. An example of the Dockerfile is:
FROM alpine:3.7
ARG month
ARG day
ARG year
RUN echo "Today is the $day/$month/$year."
3. And build our above docker image with the following one line script:
command="docker build ."; for i in $(cat args_list); do command+=" --build-arg $i"; done; $command
The output shows that everything is successfully injected:
Sending build context to Docker daemon 3.072kB
Step 1/5 : FROM alpine:3.7
---> 3fd9065eaf02
Step 2/5 : ARG month
---> Running in 5c6e695c3fab
Removing intermediate container 5c6e695c3fab
---> 6193d0d5665f
Step 3/5 : ARG day
---> Running in da0bc3e65890
Removing intermediate container da0bc3e65890
---> 41a90a4dd893
Step 4/5 : ARG year
---> Running in 83f6ae68390d
Removing intermediate container 83f6ae68390d
---> 1fd4beef3c53
Step 5/5 : RUN echo "Today is the $day/$month/$year."
---> Running in b5eaa6a5a80c
Today is the 26/March/2018.
Removing intermediate container b5eaa6a5a80c
---> de7581671706
Successfully built de7581671706
This way we can simply add new build args into the "build args" file and our CI system will build proper images without changes on its side.

How to pass ARG value to ENTRYPOINT?

Docker 1.9 allows to pass arguments to a dockerfile. See link: https://docs.docker.com/engine/reference/builder/#arg
How can i pass the same arugments within ENTRYPOINT Instruction??
My dockerfile has
ARG $Version=3.1
ENTRYPOINT /tmp/folder-$Version/sample.sh start
I am getting an error while creating container with above dockerfile.
Please suggest what is the correct way to specify the argument within ENTRYPOINT instruction??
Like Blake Mitchell sais, you cannot use ARG in ENTRYPOINT. However you can use your ARG as a value for ENV, that way you can use it with ENTRYPOINT:
Dockerfile
ARG my_arg
ENV my_env_var=$my_arg
ENTRYPOINT echo $my_env_var
and run:
docker build --build-arg "my_arg=foo" ...
Short answer: you need to use ENV
Both ARG and ENV are not expanded in ENTRYPOINT or CMD. (https://docs.docker.com/engine/reference/builder/#environment-replacement) However, because ENVs are passed in as part of the environment, they are available at run time, so the shell can expand them. (This means you can't use the array form of ENTRYPOINT or CMD.)
Here is an example:
$ cat arg/Dockerfile
FROM debian:jessie
ARG FOO=bar
ENTRYPOINT echo ${FOO:-foo}
$ sudo docker build arg
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM debian:jessie
---> f50f9524513f
Step 2 : ARG FOO=bar
---> Using cache
---> 2cfdcb514b62
Step 3 : ENTRYPOINT echo ${FOO:-foo}
---> Running in 21fb9b42c10d
---> 75e5018bad83
Removing intermediate container 21fb9b42c10d
Successfully built 75e5018bad83
$ sudo docker run 75e5018bad83
foo
$ sudo docker run -e FOO=bas 75e5018bad83
bas
$ sudo docker build --build-arg FOO=bas arg
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM debian:jessie
---> f50f9524513f
Step 2 : ARG FOO=bar
---> Using cache
---> 2cfdcb514b62
Step 3 : ENTRYPOINT echo ${FOO:-foo}
---> Using cache
---> 75e5018bad83
Successfully built 75e5018bad83
$ sudo docker run 75e5018bad83
foo
$ cat env/Dockerfile
FROM debian:jessie
ARG FOO=bar
ENV FOO=${FOO}
ENTRYPOINT echo ${FOO:-foo}
$ sudo docker build env
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM debian:jessie
---> f50f9524513f
Step 2 : ARG FOO=bar
---> Using cache
---> 2cfdcb514b62
Step 3 : ENV FOO ${FOO}
---> Running in f157a07c1b3e
---> a5e8c5b65a17
Removing intermediate container f157a07c1b3e
Step 4 : ENTRYPOINT echo ${FOO:-foo}
---> Running in 66e9800ef403
---> 249fe326e9ce
Removing intermediate container 66e9800ef403
Successfully built 249fe326e9ce
$ sudo docker run 249fe326e9ce
bar
$ sudo docker run -e FOO=bas 249fe326e9ce
bas
$ sudo docker build --build-arg FOO=bas env
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM debian:jessie
---> f50f9524513f
Step 2 : ARG FOO=bar
---> Using cache
---> 2cfdcb514b62
Step 3 : ENV FOO ${FOO}
---> Running in 6baf31684b9f
---> 8f77ad154798
Removing intermediate container 6baf31684b9f
Step 4 : ENTRYPOINT echo ${FOO:-foo}
---> Running in 892ac47cabed
---> fa97da85bf8a
Removing intermediate container 892ac47cabed
Successfully built fa97da85bf8a
$ sudo docker run fa97da85bf8a
bas
$
Just in case you have more than one parameter to pass to ENTRYPOINT or CMD, you can do like that:
FROM openjdk:8-jdk-alpine
ARG PROFILE
ENV PROFILE=${PROFILE}
...
CMD java -jar -Dspring.profiles.active=$(echo ${PROFILE}) /app/server.jar
I'm struggling with this for a day, thanks for #Rotareti for mentioning. It needs to be in ENV before it can be used to the ENTRYPOINT.
ENV variable=$from_ARG_variable
ENTRYPOINT exec your_exec_sh_file $variable
Hope this helps.
The syntax for ARG should omit the $.
Instead of ARG $Version=3.1, try ARG Version=3.1.

Resources