Creating images with a Dockerfile
This recipe explores image creation with Dockerfiles. Docker images can be created in multiple ways, which includes using Dockerfiles, using docker commit
to save the container state as a new image, or using docker import
, which imports chroot directory structure as a Docker image.
In this recipe, we will focus on Dockerfiles and related details. Dockerfiles help in automating identical and repeatable image creation. They contain multiple commands in the form of instructions to build a new image. These instructions are then passed to the Docker daemon through the docker build
command. The Docker daemon independently executes these commands one by one. The resulting images are committed as and when necessary, and it is possible that multiple intermediate images are created. The build process will reuse existing images from the image cache to speed up build process.
Getting ready
Make sure that your Docker daemon is installed and working properly.
How to do it…
- First, create a new empty directory and enter it. This directory will hold our Dockerfile:
$ mkdir myimage $ cd myimage
- Create a new file called
Dockerfile
:$ touch Dockerfile
- Now, add the following lines to the newly created file. These lines are the instructions to create an image with the Apache web server. We will look at more details later in this recipe:
FROM ubuntu:trusty MAINTAINER ubuntu server cookbook # Install base packages RUN apt-get update && apt-get -yq install apache2 && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf ENV APACHE_RUN_USER www-data ENV APACHE_RUN_GROUP www-data ENV APACHE_LOG_DIR /var/log/apache2 ENV APACHE_PID_FILE /var/run/apache2.pid ENV APACHE_LOCK_DIR /var/www/html VOLUME ["/var/www/html"] EXPOSE 80 CMD ["/usr/sbin/apache2", "-D", "FOREGROUND"]
- Save the changes and start the
docker build
process with the following command:$ docker build.
This will build a new image with Apache server installed on it. The build process will take a little longer to complete and output the final image ID:
- Once the image is ready, you can start a new container with it:
$ docker run -p 80:80 -d image_id
Replace
image_id
with the image ID from the result of the build process. - Now, you can list the running containers with the
docker ps
command. Notice theports
column of the output:$ docker ps
Apache server's default page should be accessible at your host domain name or IP address.
How it works…
A Dockerfile is a document that contains several commands to create a new image. Each command in a Dockerfile creates a new container, executes that command on the new container, and then commits the changes to create a new image. This image is then used as a base for executing the next command. Once the final command is executed, Docker returns the ID of the final image as an output of the docker build
command.
This recipe demonstrates the use of a Dockerfile to create images with the Apache web server. The Dockerfile uses a few available instructions. As a convention, the instructions file is generally called Dockerfile. Alternatively, you can use the -f
flag to pass the instruction file to the Docker daemon. A Dockerfile uses the following format for instructions:
# comment INSTRUCTION argument
All instructions are executed one by one in a given order. A Dockerfile must start with the FROM
instruction, which specifies the base image to be used. We have started our Dockerfile with Ubuntu:trusty as the base image. The next line specifies the maintainer or the author of the Dockerfile, with the MAINTAINER
instruction.
Followed by the author definition, we have used the RUN
instruction to install Apache on our base image. The RUN
instruction will execute a given command on the top read-write layer and then commit the results. The committed image will be used as a starting point for the next instruction. If you've noticed the RUN
instruction and the arguments passed to it, you can see that we have passed multiple commands in a chained format. This will execute all commands on a single image and avoid any cache-related problems. The apt-get clean
and rm
commands are used to remove any unused files and minimize the resulting image size.
After the RUN
command, we have set some environment variables with the ENV
instruction. When we start a new container from this image, all environment variables are exported to the container environment and will be accessible to processes running inside the container. In this case, the process that will use such a variable is the Apache server.
Next, we have used the VOLUME
instruction with the path set to /var/www/html
. This instruction creates a directory on the host system, generally under Docker root, and mounts it inside the container on the specified path. Docker uses volumes to decouple containers from the data they create. So even if the container using this volume is removed, the data will persist on the host system. You can specify volumes in a Dockerfile or in the command line while running the container, as follows:
$ docker run -v /var/www/html image_id
You can use docker inspect
to get the host path of the volumes attached to container.
Finally, we have used the EXPOSE
instruction, which will expose the specified container port to the host. In this case, it's port 80
, where the Apache server will be listening for web requests. To use an exposed port on the host system, we need to use either the -p
flag to explicitly specify the port mapping or the -P
flag, which will dynamically map the container port to the available host port. We have used the -p
flag with the argument 80:80
, which will map the container port 80
to the host port 80
and make Apache accessible through the host.
The last instruction, CMD
, sets the command to be executed when running the image. We are using the executable format of the CMD
instruction, which specifies the executable to be run with its command-line arguments. In this case, our executable is the Apache binary with -D FOREGROUND
as an argument. By default, the Apache parent process will start, create a child process, and then exit. If the Apache process exits, our container will be turned off as it no longer has a running process. With the -D FOREGROUND
argument, we instruct Apache to run in the foreground and keep the parent process active. We can have only one CMD
instruction in a Dockerfile.
The instruction set includes some more instructions, such as ADD
, COPY
, and ENTRYPOINT
. I cannot cover them all because it would run into far too many pages. You can always refer to the official Docker site to get more details. Check out the reference URLs in the See also section.
There's more…
Once the image has been created, you can share it on Docker Hub, a central repository of public and private Docker images. You need an account on Docker Hub, which can be created for free. Once you get your Docker Hub credentials, you can use docker login
to connect your Docker daemon with Docker Hub and then use docker push
to push local images to the Docker Hub repository. You can use the respective help
commands or manual pages to get more details about docker login
and docker push
.
Alternatively, you can also set up your own local image repository. Check out the Docker documents for deploying your own registry at https://docs.docker.com/registry/deploying/.
Note
GitLab, an open source Git hosting server, now supports container repositories. This feature has been added in GitLab version 8.8. Refer to Chapter 11, Git Hosting, for more details and installation instructions for GitLab.
We need a base image or any other image as a starting point for the Dockerfile. But how do we create our own base image?
Base images can be created with tools such as debootstrap and supermin. We need to create a distribution-specific directory structure and put all the necessary files inside it. Later, we can create a tarball of this directory structure and import the tarball as a Docker image using the docker import
command.
See also
- Dockerfile reference: https://docs.docker.com/reference/builder/
- Dockerfile best practices: https://docs.docker.com/articles/dockerfile_best-practices
- More Dockerfile best practices: http://crosbymichael.com/dockerfile-best-practices.html">
- Create a base image: http://docs.docker.com/engine/articles/baseimages/