Building the app, installing the dependencies and services, automating the deployment, and more --- it all starts with the Dockerfile. Let's review the syntax, from basic to elaborate, and some best practices when building your Docker images.
In this guide, we'll write a Dockerfile instructing Docker to select a minimal Linux (base image) for the application we'll deliver, and ship with it a set of tools of our choice and a certain configuration, effectively building our own Linux distribution which is just right for running our app.
Why Docker
With Docker you can "Build, ship, and run any app, anywhere". That is, you can pack your application with all of the binaries and runtime libraries, back-end tools, OS tweaks, and even specific services your application needs for running --- and make it readily available for instant delivery and automatic deployment.
The software containers technology that Docker implements is what makes this possible. And although I won't cover here much of the detail behind it, 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 starting, you'll need to have Docker installed, whether it's on your local machine or on a remote server.
Fortunately, the latest version of Docker (1.12 as of this writing) made the installation process really smooth, and you have easy-to-follow guides for Windows, MacOS and Linux.
The Dockerfile
In order to build an image in Docker, you first need to set the instructions for this build on a plain text file named Dockerfile
and a context (more on this later). This file has a syntax similar to that of Apache configuration files --- one instruction per line with its respective arguments, and all instructions processed in sequence, one after another. Comments are preceded by the #
character and a whitespace. Finally, once you have a Dockerfile, the command docker build
will build the image, as we'll see in more detail later.
Before we start writing the Dockerfile, we'll set the working space. We'll create a directory called my_image
in our home directory, use it as our working directory, and place the Dockerfile
in there:
mkdir ~/my_build
cd ~/my_build
touch Dockerfile
Now we're ready to start building the image.
Selecting the base image
Most of the time when creating an image, you'll use a starting point --- that is, another image. This can be an official Ubuntu, MySQL, WordPress, or any other image available from the Docker Hub. You can also use an image you created yourself previously.
Note: You can create your own base image with your own core tools and directory structure, using Docker's reserved, minimal image, called scratch
. It's a process I won't cover here, but you can refer to the Docker site's guide on creating a base image.
For example, if you want to start off with a minimal Debian distribution, you'll add the following content to the Dockerfile
:
# set the base image
FROM debian
FROM
must be the first instruction you use when writing a Dockerfile. Notice that you can also use a specific version of your base image, by appending :
and the version_name
at the end of the image name. For example:
# set the base image
FROM debian:sid
In the code above, we're using the "sid" Debian (unstable distribution). This will be relevant also when you want a specific version of a Ruby or Python interpreter, MySQL version, or what have you, 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's the MAINTAINER
, replacing Lucero del Alba
by your name or the person or team responsible for the build:
# author
MAINTAINER Lucero del Alba
It isn't necessary, but we may also add some metadata using the LABEL
instruction, and this information will become available later when using then docker inspect
command to examine the image:
# extra metadata
LABEL version="1.0"
LABEL description="First image with Dockerfile."
For more on this feature, refer to Docker object labels.
Making your own distro
At this point, we're going to select some tools and libraries to be included 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'll be doing something that's very close to actually building a Linux distribution.
Some containers, such as one running a PostgreSQL database, are meant to run in the background. But often we need a console to perform some operations on the container, so we're likely to need some extra tools, because the base image will bundle just a minimal set of GNU tools.
Dealing with cache issues
It's almost guaranteed that you'll experience cache issues when trying to install additional packages on your image. This is because the base image comes with cached metadata, and the live repositories you're pulling data from are often changing.
So before requesting to install any package, you need to address this issue. In Debian-based distributions, you can handle this by adding the following commands before installing new packages:
# update sources list
RUN apt-get clean
RUN apt-get update
Installing basic tools
Code editors, locales, tools such as git
or tmux
--- this is the time to install everything you're going to need later, so that they're bundled in the image.
We'll install one per line:
# install basic apps, one per line for better caching
RUN apt-get install -qy git
RUN apt-get install -qy locales
RUN apt-get install -qy nano
RUN apt-get install -qy tmux
RUN apt-get install -qy wget
We could install all of them in a single line, but if we later want to add or remove a package, we need to re-run the whole process. So the best practice here is to install one package per line so you can benefit from Docker's caching.
Also, keep it tight. You don't want to install tools "just in case", as this may increase the build time and the image size.
Installing runtime libraries for your app
We'll be shipping our app in this image as well. Do you need a specific version of PHP, Ruby or Python, together with certain modules? Now's the time to deliver all of the programs and runtimes our app is going to need.
Be as specific as you like, as this container is intended to run only your app:
# install app runtimes 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
For this example, we'll install Python 3 with the packages Psycopg 2 (to connect to PostgreSQL databases), the Mustache for Python module, and the YAML module. (You'll naturally install the specific dependencies you need when doing your own.)
Compiling and downloading packages
It's also possible that your distribution won't 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
instruction (one per line) to batch the process of downloading, compiling and setting whichever library your application will need.
You can even write a script on a separate file, add this file to the build and run it, as we'll see later in the "Shipping Your Own App" section.
Cleaning up
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, notice we're using apt-get
because we chose Debian, but use the appropriate command for the distribution of your base image.
Continue reading %How to Build an Image with the Dockerfile%
by Lucero del Alba via SitePoint
No comments:
Post a Comment