Building Container Image using Dockerfile

In the tutorial we are going to learn Building Container Image using Dockerfile. I am already published some article related to Containers, please visit those articles :

Building Base Containers

Dockerfile is a mechanism to automate the building of container images. There are three mazor steps to build a cointainer from dockerfile

    • Create a working directory
    • Write the Dockerfile
    • Build the image with Podman / docker

Create a Working Directory

Working directory contains all files which are needed to build the image. It is better to create an empty working directory to avoid incorporating unnecessary files into the image. root directory, /, should never be used as a working directory for image builds, for security reasons.

Dockerfile Specification

Dockerfile is a text file which contains instructions needed to build the image, it must be there in working directory to build the image. The basic syntax of a Dockerfile follows:

# Comment
INSTRUCTION arguments

To add any comment add hash, pound, or symbol(#) in starting of line. INSTRUCTION states for any instruction keyword for Dockerfile, it is not case-sensitive but to make it more visible keep it in capital letters is suggested.

First non-comment instruction must be a FROM instruction to specify the base image. Dockerfile instructions are executed into a new container using this image and then committed as a new image. Next instruction (if any) executes into that new image. The order of execution of instruction is the order of their appearance in Dockerfile.

Each instruction is independent from other instructions in the Dockerfile. Dockerfile instruction runs in an independent container using an intermediate image built from every previous command.

CMD and ENTRYPOINT

CMD and ENTRYPOINT instruction have two formats:

1. Exec form (using a JSON array):

ENTRYPOINT ["command", "param1", "param2"]
CMD ["param1","param2"]

2. Shell Form:

ENTRYPOINT command param1 param2
CMD param1 param2

Generally we use Exec form, it is recommended, because Shell form wraps the commands in a /bin/sh -c shell, creating a sometimes unnecessary shell process. Also, some combinations are not allowed, or may not work as expected. For example, if ENTRYPOINT is [“ping”] (exec form) and CMD is localhost (shell form), then the expected executed command is ping localhost, but the container tries ping /bin/sh -c localhost, which is a malformed command.

The Dockerfile should have at least one ENTRYPOINT and one CMD instruction, If a Dockerfile contains more than one ENTRYPOINT or CMD then only last instruction takes effect. CMD can be present without specifying an ENTRYPOINT. In this case, the base image’s ENTRYPOINT applies, or default ENTRYPOINT applies if none is defined.

CMD instruction can be override when starting a container by podman / docker. If it is present, all parameters for the podman run command after the image name forms the CMD instruction. For example, following instructions cause the running container to display the current time:

ENTRYPOINT ["/bin/date", "+%H:%M"]

ENTRYPOINT defines both commands to be executed with parameters, so  CMD instruction can’t be used. Below example provides the same functionality, with the added benefit of the CMD instruction being over writable when a container starts:

ENTRYPOINT ["/bin/date"]
CMD ["+%H:%M"]

In both cases, when a container starts the current time is displayed without providing a parameter:

[[email protected] ~]$ sudo podman run -it onionlinux/demo  10:44

It overwrites the CMD instruction, if a parameter appears after the image name in the podman run command,  The following command displays the current day of the week instead of the time:

[[email protected]]sudo podman run -it onionlinux/demo +%A Tuesday

Another approach is that we can use default ENTRYPOINT and the CMD instruction to define the initial command. Below instruction displays the current time, with the added benefit of being able to be overridden at run time

CMD ["date", "+%H:%M"]         

ADD and COPY

ADD and COPY instructions have two forms:

  • Shell Form
  • Exec Form

 Shell form:

ADD <source>... <destination> COPY <source>... <destination>

If source is a file system path, it must be inside the working directory.

ADD instruction also allows to specify a resource using a URL:

class="screen">ADD http://onionlinux.com/filename.pdf /var/www/html

If  source is a compressed file, then the ADD instruction decompresses the file to the destination folder. COPY instruction does not have this functionality.

Layering Image

Each instruction in a Dockerfile creates a new image layer. big images too many instructions in a Dockerfile creates too many layers.

RUN yum --disablerepo=* --enablerepo="rhel-7-server-rpms"
RUN yum update -y
RUN yum install -y mysql

Above example is not a good practice when creating container images. It creates three layers (one for each RUN instruction) while only the last is meaningful. You can achieve the same objective while creating a single layer by using the && conjunction like below:

RUN yum --disablerepo=* --enablerepo="rhel-7-server-rpms" && yum update -y && yum install -y httpd

We get readability issue while writing many RUN in a same line but using \ escape code we can break the lines and make it readable:

RUN yum --disablerepo=* --enablerepo="rhel-7-server-rpms" && \
    yum update -y && \
    yum install -y mysql

This also creates only one layer, and improves the readability. RUNCOPY, and ADD instructions create new image layers, but RUN can be improved this way.

Recommendation is to applying similar formatting rules to other instructions accepting multiple parameters, such as LABEL and ENV:

LABEL version="2.0" \
      description="This is a customized image for onionlinux users" \
      creationDate="15-06-2020"

ENV MYSQL_ROOT_PASSWORD="my_password" \
    MYSQL_DATABASE "my_database"

Building Container Image using Dockerfile

Now we are Building Container Image using Dockerfile. So we are Creating a Apache Web Server Container Image using Dockerfile.

Creating a Apache Web Server Container Image using Dockerfile

# This is a comment line >> 1
FROM ubi7/ubi:7.6 >>2
LABEL description="This is a custom httpd container image" >>3
MAINTAINER Onionlinux <[email protected]>  >>4
RUN yum install -y httpd >>5
EXPOSE 80 >>6
ENV LogLevel "info" >>7
ADD http://someserver.com/filename.pdf /var/www/html >>8
COPY ./src/ /var/www/html/ >>9
USER apache >>10
ENTRYPOINT ["/usr/sbin/httpd"] >>11
CMD ["-D", "FOREGROUND"]  >>12
1 comments always starts with a hash, or pound, sign (#).
2 FROM instruction declares that the new container image extends ubi7/ubi:7.6 container base image. Dockerfiles can use any other container image as a base image.
3 LABEL is responsible for adding generic metadata to an image. It is a simple key-value pair.
4 MAINTAINER indicates the Author  of the generated container image’s metadata. To view image metadata We can use podman inspect command.
5 RUN executes commands in a new layer on top of current image. default shell which is used to execute commands is /bin/sh.
6  To specify listening network port in container use EXPOSE instruction. It defines metadata only, it does not make ports accessible from the host. We can use -p option in the podman run command to exposes container ports from the host.
7 ENV is use to defining environment variables for container. We can declare multiple ENV instructions within a Dockerfile. We can use the env command inside the container to view each of the environment variables.
8 To copy files or folders from a local or remote source and adds them to the container’s file system we can use ADD instruction . To copy local files, files must be in the working directory. ADD instruction unpacks local .tar files to the destination image directory.
9 It is not possible to copy a remote file using its URL with this Dockerfile instruction. COPY copies files from the working directory and adds them to the container’s file system.
10 USER instruction used to specifies the username or the UID when running the container image for the RUNCMD, and ENTRYPOINT instructions. Recommendation is to define a different user other than root for security reasons.
11 default ENTRYPOINT in container is /bin/sh -c, It specifies the default command to execute when the image runs in a container.
12 If the default ENTRYPOINT applies (/bin/sh -c), then CMD forms an executable command and parameters that runs at container to start, CMD provides the default arguments for the ENTRYPOINT instruction.

Building Images with Podman

The podman build command used to builds a new image based on the instructions in Dockerfile . The syntax for this command is as follows:

$ podman build -t NAME:TAG DIR

DIR is the path of working directory, which must be included in the Dockerfile. If working directory is current directory it can be defined by a dot (.) . NAME:TAG is a name with a tag given to the new image. If TAG is not specified, then the image  automatically tagged as latest tag.

You can read more about container architecture


I hope you like this post “Building Container Image using Dockerfile”, if you have any questions? please leave comment below! 

Thanks for reading. If you like this post probably you might like my next ones, so please support me by subscribing my blog.

Rohit Kumar Singh

Technical writer, enthusiastic to learn new technologies and exploring the things.

Leave a Reply

Your email address will not be published. Required fields are marked *