Intro to the Dockerfile Components

1249 VIEWS

· ·

What is a Dockerfile?

Dockerfile - What is it?

It will be fair to state that in this article we will be juggling between some common terms associated with the Dockerfile, that is Docker, Docker Images, and Containers. Let’s understand what this terms mean:

Docker is an open-source project designed to make it easier to develop, deploy, and run applications by the use of containers. It allows developers to package up an application with all the parts it needs into a container, then ships it out mutually as one package.

Docker Container is a software package that will include all dependencies required such as libraries, config files, etc… to run an application and ship them as one package.

Docker Image is a snapshot of what a project contains and it is represented by a Dockerfile. This file contains everything that you need to run an application. They are either using a predefined Image or using a Dockerfile

That being said, we can describe a Dockerfile as a text document that contains a series of commands that are used to automatically create containers and assemble an image on the Docker platform.

As defined it is a text document, also it’s important to state that the Dockerfile has no extension, all you need is to save it as Dockerfile

It is within the Dockerfile that applications, frameworks, servers, base images are specified.

All commands initiated in the Docker file are executed from top to bottom with each step being cached. Execution of these instructions takes place on a base image.

On building the Dockerfile, the successive actions form a new image from the base parent image which is specified in the file as the first command.

Dockerfile Syntax

The Dockerfile syntax consists of two main line blocks, that is the comment block and the command block.

Syntax

# Comment
COMMAND argument

An example in a file:

# Prints greeting message
RUN echo "Greetings Team SweetCode"

The Commands are not usually case sensitive, but in order to differentiate them from the arguments they are usually written in UPPERCASE and arguments in lowercase as shown above.

When writing a Dockerfile, there are a dozen of commands which can be specified inside the file. Let’s go ahead and have a look at the most commonly used.

Dockerfile Commands 

Besides this we have common practices that are considered must in a Dockerfile, In each file the first command will always be FROM command.

That being said, let’s begin:

FROM

It is considered the most crucial amongst all other commands in the Dockerfile. A Dockerfile MUST begin with the FROM command.

It defines the base image which will be used in the build process.

Syntax

FROM <Image name>
or
FROM <image>:<tag>
or
FROM <image>@<digest>

The image name defines where the new image should inherit from.

Example

FROM Ubuntu

What basically this command does is, it tells the Docker builder to use this base image as a starting point. It’s going to download the latest Ubuntu version from the docker hub.

To learn more about all the Images we have, check out the Docker Hub here https://hub.docker.com/.

MAINTAINER

It is an optional command, it declares the author of the image. It is considered a good practice to declare it after the FROM command.

Syntax

MAINTAINER <authors_name>

Example

MAINTAINER = Hillary <[email protected]>
RUN

It specifies the commands that are run inside the container. Once the container is started the commands are executed.

Syntax

RUN <command>

Example

RUN apt-get update

What this does, is it updates all the existing packages within your image.

We can have multiple RUN commands set at a go like:

RUN apt-get update
RUN apt-get install python3 -y

It is fair to note that, by doing this it will make the size of our Image bigger hence the image will take longer to build. Hence it is considered a good practice to use a command and chain the other arguments with the help of &&, like below:

RUN apt install python3-pip -y && pip3 install --upgrade pip
CMD

It is only declared once in a Dockerfile. In a case it is listed more than once only the last one will be executed. It is similar to RUN,  however unlike RUN, it is not executed during build, but when the container is instantiated using the image built.

Syntax

CMD ['executable' 'param1','param2']

Example

CMD "echo" "Hello from SweetCode"
ENTRYPOINT

ENTRYPOINT instructions build Dockerfiles meant to run specific commands. Just like CMD, they execute when the container has already started. It exists once in a Dockerfile. 

The ENTRYPOINT command can overwrite the CMD command during run.

Syntax

ENTRYPOINT <command>
or
ENTRYPOINT ["executable command", "param1", "param2", "..."]

Example

ENTRYPOINT ["echo", "Hello, Hillary"]
ADD

It gets two arguments, in this case a source and a destination, it copies the files from the source on a host machine into the destination.

It also accepts URLs as source. Just in case the source file is in the form of a tarball file, Dockerfile extracts it into the destination, too.

Syntax

ADD <src> <dest>
or
ADD ["<src1>", "<src2>", "<...>", "<dest>"]

Example

ADD django-project.tar.gz /Projects/

This copies the file with the name “django-project.tar.gz” from the local machine into the /Projects directory of the image and extracts it there.

ENV

ENV gets environment variables. These consist of key value pairs, which scripts access within the container.

Syntax

ENV <key>=<value>

The key and value are mandatory fields.

Example

ENV PYTHONDONTWRITEBYTECODE 1
WORKDIR

It sets the default work directory path within our image to store our code together with the other commands. It sets where Dockerfile executes the commands.

If not set, Dockerfile creates it automatically.

Syntax

WORKDIR /path/to/workdir

Example

WORKDIR /code/
EXPOSE

This associates a port number to an application inside a container.

It is useful more so when building frontend applications, the specified port will be accessible from inside the container.

Syntax

EXPOSE <port>

Example

EXPOSE 8020
USER

It is used to set the username/UID, which is to run the container based on the image being built.

Syntax

USER <user>
or
USER <uid>

Example

USER 751
VOLUME

This sets a custom path where your container will store all the files. The data in the host machine and the container will always be the same.

Syntax

VOLUME <mount point>

Example

VOLUME users/l/Desktop

In the above example, if your application stores its data in “users/l/Desktop”, it becomes defined in the VOLUME as a mounting point. With this it is possible to define the host directory that should be mounted to this point as a shared directory between directory and host machine.

How to Write a Dockerfile

Each command that is specified in the Dockerfile creates a layer during the execution. So, when writing a Dockerfile, there are some guidelines you should consider to write a good file.

Let’s have a look at some of the best practices to follow while writing:

  1. Image Size

Using smaller images will result in faster deployments, In order for you to keep your image size smaller be sure to avoid installing unnecessary dependencies.

  1. Keep it safe

It is a good practice to perform scans to your images, this will help you identify vulnerabilities if any.

We have tools to help with this, like Docker’s own command: Docker Scan. Once run it assesses the operating system packages installed in the image and match them against known lists of Common Vulnerabilities and Exposures. Suggested remediation steps are provided for each detected problem.

  1. Maintainability

Choosing the right image will reduce the burden for you. To help in this using official Docker images will help because they are:

  • Trusted and secure
  • Maintained and constantly updated
  • Tested and guaranteed for use
  1. Keep it updated

Dockerfile can change overtime, keeping them updated with the latest changes will be helpful for your production.

A Good way to always keep track and easy update is by using version control, you can easily keep track and review changes you make to your Dockerfile.

Get more insight about the Dockerfile best practices HERE

Example of a Dockerfile

# FROM directive instructing base image to build upon
FROM ubuntu:latest


MAINTAINER = Hillary “[email protected]”

# COPY startup script into known file location in container
COPY start.sh /usr/local/Desktop/start.sh

# COPY Django Project folder and requirements.txt into the working directory
COPY Portfolio  /usr/local/Desktop/Portfolio
COPY requirements.txt  /usr/local/Desktop/

# install python 3 and pip
RUN apt-get update
RUN apt-get install python3 -y
RUN apt install python3-pip -y && pip3 install --upgrade pip

# install Django from the requirements file
RUN cd /usr/local/Desktop && pip install -r requirements.txt 

# EXPOSE port 8020 to allow communication to/from server
EXPOSE 8020

# CMD specifies the command to execute to start the server running.
CMD ["/start.sh"]
# done! :)

Breakdown of the Dockerfile

First we use the FROM command to pull the base image, in this case the latest version of ubuntu.

Next we use the COPY command to copy the required files of our project into the Desktop, that is the *script, project folder (Portfolio) and the requirements.txt file.

The third step is to install python and pip using the  RUN command. After installation we still use the RUN command to install the dependencies to our project, usually listed in the requirements.txt file.

The EXPOSE command is used to assign a port where the application will run, in this case 8020.

And finally the CMD which will execute the listed commands when the container is built.

The above is an example of a Dockerfile from one of my Django projects.

Conclusion

We can consider Dockerfile as the recipe of our image. In this article we have discussed it’s components and best practices to follow when writing one.

Now, if you are new to Docker, I recommend putting into action some of these best practices when you will be writing your first Dockerfile. It will help you to understand them further and allow you to use Docker smartly.


Hillary ("Lary") Nyakundi is a Growing Developer, with great interest in technology, open-source and the Python programming language. He is also a technical writer aiming to help share knowledge with other developers through informative articles. Through this, he has been able to work with tech companies from the US, India and Kenya. His passion in the developer world led him to start a podcast, “Let’s Talk Developer” where he gets to connect with other developers, learn from them and share their stories to help inspire upcoming developers.


Discussion

Click on a tab to select how you'd like to leave your comment

Leave a Comment

Your email address will not be published.

Menu
Skip to toolbar