Skip to content

Building Images

The Docker images are the fundamental building blocks of containers. These images could be very basic operating environments, such as busybox or ubuntu.

Dockerfile is a text-based build script that contains special instructions in a sequence for building the right and relevant images from the base images.

$ cat Dockerfile 
FROM busybox:latest 
CMD echo Hello World!! 

In the above Dockerfile, we see the two instructions mentioned earlier: * The first instruction is for choosing the base image selection. In this example, we will select the busybox:latest image * The second instruction is for carrying out the command CMD, which instructs the container to echo Hello World!!.

$ sudo docker build .
   Sending build context to Docker daemon 3.072 kB 
   Sending build context to Docker daemon 
   Step 0 : from busybox:latest 
   Successfully built 0a2abe57c325 
$ sudo docker run 0a2abe57c325 
   Hello World!! 

Dockerfile Syntax

A Dockerfile is made up of instructions, comments, and empty lines:

# Comment
INSTRUCTION arguments
A valid Dockerfile comment line always begins with a # symbol as the first character of the line:
# This is my first Dockerfile comment 
The # symbol can be a part of an argument:
CMD echo ### Welcome to Docker ### 
If the # symbol is preceded by a whitespace, then it is considered as an unknown instruction by the build system:
   # this is an invalid comment line 

FROM instruction

  • The FROM instruction is the most important one and it is the first valid instruction of a Dockerfile.
  • It sets the base image for the build process.
  • The subsequent instructions would use this base image and build on top of it.
  • The docker build system lets you flexibly use the images built by anyone.
  • If the image is not found in the Docker host, then the docker build system will pull the image from the publicly available Docker Hub Registry.

The FROM instruction has the following syntax:

FROM <image>[:<tag>] 

Example:
FROM ubuntu:14.04 

MAINTAINER instruction (deprecated)

  • The MAINTAINER instruction is an informational instruction of a Dockerfile.
  • This instruction capability enables the authors to set the details in an image.
  • Docker does not place any restrictions on placing the MAINTAINER instruction in Dockerfile.
  • However, it is strongly recommended that you should place it after the FROM instruction.

The following is the syntax of the MAINTAINER instruction, where can be in any text.

MAINTAINER <author's detail> 
it is strongly recommended that you use the image author's name and the e-mail address, as shown in this example:
MAINTAINER John Doe <john.doe@gmail.com> 

COPY instruction

The COPY instruction enables you to copy the files from the Docker host to the filesystem of the new image:

COPY <src> ... <dst>

  • <src>: This is the source directory, the file in the build context, or the directory from where the docker build subcommand was invoked
  • ...: This indicates that multiple source files can either be specified directly or be specified by wildcards
  • <dst>: This is the destination path for the new image into which the source file or directory will get copied. If multiple files have been specified, then the destination path must be a directory and it must end with a slash /

copy the html directory from the source build context to /var/www/html:

COPY html /var/www/html 
copy the multiple files from the source build context to /etc/httpd/conf/, which is in the image filesystem:
COPY httpd.conf magic /etc/httpd/conf/

ADD instruction

The ADD instruction is similar to the COPY instruction. However, in addition to the functionality supported by the COPY instruction, the ADD instruction can handle the TAR files and the remote URLs.

ADD <src> ... <dst> 
* <src>: This is either the source directory or the file that is in the build context or in the directory from where the docker build subcommand will be invoked. However, the noteworthy difference is that the source can either be a TAR file stored in the build context or be a remote URL. * ...: This indicates that the multiple source files can either be specified directly or be specified by using wildcards. * <dst>: This is the destination path for the new image into which the source file or directory will be copied.

copying the TAR file to the target image and extracting the TAR file from the root directory (/) of the target image:

ADD web-page-config.tar /
For example, the above instruction can add the follwoing files to the root directory:
etc/httpd/conf/httpd.conf 
var/www/html/index.html 
var/www/html/aboutus.html 
var/www/html/images/welcome.gif 
var/www/html/images/banner.gif 

ENV instruction

The ENV instruction sets an environment variable in the new image. An environment variable is a key-value pair, which can be accessed by any script or application. The Linux applications use the environment variables a lot for a starting configuration. The following line forms the syntax of the ENV instruction:

ENV <key> <value> 
* <key>: This is the environment variable * <value>: This is the value that is to be set for the environment variable

For example you can set the debug level or log directory of your software as sollows:

ENV DEBUG_LVL 3 
ENV APACHE_LOG_DIR /var/log/apache 

USER instruction

The USER instruction sets the start up user ID or username in the new image.
By default, the containers will be launched with root as the user ID or UID. Essentially, the USER instruction will modify the default user ID from root to the one specified in this instruction.

USER <UID>|<username> 

  • <UID>: This is a numerical user ID
  • <username>: This is a valid username

It is recommended that you have a valid user ID or usename to match with the /etc/passwd file, the user ID can contain any random numerical value. otherwise the docker run subcommand will fail and it returns error message

WORKDIR instruction

The WORKDIR instruction changes the current working directory from / to the path specified by this instruction. The ensuing instructions, such as RUN, CMD, and ENTRYPOINT will also work on the directory set by the WORKDIR instruction.

WORKDIR <dirpath> 
* <dirpath> is the path for the working directory to set in.

The path can be either absolute or relative. In case of a relative path, it will be relative to the previous path set by the WORKDIR instruction. If the specified directory is not found in the target image filesystem, then the director will be created.

WORKDIR /var/log 

VOLUME instruction

The VOLUME instruction creates a directory in the image filesystem, which can later be used for mounting volumes from the Docker host or the other containers.

The VOLUME instruction has two types of syntax, as shown here: The first type is JSON array (all values must be within doublequotes (")):

VOLUME ["<mountpoint>"] 
The second type is a single value:
VOLUME <mountpoint> 
In the preceding line, <mountpoint> is the mount point that has to be created in the new image.

EXPOSE instruction

The EXPOSE instruction opens up a container network port for communicating between the container and the external world.

EXPOSE <port>[/<proto>] [<port>[/<proto>]...]
* <port>: This is the network port that has to be exposed to the outside world. * <proto>: This is an optional field provided for a specific transport protocol, such as TCP and UDP.

If no transport protocol has been specified, then TCP is assumed to be the transport protocol. The EXPOSE instruction allows you to specify multiple ports in a single line.

EXPOSE 7373/udp 8080 

RUN instruction

The RUN instruction is the real workhorse during the build time, and it can run any command. The general recommendation is to execute multiple commands by using one RUN instruction. This reduces the layers in the resulting Docker image because the Docker system inherently creates a layer for each time an instruction is called in Dockerfile. The RUN instruction has two types of syntax: The first is the shell type

RUN <command> 
The second syntax type is either exec or the JSON array, as shown here:
RUN ["<exec>", "<arg-1>", ..., "<arg-n>"] 
* <exec>: This is the executable to run during the build time. * <arg-1>, ..., <arg-n>: These are the variables (zero or more) number of the arguments for the executable.

CMD instruction

The CMD instruction can run any command (or application), which is similar to the RUN instruction. The major difference between those two is the time of execution. The command supplied through the RUN instruction is executed during the build time, whereas the command specified through the CMD instruction is executed when the container is launched from the newly created image. It can be overridden by the docker run subcommand arguments ( docker run image command ) The CMD instruction has three types of syntax: The first syntax type is the shell type:

CMD <command> 
Within this, the <command> is the shell command, which has to be executed during the launch of the container.

The second type of syntax is exec or the JSON array:

CMD ["<exec>", "<arg-1>", ..., "<arg-n>"] 
* <exec>: This is the executable, which is to be run during the container launch time. * <arg-1>, ..., <arg-n>: These are the variable (zero or more) numbers of the arguments for the executable.

The third type of syntax is also exec or the JSON array, which is similar to the previous type. However, this type is used for setting the default parameters to the ENTRYPOINT instruction:

CMD ["<arg-1>", ..., "<arg-n>"] 
* <arg-1>, ..., <arg-n>: These are the variable (zero or more) numbers of the arguments for the ENTRYPOINT instruction, which will be explained in the next section.

Syntactically, you can add more than one CMD instruction in Dockerfile. However, the build system would ignore all the CMD instructions except for the last one. In other words, in the case of multiple CMD instructions, only the last CMD instruction would be effective.

ENTRYPOINT instruction

The ENTRYPOINT instruction will help in crafting an image for running an application (entry point) during the complete life cycle of the container, which would have been spun out of the image.

When the entry point application is terminated, the container would also be terminated along with the application and vice versa. Therefore, the ENTRYPOINT instruction would make the container function like an executable.

Functionally, ENTRYPOINT is akin to the CMD instruction, but the major difference between the two is that the entry point application is launched by using the ENTRYPOINT instruction, which cannot be overridden by using the docker run subcommand arguments. Docker provides a mechanism for overriding the entry point application through the --entrypoint option in the docker run subcommand. The --entrypoint option can accept its argument, and so it has limited functionality.

the ENTRYPOINT instruction is very similar to the RUN and CMD instructions it has two types of syntax: The first type of syntax is the shell type

ENTRYPOINT <command>
* <command> is the shell command, which is executed during the launch of the container. If this type of syntax is used, then the command is always executed by using /bin/sh -c. The second type of syntax is exec or the JSON array:
ENTRYPOINT ["<exec>", "<arg-1>", ..., "<arg-n>"] 
* <exec>: This is the executable, which has to be run during the container launch time. * <arg-1>, ..., <arg-n>: These are the variable (zero or more) numbers of arguments for the executable.

HEALTHCHECK

The HEALTHCHECK instruction has two forms:

HEALTHCHECK [OPTIONS] CMD command (check container health by running a command inside the container)
HEALTHCHECK NONE (disable any healthcheck inherited from the base image)
The HEALTHCHECK instruction tells Docker how to test a container to check that it is still working. This can detect cases such as a web server that is stuck in an infinite loop and unable to handle new connections, even though the server process is still running.

When a container has a healthcheck specified, it has a health status in addition to its normal status. This status is initially starting. Whenever a health check passes, it becomes healthy (whatever state it was previously in). After a certain number of consecutive failures, it becomes unhealthy.

The options that can appear before CMD are:

  --interval=DURATION (default: 30s)
  --timeout=DURATION (default: 30s)
  --start-period=DURATION (default: 0s)
  --retries=N (default: 3)
The health check will first run interval seconds after the container is started, and then again interval seconds after each previous check completes.

There can only be one HEALTHCHECK instruction in a Dockerfile. If you list more than one then only the last HEALTHCHECK will take effect.

The command after the CMD keyword can be either a shell command (e.g. HEALTHCHECK CMD /bin/check-running) or an exec array (as with other Dockerfile commands; see e.g. ENTRYPOINT for details).

The command’s exit status indicates the health status of the container. The possible values are:

  • 0: success - the container is healthy and ready for use
  • 1: unhealthy - the container is not working correctly
  • 2: reserved - do not use this exit code

For example, to check every five minutes or so that a web-server is able to serve the site’s main page within three seconds:

HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1

LABEL instruction

The LABEL instruction adds metadata to an image. A LABEL is a key-value pair. To include spaces within a LABEL value, use quotes and backslashes as you would in command-line parsing.

LABEL <key>=<value> <key>=<value> <key>=<value> ... 
A few usage examples:
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
       that label-values can span multiple lines."

To set a label corresponding to the MAINTAINER field you could use:

LABEL maintainer="John.Doe@example.com"

To view an image’s labels, use the docker image inspect command. You can use the --format option to show just the labels;

docker image inspect --format='' myimage

ONBUILD instruction

The ONBUILD instruction registers a build instruction to an image and this is triggered when another image is built by using this image as its base image.

Any build instruction can be registered as a trigger and those instructions will be triggered immediately after the FROM instruction in the downstream Dockerfile.

The ONBUILD instruction can be used to defer the execution of the build instruction from the base image to the target image.

ONBUILD <INSTRUCTION> 
* <INSTRUCTION> is another Dockerfile build instruction, which will be triggered later. The ONBUILD instruction does not allow the chaining of another ONBUILD instruction. In addition, it does not allow the FROM and MAINTAINER instructions as ONBUILD triggers. Here is an example of the ONBUILD instruction:
ONBUILD ADD config /etc/appconfig 

NGINX

  • Create a simple HTML file
  • Create a Docker File with base image nginx
  • Copy HTML file to /usr/share/nginx/html/
  • Build Image
  • Run Container

PHP

  • Create a simple PHP app (file)
  • Create a Docker File with base image php:7.4-cli
  • Copy app to the image directory /usr/src/myapp
  • Set the working directory to /usr/src/myapp
  • Add php command with app name as input argument
  • Build Image
  • Run Container