Dockerfile Step by Step: Creating custom Containers
What is a Dockerfile?
As mentioned in a previous article of this series, a Docker image is the blueprint of your docker container. There are many different types of Docker images, but sometimes there is no perfect fit for your particular use case and you might want to create our own. To create a Docker image, we need a Dockerfile, which is a simple text file containing a step by step list of instructions that can be used for the setup of your container. One of the fantastic features of Docker is that we can also leverage other user’s Docker images when creating our own.
Lets use an abstract example – say you want to create a docker image for your favorite database using a Dockerfile (Note: there is already a Docker image on DockerHub for most databases). You will probably want to use an operating system such as Ubuntu. Instead of having to write your Dockerfile to include all the commands for a full installation and configuration of Ubuntu, you can simply use an Ubuntu image that does all of this for you. We would be using this Ubuntu Docker image as the “base” for our new image. In your Dockerfile, you will then only need to include the linux commands that will install and run your database on the system.
Creating and Building a custom Image using a Dockerfile
As mentioned previously, to create our own images, we will want to create and write our own Dockerfile. To create one, simply generate a new file with the name “Dockerfile” (no extension is required).
Unlike third party Docker images from DockerHub, which we can simply download and run right away, we need to build our Dockerfile into a Docker image every time we make a change to it. However, to do this, we need to add lines of instructions to the file.
Using a base Image
For this example, I am going to leverage the official Ubuntu image as the base for my Dockerfile, because I want to setup a linux based application server. Make sure your base image definition is declared at the top of your Dockerfile, using the FROM instruction.
Executing a Command in the Container
We are now going to look at executing a linux command using a Dockerfile. Commands that you want to execute within the container, during the build process, are preceded with the RUN instruction in the Dockerfile. The CMD instruction is just like the RUN instruction, except it is used to execute commands after the build process is finished. This is usually what you want to do to start jobs or software that you want to be running for as long as the container is active i.e. starting your application server so it can handle requests. Let’s extend our Dockerfile as follows:
FROM ubuntu:18.04 RUN echo 'Hello World' CMD echo 'Hello World'
Building and Running the Container
Now, we can also build our Docker container using
docker build -t my-image .. After entering the command, you should see our Ubuntu image being pulled from DockerHub (if it isn’t on your machine already) and notice how all our instructions are listed in the terminal. The “-t” flag is used to give our image a name (in this case, I am giving it the name “my-image”). To check that it built correctly, we want to list all the images on our machine using the command
docker images. To run our container with the new image, use
docker run my-image You should now get something printed to your command line: “Hello World”. But hold on – didn’t we output that twice? Remember: the RUN instruction commands are executed at build time, so they are not output to the console. This is also why we use RUN instructions before CMD in our Dockerfile.
Copying Files into a Container
The most common reason to create your own image with a Dockerfile, is because you need to use some of your own code or binaries to host an application. Docker has two instructions that can be used for this, ADD and COPY. The difference between the two is really minimal. ADD is able to take additional parameters that allows you to extract files as you copy them from your local directory (from a tar or zip file), while COPY does not. For this reason, it is convention to use COPY whenever all you are doing is copying files into your container. I decided to create a really simple text file that I want to copy over and then output the contents of the directory to my container. My directory currently looks like this:
The COPY instruction requires two parameters. The first is the file path to the file you want to copy, the other is the directory that you want to copy the file to.
FROM ubuntu:18.04 COPY ./helloworld.txt . CMD ls
We need to rebuild our image and run it, with the exact same commands we used previously. Here is the output:
Now we can see that besides our helloworld.txt, we get the root linux folder contents listed in the console. This means it worked! Right now, it seems that the default working directory for our container (or more specifically, the ubuntu:18.04 docker image) is the root linux folder!
Changing the Docker working directory
Changing the docker working directory is as simple as using the WORKDIR instruction.
FROM ubuntu:18.04 WORKDIR /usr/ COPY ./helloworld.txt . CMD ls
I now get the following output:
I hope you enjoyed this introduction to Dockerfiles! There is still a lot more to learn, but this will be a great start I believe. There are several key Docker features and their corresponding instructions that I have not mentioned here, specifically EXPOSE and VOLUME. The reason for this is that these are usually defined using the parameters from the command line, which I covered in my previous Docker tutorial. For further study and a practical example on using a Dockerfile to setup an nginx web server, I recommend the following slightly more advanced tutorial.