RUN Instruction in building Docker Image - docker

Below is my Docker file
ARG tag_info=latest
FROM alpine:${tag_info} AS my_apline
ARG message='Hello World'
RUN echo ${message}
RUN echo ${message} > myfile.txt
RUN echo "Hi Harry"
When i run docker image build -t myimage:v1_0 - < Dockerfile
the output is :
Sending build context to Docker daemon 2.048kB
Step 1/6 : ARG tag_info=latest
Step 2/6 : FROM alpine:${tag_info} AS my_apline
latest: Pulling from library/alpine
cbdbe7a5bc2a: Already exists
Digest: sha256:9a839e63dad54c3a6d1834e29692c8492d93f90c59c978c1ed79109ea4fb9a54
Status: Downloaded newer image for alpine:latest
---> f70734b6a266
Step 3/6 : ARG message='Hello World'
---> Running in 74bcc8897e8e
Removing intermediate container 74bcc8897e8e
---> d8d50432d375
Step 4/6 : RUN echo ${message}
---> Running in 730ed8e1c1d3
Hello World
Removing intermediate container 730ed8e1c1d3
---> 8417e3167e80
Step 5/6 : RUN echo ${message} > myfile.txt
---> Running in c66018331383
Removing intermediate container c66018331383
---> 07dc27d8ad3d
Step 6/6 : RUN echo "Hi Harry"
---> Running in fb92fb234e42
Hi Harry
Removing intermediate container fb92fb234e42
---> a3bec122a77f
It displays "Hi Harry" and "Hello World" in the middle (which I do not understand why)
Why "Hi Harry and "Hello World" is not displayed when i spin the container from image file?

Because the RUN command executes the commands when you are building the image not when you are spinning up the container using the image. It is used to alter the image like adding new packages using apt-get or changing file permissions etc
If you need something to run when the container is starting you need to use command to entrypoint instructions

From the docker official documentation:
The RUN instruction will execute any commands in a new layer on top of the current image and commit the results. The resulting committed image will be used for the next step in the Dockerfile.
You should use CMD if you want to obtain the described behavior.
The main purpose of a CMD is to provide defaults for an executing container. These defaults can include an executable, or they can omit the executable, in which case you must specify an ENTRYPOINT instruction as well.
This has three forms:
- CMD ["executable","param1","param2"] (exec form, this is the preferred form)
- CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
- CMD command param1 param2 (shell form)

Related

"WORKDIR $DOCKER_MOUNT_DIR cannot normalize nothing" error on multi stage dockerfile build

I want to incorporate a virtual env in the docker container hence I'm trying to create a Conda environment (line 117-128) with the necessary dependencies needed for my use case which is in the environment.yml file. However, when I try to build the Dockerfile I get the following issue as mentioned in the title.
https://gist.github.com/impaidk/504363033b34406367e774c72544c540
The Dockerfile can be found in the above link.
After adding the lines 117 to 128 I get this problem.
Before adding these lines, I could successfully build the docker file. Can someone please direct me towards what am I wrongly doing here?
Removing intermediate container 426298ea76b6
---> 68c8b7342cc2
Step 36/54 : RUN echo "conda activate env_vis2mesh" >> ~/.bashrc
---> Running in 53949a223622
Removing intermediate container 53949a223622
---> 7635492d073b
Step 37/54 : SHELL ["/bin/bash", "--login", "-c"]
---> Running in 992f511d7842
Removing intermediate container 992f511d7842
---> aafcba711b55
Step 38/54 : RUN echo '[ ! -z "$TERM" -a -r /etc/motd ] && ( echo "cat <<EOF" ; cat /etc/motd ; echo EOF ) | sh' >> /etc/bash.bashrc
---> Running in 0ff2bb34ea36
Removing intermediate container 0ff2bb34ea36
---> 643f0f83bbd5
Step 39/54 : COPY motd /etc/
---> d10c760f8d25
Step 40/54 : USER $DOCKER_USER
---> Running in 9cc835cbdebb
Removing intermediate container 9cc835cbdebb
---> 819fae915c5f
Step 41/54 : RUN echo "umask 002" >> $DOCKER_HOME/.bashrc
---> Running in 9a24755e10ab
Removing intermediate container 9a24755e10ab
---> 5acb97493c3d
Step 42/54 : RUN echo "source $DOCKER_HOME/.version_information.sh" >> $DOCKER_HOME/.bashrc
---> Running in affb5a5086b1
Removing intermediate container affb5a5086b1
---> 101f71c5c403
Step 43/54 : COPY .version_information.sh $DOCKER_HOME
---> bc0bdd3b2437
Step 44/54 : RUN echo "if [[ -f $DOCKER_HOME/.bashrc-appendix ]]; then source $DOCKER_HOME/.bashrc-appendix; fi" >> $DOCKER_HOME/.bashrc
---> Running in badd158a5c1f
Removing intermediate container badd158a5c1f
---> cd42c89e893c
Step 45/54 : RUN mkdir -p $DOCKER_MOUNT_DIR/src
---> Running in 92dceb96a5f8
Removing intermediate container 92dceb96a5f8
---> d702d73b89f0
Step 46/54 : RUN mkdir $DOCKER_HOME/.ssh
---> Running in 463b4c55314b
Removing intermediate container 463b4c55314b
---> 49bc41c8e310
Step 47/54 : ADD templates $DOCKER_HOME/templates
---> d62e67ea2fc5
Step 48/54 : RUN echo 'export PATH=$PATH:$DOCKER_HOME/templates/bin' >> $DOCKER_HOME/.bashrc # using single quotes ensures that $PATH is not replaced here
---> Running in aee1b4ca57d9
Removing intermediate container aee1b4ca57d9
---> e119faed304a
Step 49/54 : USER root
---> Running in acc1ca160be5
Removing intermediate container acc1ca160be5
---> 6f3130f1a9a1
Step 50/54 : WORKDIR $DOCKER_MOUNT_DIR
cannot normalize nothing
By design, the environment variables declared in Dockerfile as ENV (and also ARG) are build-stage specific. And hence, whenever you start a new Docker build-stage (by using the FROM parameter), the variables have to be declared once again as per your need.
Just in case you're wondering how the other ENV values such as DOCKER_USER and DOCKER_HOME are (seemingly) working in the newer build stages of your Dockerfile - they're simply being set to null values which won't throw any error during the build (but are surely going to give unpleasant surprises later!!).
What it happens is docker can't find environment variable $DOCKER_MOUNT_DIR so you can add a .env file in order to set this env. variable. Then, you must run docker-compose up to .env file works.
Also, you can check more information in the official docker documentation about environment variables.

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.

Build a docker image without staring it

I am trying to create a docker image and tag it at the same time. This way I can create a script that uses the -t option in the "docker build" command. Thus staff members that deploy new images does not need to type docker commands, they simply run the script.
The problem that I have is that the "docker build" command also starts the image. This causes the docker build command to get 'stuck' when it gets to the point where the image runs, because the image is suppose to run indefinitely, it is a service, thus the build command never finishes, and the result is that the tag mentioned in the "-t" part of the build command never gets applied to the new image.
So there is no way to identify new images because none of them have tags. I can fix it by terminating the build command with Ctrl+C and then afterwards using the "docker tag" command. But that means that I cannot put the build and tag commands in a bash script, because I have to tag the image ID and not the name. Which changes every time I run the docker build command.
I have tried the following:
Hitting Ctrl+C to terminate the application running inside the new image. This does end the current running application. But this terminates the docker build command as well. Thus the image tag never gets applied.
I have tried using "docker ps" in another terminal to find the currently running container and stopping it with "docker stop ID". This also stops the application / container but this generates an error on the docker build command and once again doesn't finish and doesn't apply the tag.
This is what I see after I have tried steps 1 or 2 above and run a "docker image list" command, the neither the tag field nor the repository field being set:
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> df355e74685b 6 minutes ago 493MB
openjdk latest e92ef2c3a3dd 12 days ago 470MB
openjdk 8 b84359d0cbce 3 weeks ago 488MB
portainer/portainer latest da2759008147 4 weeks ago 75.4MB
My docker build command :
sudo docker build -t slite/cloud-db-host -f slite/cloud/db/Dockerfile.Host.docker .
And here is my docker file:
FROM openjdk:8
LABEL maintainer="techss.co.za"
LABEL vendor="techss.co.za"
LABEL app="slite-db-host"
LABEL repository="slite"
COPY slite/cloud/db /slite/cloud/db
COPY slite/lib/java /slite/lib/java
EXPOSE 51173
WORKDIR .
RUN javac slite/cloud/db/*.java && javac slite/lib/java/*.java && java slite.cloud.db.SliteDBHost
ENTRYPOINT ["java","slite.cloud.db.SliteDBHost"]
Here is the output from docker build:
Sending build context to Docker daemon 13.43MB
Step 1/11 : FROM openjdk:8
---> b84359d0cbce
Step 2/11 : LABEL maintainer="techss.co.za"
---> Running in 3dc3f0fcea2c
Removing intermediate container 3dc3f0fcea2c
---> 0946737c1386
Step 3/11 : LABEL vendor="techss.co.za"
---> Running in c289dd741158
Removing intermediate container c289dd741158
---> 00d5a7f3d7e5
Step 4/11 : LABEL app="slite-db-host"
---> Running in 1d7e953bdf6f
Removing intermediate container 1d7e953bdf6f
---> 4540390e8bb5
Step 5/11 : LABEL repository="slite"
---> Running in c366a92becb5
Removing intermediate container c366a92becb5
---> c9be0ef5e6da
Step 6/11 : COPY slite/cloud/db /slite/cloud/db
---> f3efeb406aef
Step 7/11 : COPY slite/lib/java /slite/lib/java
---> 797bf7df8335
Step 8/11 : EXPOSE 51173
---> Running in 93389673e9cc
Removing intermediate container 93389673e9cc
---> abfb10413edf
Step 9/11 : WORKDIR .
---> Running in 77a67baa9be6
Removing intermediate container 77a67baa9be6
---> 7d313395f072
Step 10/11 : RUN javac slite/cloud/db/*.java && javac slite/lib/java/*.java && java slite.cloud.db.SliteDBHost
---> Running in 99edcf79d5f4
Sun Jul 07 18:47:02 UTC 2019 Listening on port 51173
It just hangs on the last line, I assume it's waiting for the application running inside the container to end, which will never happen because it's a service. So how do I force docker build to carry on, even though the container is running, thus applying the needed tags. Or force docker build to NOT start the image but simply create it, which would be first prize.
Just replace RUN with CMD and it will not be runned during the build:
CMD ["sh","-c","javac slite/cloud/db/*.java && javac slite/lib/java/*.java && java slite.cloud.db.SliteDBHost"]
Cheers
Any RUN instruction will be executed when the Docker image is built. I suspect your issue will be fixed if you change line 10 of your Dockerfile.
Before:
RUN javac slite/cloud/db/*.java && javac slite/lib/java/*.java && java slite.cloud.db.SliteDBHost
After:
RUN javac slite/cloud/db/*.java && javac slite/lib/java/*.java

How to bust the cache for the FROM line of a Dockerfile

I have a Dockerfile like this:
FROM python:2.7
RUN echo "Hello World"
When I build this the first time with docker build -f Dockerfile -t test ., or build it with the --no-cache option, I get this output:
Sending build context to Docker daemon 40.66MB
Step 1/2 : FROM python:2.7
---> 6c76e39e7cfe
Step 2/2 : RUN echo "Hello World"
---> Running in 5b5b88e5ebce
Hello World
Removing intermediate container 5b5b88e5ebce
---> a23687d914c2
Successfully built a23687d914c2
My echo command executes.
If I run it again without busting the cache, I get this:
Sending build context to Docker daemon 40.66MB
Step 1/2 : FROM python:2.7
---> 6c76e39e7cfe
Step 2/2 : RUN echo "Hello World"
---> Using cache
---> a23687d914c2
Successfully built a23687d914c2
Successfully tagged test-requirements:latest
Cache is used for Step 2/2, and Hello World is not executed. I could get it to execute again by using --no-cache. However, each time, even when I am using --no-cache it uses a cached python:2.7 base image (although, unlike when the echo command is cached, it does not say ---> Using cache).
How do I bust the cache for the FROM python:2.7 line? I know I can do FROM python:latest, but that also seems to just cache whatever the latest version is the first time you build the Dockerfile.
If I understood the context correctly, you can use --pull while using docker build to get the latest base image -
$ docker build -f Dockerfile.test -t test . --pull
So using both --no-cache & --pull will create an absolute fresh image using Dockerfile -
$ docker build -f Dockerfile.test -t test . --pull --no-cache
Issue - https://github.com/moby/moby/issues/4238
FROM pulls an image from the registry (DockerHub in this case).
After the image is pulled to produce your build, you will see it if you run docker images.
You may remove it by running docker rmi python:2.7.

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.

Resources