How to Build an Image with the Dockerfile – SitePoint

Building the app, installing dependencies and services, automating deployment, and more—it all starts with Dockerfile. Let’s review the syntax, from basic to elaborate, and some best practices when creating your Docker images.

In this guide, we will write a Dockerfile instructing Docker to select a minimum Linux (base image) for the application we will deliver, and send with it a set of tools of our choice and a certain configuration, effectively building our own Linux distribution that is suitable for running our application.

<img src="https://uploads.sitepoint.com/wp-content/uploads/2016/11/1479211726build-image-with-dockerfile.jpg" alt="

Creating an image with Dockerfile

” />

Why

Docker

docker_logo

With Docker you can “Build, ship, and run any application, anywhere.” That is, you can package your application with all the runtime binaries and libraries, back-end tools, operating system tweaks, and even specific services your application needs to run, and make it available for instant delivery and automatic deployment.

The software container technology that Docker implements is what makes it possible. And while I won’t cover many of the details behind it here, you can read more about Docker, what software containers are, and how they work in Understanding Docker, Containers, and Safer Software Delivery.

Installing Docker Before you

begin, you’ll need to have Docker installed, either on your local machine or on a remote server

.

Luckily, the latest version of Docker (1.12 at the time of writing) made the installation process really smooth, and it has easy-to-follow guides for Windows, MacOS, and Linux.

The Dockerfile To create an image in Docker, you must first set the instructions for this build to a plain text file called

Dockerfile

and a context (more on this later). This file has a syntax similar to that of Apache configuration files: one statement per line with its respective arguments, and all instructions processed in sequence, one after the other. Comments are preceded by the # character and a blank space. Finally, once you have a Dockerfile, the docker build command will compile the image, as we’ll see in more detail later.

Before we start writing the Dockerfile, we’ll set the workspace. We will create a directory called my_image in our home directory, use it as our working directory and place

the Dockerfile there: mkdir ~/my_build cd ~/my_build touch Dockerfile

Now we are ready to start building

the image.

Selecting the base image

Most

of the time, when creating an image, you will use a starting point, i.e. Another image. This can be an official Ubuntu, MySQL, WordPress or any other image available on Docker Hub. You can also use an image that you created yourself before.

Note: You can create your own base image with your own basic tools and directory structure, using the minimum reserved Docker image, called scratch. It’s a process I won’t cover here, but you can check out the Docker site’s guide on how to create a base image.

For example, if you want to start with a minimal Debian distribution, you will add the following content to the

Dockerfile: # set the base image FROM debian

FROM should be the first statement you use when writing a Dockerfile. Note that you can also use a specific version of the base image, adding : and the version_name to the end of the image name. For example

: # set the base image FROM debian:sid In the

code above, we are using the Debian “sid” (unstable distribution). This will be relevant also when you want a specific version of a Ruby or Python interpreter, a version of MySQL, or whatever, when you use an official base image for any of these tools. For now, we’ll stick to the default (stable) Debian image for this guide.

Specifying a maintainer and adding

metadata Optionally, you can specify who the MAINTAINER is, replacing Lucero del Alba

with his name or the person or team responsible for the compilation: # author MAINTAINER Lucero del Alba

It is not necessary, but we can also add some metadata using the LABEL statement, and this information will be available later when we use the docker inspect command to examine the image:

# additional metadata LABEL version=”1.0″ LABEL description=”First image with Dockerfile”.

For more information about this feature, see Docker object tags.

Making your own distro

At this point, we are going to select some tools and libraries to include in our image, so that our container has everything it needs for what we intend it to do. At the end of this tutorial, we will do something that is very close to building a Linux distribution.

Some containers, such as one running a PostgreSQL database, are designed to run in the background. But we often need a console to perform some operations on the container, so we will likely need some additional tools, because the base image will include only a minimal set of GNU tools.

Dealing with

cache issues

It is almost guaranteed that you will experience cache issues when trying to install additional packages on your image. This is because the base image comes with cached metadata, and the active repositories from which it pulls data often change.

In Debian-based distributions, you can handle this by adding the following commands before installing new packages

: # update source list RUN apt-get clean RUN apt-get update

Installing

basic tools

Code editors, locales, tools like Git or TMUX: this is the time to install everything you’ll need later, so they’re included in the image.

We will install one per line:

# install basic apps, one per line for better caching RUN apt-get install -qy git RUN apt-get install -qy local RUN apt-get install -qy nano RUN apt-get install -qy tmux RUN apt-get install -qy wget

We could install them all on a single line, but if we then want to add or remove a package, we must rerun the whole process. Therefore, the best practice here is to install one package per line so that you can benefit from Docker caching.

Also, keep it tight. You don’t want to install tools “just in case” as this can increase compile time and image size.

Installing runtime libraries for your

app

We will also submit our app in this image. Do you need a specific version of PHP, Ruby or Python, along with certain modules? Now is the time to deliver all the programs and runtimes that our application will need.

Be as specific as you want, as this container is designed to run only your application:

# install runtimes and application modules RUN apt-get install -qy python3 RUN apt-get install -qy python3-psycopg2 RUN apt-get install -qy python3-pystache RUN apt-get install -qy python3-yaml

For this example, we will install Python 3 with the Psycopg 2 packages (to connect to PostgreSQL databases), the Mustache module for Python and the YAML module. (Naturally, you’ll install the specific dependencies you need by making your own Dockerfile.)

Building and downloading packages

It is also possible that your distribution does not have a package for a certain module or program that you need. But you don’t need to manually install it in your running container! Instead, you can use the RUN statement (one per line) to batch process the download, build, and configure library process that your application needs.

You can even write a script in a separate file, add this file to the build, and run it, as we’ll see later in the “Submitting Your Own Application” section.

Cleaning

To keep your image tidy and as small as possible, it’s also a good idea to do a cleanup at the end of the installation sequence: # cleanup

RUN apt-get -qy autoremove

Again, note that we are using apt-get because we chose Debian, but use the appropriate command for your base image distribution.

Submitting your own app

The goal of creating this environment is so that you can deliver your application smoothly and ready to run. To add files, directories, and even the contents of remote URLs to the image, we’ll use the ADD statement.

However, before adding files, we must put them in the appropriate context. To make things easier, we’ll simply locate everything in the my_build directory mentioned above, along with the Dockerfile itself.

Let’s say that, with the app and everything we want to put in the image, we

have the following files in ~/my_build (where app.py and lib.py are inside

the app/ subdirectory): .bashrc .profile app/app.py app/lib.py Dockerfile

We will add .bashrc and .profile scripts to the /root directory in the container to run every time we launch a shell in the container, And we will copy the contents of app/ in the /app/ directory of the container.

We added the following instructions:

# add scripts to the container ADD .bashrc /root/.bashrc ADD .profile /root/.profile # Add the app to the ADD container app /app

Environment settings

Finally, we

will set some environment variables that we will need at the system and application level.

Many of you will do well with the default Debian character set, but since we are targeting an international audience, let’s see how to have a UTF-8 terminal. We previously installed the locale package, so all we have to do now is generate the character sets and set the appropriate Linux environment: # locales

for UTF-8 RUN locale-gen C.UTF-8 && /usr/sbin/update-locale LANG=C.UTF-8 ENV LC_ALL C.UTF-8

You may also need to set some environment variables for your application, to exchange passwords and routes.

The Dockerfile provides the ENV statement to do just this: # ENV PYTHONIO application environmentUTF-8 ENV PYTHONPATH coding /app/

Note that you can also pass environment variables from the command line when launching the container, which can be convenient for sharing sensitive information such as passwords.

The complete Dockerfile Naturally, you will have to adapt the Dockerfile

to your needs, but I hope you get an idea of the possibilities

.

Here is the full file:

# author MAINTAINER Lucero del Alba # additional metadata LABEL version=”1.0″ LABEL description=”First image with

Dockerfile

.” # set the base image FROM debian # update RUN font list apt-get clean RUN apt-get update # install basic applications, one per line for better caching RUN apt-get install -qy git RUN apt-get install -qy local RUN apt-get install -qy nano RUN apt-get install -qy tmux RUN apt-get install -qy wget # install runtime applications and modules RUN apt-get install -qy python3 RUN apt-get install -qy python3-psycopg2 RUN apt-get install -qy python3-pystache RUN apt-get install -qy python3-yaml # cleanup RUN apt-get -qy autoremove # add scripts to the container ADD .bashrc /root/. bashrc ADD .profile /root/.profile # add the application to the container ADD app /app # locals to UTF-8 RUN locale-gen C.UTF-8 && /usr/sbin/update-locale LANG=C.UTF-8 ENV LC_ALL C.UTF-8 # app environment ENV PYTHONIOENCODING UTF-8 ENV PYTHONPATH /app/

Building

the image

From within the my_build directory, we will use the docker build command, Passing the -t flag to “tag” the new image with a name, which in this case will be my_image. The. indicates that the Dockerfile is in the current directory, along with the so-called “context”, i.e. the rest of the files that may be in that location:

cd ~/my_build docker build -t my_image .

That will generate a long output where each “step” is an instruction in our Dockerfile. This is truncated output

: Send build context to Docker 5.12 kB daemon Step 1 : FROM debian -> 7b0a06c805e8 Step 2 : MAINTAINER Dawn Lucifer -> Execution on d37e46e5455d -> 2d76561de558 Removing the intermediate container d37e46e5455d Step 3: LABEL version “1.0” -> Running on 904dde1b4cd7 -> a74b7a492aaa Removing the intermediate container 904dde1b4cd7 Step 4 : Description of LABEL “First image with Dockerfile”. -> Execution in 9aaef0353256 -> 027d8c10e966 Intermediate deletion container 9aaef0353256 Step 5 : RUN apt-get clean -> Run on bc9ed85dda16 -> a7407036e74a Removing the intermediate container bc9ed85dda16 Step 6 : RUN apt-get update -> Run on 265e757a7563 Get:1 http://security.debian.org jessie/updates InRelease [63.1 kB] ign http://deb.debian.org jessie InRelease Get:2 http://deb.debian.org jessie-updates InRelease [145 kB] Get:3 http://deb.debian.org jessie Release.gpg [2373 B] Get:4 http://deb.debian.org jessie Release [148 kB] Get:5 http://security.debian.org Jessie/Updates/Main amd64 packages [402 kB] Get:6 http://deb.debian.org Jessie-Updates/Main amd64 packages [17.6 kB] Get 7 http://deb.debian.org Jessie/Main AMD64 packages [9064 kB] Obtained 9843 kB in 10s (944 kB/s) Reading package lists… -> 93fa0a42fcdc Removal from intermediate container 265e757a7563 Step 7 : RUN apt-get install -qy git -> Running on c9b93cecd953 (…)

List

of images

We can list

our images with the command docker images: docker images

This will generate our newly created my_image along with other base images we have downloaded:

REPOSITORY TAG IMAGE ID CREATED SIZE my_image last e71dc183df2b 8 seconds ago 305.6 MB debian latest 7b0a06c805e8 2 weeks ago 123 MB debian sid c1857cb435d7 3 weeks ago 97.77 MB

… And there it is, our image is ready to ship and run!

Starting

a container Finally, to

launch an interactive terminal of our newly created image, we will use the docker run command: docker run

-ti my_image /bin/bash

What to do next

I haven’t covered all the possibilities of Dockerfile. In particular, I haven’t reviewed how to EXPOSE ports so you can run services and even link containers together; how HEALTHCHECK containers to verify that they still work; or even how to specify a VOLUME to store and retrieve data from the host machine… among other useful features.

We will be able to cover them in future articles. For now, you may want to check out the following resources.

From

the Docker website

: Dockerfile reference

  • Best practices for writing Dockerfiles

From SitePoint:

Understanding Docker,

  • Containers, and Delivering More Secure Software
  • The Docker

  • subchannel
  • All Docker related articles

Contact US