PURPOSE: This chapter explains how to create a Docker image.
As explained in [Docker_Basics], Docker image is the build component of Docker and a read-only template of application operating system.
Docker build images by reading instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. docker build
command uses this file and executes all the commands in succession to create an image.
build
command is also passed a context that is used during image creation. This context can be a path on your local filesystem or a URL to a Git repository.
Dockerfile is usually called Dockerfile. The complete list of commands that can be specified in this file are explained at https://docs.docker.com/reference/builder/. The common commands are listed below:
Command | Purpose | Example |
---|---|---|
FROM |
First non-comment instruction in Dockerfile |
|
COPY |
Copies mulitple source files from the context to the file system of the container at the specified path |
|
ENV |
Sets the environment variable |
|
RUN |
Executes a command |
|
CMD |
Defaults for an executing container |
|
EXPOSE |
Informs the network ports that the container will listen on |
|
Create a new directory.
Create a new text file, name it Dockerfile, and use the following contents:
FROM ubuntu
CMD ["/bin/echo", "hello world"]
This image uses ubuntu
as the base image. CMD
command defines the command that needs to run. It provides a different entry point of /bin/echo
and gives the argument “hello world”.
Build the image:
> docker build -t helloworld .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ubuntu
latest: Pulling from library/ubuntu
90d6565b970a: Pull complete
40553bdb8474: Pull complete
c3129e7479ab: Pull complete
091663bd70db: Pull complete
Digest: sha256:ba1688fec34f66d8a7ff5b42e6971625d8232c72bf0e38ad06dda10cad157293
Status: Downloaded newer image for ubuntu:latest
---> cf62323fa025
Step 2 : CMD /bin/echo hello world
---> Running in 3ea56763bbdc
---> f3573ec571c5
Removing intermediate container 3ea56763bbdc
Successfully built f3573ec571c5
.
in this command is the context for docker build
.
List the images available:
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
helloworld latest a85376e122e2 4 seconds ago 125 MB
ubuntu latest cf62323fa025 5 days ago 125 MB
Run the container:
docker run helloworld
to see the output:
hello world
If you do not see the expected output, check your Dockerfile, build the image again, and now run it!
Change the base image from ubuntu
to busybox
in Dockerfile
. Build the image again:
docker build -t helloworld:2 .
and view the images using docker images
command:
REPOSITORY TAG IMAGE ID CREATED SIZE
helloworld 2 f74665e81a75 8 seconds ago 1.093 MB
helloworld latest a85376e122e2 About a minute ago 125 MB
ubuntu latest cf62323fa025 5 days ago 125 MB
busybox latest 2b8fd9751c4c 2 weeks ago 1.093 MB
helloworld:2
is the format that allows to specify the image name and assign a tag/version to it separated by :
.
Also note how base images for Ubuntu and Busybox are downloaded as well.
Create a new Java project:
mvn archetype:generate -DgroupId=org.examples.java -DartifactId=helloworld -DinteractiveMode=false
Build the project:
cd helloworld
mvn package
Run the Java class:
java -cp target/helloworld-1.0-SNAPSHOT.jar org.examples.java.App
This shows the output:
Hello World!
Let’s package this application as a Docker image.
Pull the latest Docker image for Java:
docker pull openjdk
Run the container in an interactive manner:
docker run -it openjdk
Check the version of Java:
root@e6eb7c32b32d:/# java -version
openjdk version "1.8.0_102"
OpenJDK Runtime Environment (build 1.8.0_102-8u102-b14.1-1~bpo8+1-b14)
OpenJDK 64-Bit Server VM (build 25.102-b14, mixed mode)
A different version may be seen in your case. Exit out of the container by typing exit
in the shell.
Create a new Dockerfile in helloworld
directory and use the following content:
FROM java
COPY target/helloworld-1.0-SNAPSHOT.jar /usr/src/helloworld-1.0-SNAPSHOT.jar
CMD java -cp /usr/src/helloworld-1.0-SNAPSHOT.jar org.examples.java.App
Build the image:
docker build -t hello-java .
Run the image:
docker run hello-java
This displays the output:
Hello World!
This shows that the java
CLI and Docker image are priting the same output.
Docker Maven Plugin allows you to manage Docker images and containers using Maven. It comes with predefined goals:
Goal | Description |
---|---|
|
Build images |
|
Create and start containers |
|
Stop and destroy containers |
|
Push images to a registry |
|
Remove images from local docker host |
|
Show container logs |
Clone the sample code from https://github.com/arun-gupta/docker-java-sample/.
Create the Docker image:
mvn -f docker-java-sample/helloworld/pom.xml package -Pdocker
This will show an output like:
[INFO] DOCKER> [hello-java] : Built image sha256:428f7
The list of images can be checked:
docker images | grep hello-java
hello-java latest 428f7ac0195d 58 seconds ago 669.2 MB
Run the Docker container:
mvn -f docker-java-sample/helloworld/pom.xml install -Pdocker
This will show an output like:
[INFO] DOCKER> [hello-java] : Start container b3b0e4b63174
[INFO] DOCKER> [hello-java] : Waited on log out 'Hello' 504 ms
[INFO]
[INFO] --- docker-maven-plugin:0.14.2:logs (docker:start) @ helloworld ---
b3b0e4> Hello World!
This is similar output when running the Java application using java
CLI or the Docker container using docker run
command.
Only one change was required in the project to enable Docker packaging and running. A Maven profile is added in pom.xml
:
<profiles>
<profile>
<id>docker</id>
<build>
<plugins>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.14.2</version>
<configuration>
<images>
<image>
<name>hello-java</name>
<build>
<from>java</from>
<assembly>
<descriptorRef>artifact</descriptorRef>
</assembly>
<cmd>java -cp maven/${project.name}-${project.version}.jar org.examples.java.App</cmd>
</build>
<run>
<wait>
<log>Hello</log>
</wait>
</run>
</image>
</images>
</configuration>
<executions>
<execution>
<id>docker:build</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
<execution>
<id>docker:start</id>
<phase>install</phase>
<goals>
<goal>start</goal>
<goal>logs</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
TL;DR CMD
will work for most of the cases.
Default entry point for a container is /bin/sh
, the default shell.
Running a container as docker run -it ubuntu
uses that command and starts the default shell. The output is shown as:
> docker run -it ubuntu
root@88976ddee107:/#
ENTRYPOINT
allows to override the entry point to some other command, and even customize it. For example, a container can be started as:
> docker run -it --entrypoint=/bin/cat ubuntu /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
. . .
This command overrides the entry point to the container to /bin/cat
. The argument(s) passed to the CLI are used by the entry point.
TL;DR COPY
will work for most of the cases.
ADD
has all capabilities of COPY
and has the following additional features:
-
Allows tar file auto-extraction in the image, for example,
ADD app.tar.gz /opt/var/myapp
. -
Allows files to be downloaded from a remote URL. However, the downloaded files will become part of the image. This causes the image size to bloat. So its recommended to use
curl
orwget
to download the archive explicitly, extract, and remove the archive.