DCA – 05 – Creating Docker Images

Jarret B

Well-Known Member
Staff member
May 22, 2017
Reaction score
There are many images we can use in Docker, but there may be times we need to create our own images. Images that we need to manipulate to suit our needs.

So, we are going to make our own images with an example to help.

Building an Image

Let's start by creating a file to be used to build an image. Let's open a Terminal and in our HOME folder, create a directory named 'Builds' and move into that folder with the commands:

mkdir Builds
cd Builds

Use an editor to create a file named 'Dockerfile' using your favorite editor. Add the following lines:

FROM ubuntu:latest
RUN apt update && apt upgrade -y

Save and exit your editor. The file will create a basic image using the latest version of Ubuntu. After Docker downloads the image, if it doesn’t exist already on your system, Docker runs the command 'apt update && apt upgrade -y' inside the container. We will now have an Ubuntu image that is upgraded with all updates.

NOTE: You may want to remove all images you have previously installed, or keep note of the ones you have installed.

Before building the image, if you run 'docker images', hopefully you do not have an existing image named 'ubuntu:latest'. If you want to, delete it. This way, you can see what happens when you build your new image.

We need to build the new image from this file by issuing the command:

docker build -t ubuntu:v1 .

The command is creating an image named 'ubuntu' with a tag of 'v1'. In the terminal, we are currently in the folder '~/Builds' and the build parameter causes the 'docker' program to look for a file named 'Dockerfile'. So we designate the location as '.'. We can specify the full location and a different file name if we did not use the default file name and are not currently in the same folder as the file.

If you look at your current image list, you should see that there is a new image named 'ubuntu:v1' as well as the base image you used 'ubuntu:latest'.

At the end of the build command, I show the last two lines in Figure 1. It shows the Image ID of the newly created build and the name with the tag.

Figure 1.JPG


Docker stores the build process in cache. If you change a line in the 'Dockerfile' to create another build, then the process will use the cache up to the change made. Once Docker finds a change, then it processes all lines, from that point, again without the cache.

If you want to process all lines without pulling from the cache, then you use the parameter '--no-cache'. You can also have Docker pull the base image from the repository again without using the already downloaded image by specifying the parameter '--pull'.

Extending the Dockerfile

Let's edit the 'Dockerfile' and make some changes. The contents of the file should be:

FROM ubuntu:latest
RUN apt update && apt upgrade -y
RUN apt install webfs -y
RUN mkdir -p /var/www/html
ENTRYPOINT /bin/bash

Save the file and exit the editor.

The third line is installing a Web Service called 'webfs'. You can notice that the fourth line is creating the folder structure 'var/www/html' since it is not created automatically when 'webfs' is installed. The fifth line will expose the port 8000 for the 'webfsd' service. Finally, we start a command prompt when we start the image.

We need to build the new image with the command:

docker build -t ubuntu:v2 .

Once we have built the new image, we can start it as a container with the command:

docker run -d -p 8000:8000 --rm -t ubuntu:v2

Here, the '-d' is for detached mode. We publish the container's port to the host's port, both 8000. The '--rm- removes the container when it has finished.

Now that it is running as a detached container, we can verify it by running 'docker ps -a'. The output should show that the container is running and also show its name, such as 'fervent_brown'. Remember the name to be used later.

We can pass commands to the detached container with the command 'docker exec <name> <command>'. For example, we can start the 'webfsd' service with the command 'docker exec fervent_brown webfsd'. Of course, change the name that is appropriate to your container.

To check the Web Service, we need an IP Address of the container. Use the command 'docker exec fervent_brown cat /etc/hosts' to see the IP Address.

On the host system, open a web browser and type in the address 'http://<ip address>:8000'. An example would be ''. You should see a listing of the root file structure of the Container as shown in Figure 2.

Figure 2.JPG


Now, let's look at improving this a bit more. We had to start the service 'webfsd' after the container started. We can set this up to start automatically. So, let's edit the 'Dockerfile' to be:

FROM ubuntu:latest
RUN apt update && apt upgrade -y
RUN apt install webfs -y
RUN mkdir -p /var/www/html
ENTRYPOINT webfsd && /bin/bash

So now we build the new image with the command 'docker build -t ubuntu:v3 .'. Running 'docker images', we should now see the image 'ubunut:v3'. We can run it and connect to it with a browser like before and not have to start the 'webfs' service like we previously did.

When a container starts, Docker executes any commands in the 'ENTRYPOINT'.

Image Optimization

Each image is a specific size when it is built. Depending on the various 'RUN' commands you use, and others, the size can vary.

I took all the 'RUN' commands that were one line and separated them into multiple lines:

FROM ubuntu:latest
RUN apt update
RUN apt upgrade -y
RUN apt install webfs -y
RUN mkdir -p /var/www/html
ENTRYPOINT webfsd && /bin/bash

There is no '&&' except in the 'ENTRYPOINT', which cannot be split into multiple lines. The result from the command 'docker history ubuntu:v4' is in Figure 3.

Figure 3.JPG


Running the 'docker images' command, it shows that the 'ubuntu:v4' image is 125 MB.

Now I'm going to join as many commands as I can into single lines and get the 'Dockerfile':

FROM ubuntu:latest
RUN apt update && apt upgrade -y && apt install webfs -y
RUN mkdir -p /var/www/html
ENTRYPOINT webfsd && /bin/bash

The resulting size of the new image is 124 MB. This doesn't seem like much, but if you have an extensive build, the resulting size difference could add up and be significant.

To see where the size is coming from, use the 'docker history' command we just used. Look back at Figure 3 and start from the bottom and look at the contents of the 'Dockerfile' that was used for that build.

The bottom line is an 'ADD file' command that was issued. The file that was added was the 'FROM ubuntu:latest'. This is the base image we are using of the Ubuntu OS.

The second line from the bottom is a 'bash' command to open the image to perform the 'RUN' commands in the image.

NOTE: When you build an image, monitor the output as it builds. If one of your 'RUN' commands is mis-typed or you attempt to run a command that isn't present, you'll need to be aware to fix it.

So the last third line to the last sixth line are all the 'RUN' commands that were issued.

The second line shows that command to 'EXPOSE' Port 8000. The first line shows the 'ENTRYPOINT' command.

You can see that any command that brought in files has a size shown for that layer. Each command on the list is a layer of the image. It overlays the layers on each other to get the final image that is used to start a container. The combined size is then 125.14 MB.

If we should combine all the lines like we did for 'ubuntu:v5'. The history command for 'ubuntu:v5' is shown in Figure 4.

Figure 4.JPG

Figure 4

Here, you can see there are fewer layers to the image and the total size is 123.7 MB. Combining layers can help reduce size.

Export/Import Images

If you built an image on one system, you may want to export it to another system. You could easily move the 'Dockerfile' and build the image again, but if we have changed it later, you may not want to go through all that work again.

The 'import' and 'export' commands are used on containers and not images.

To begin, you'll need to start the container that you want to export. In my case, I'll start the image for 'ubuntu:v5' as we normally would. Once the container is running, I'll issue the command:

docker ps -a

This way I can get the current name of the container I want to export. So, in this example, let's say the container name is 'unruffled_lalande'.

You'll also want to run the command 'docker history –no-trunc' on your image, in my case 'docker history –no-trunc ubuntu:v5'. We need the 'ENTRYPOINT' command that starts the container because we lose it in the export/import process. In my case, the part is 'ENTRYPOINT ["/bin/sh" "-c" "webfsd && /bin/bash"]'.

To export the container, we'll issue the command:

docker export unruffled_lalande > ubuntu-v5.tar

We export the container to the present working directory as a file named 'ubuntu-v5.tar'. You can change the filename and location as you need. Now, you can copy the file to another system.

Once we copy the file to another system, you can open the folder in the terminal. We'll assume it's still Linux. We'll issue the command from the folder where the file is located:

docker import -c 'ENTRYPOINT ["/bin/sh", "-c", "webfsd && /bin/bash"]' ubuntu-v5.tar ubuntu:v5

The first parameter is a '-c' for 'change' and the command we took from the history of the original image. Here, it was 'ENTRYPOINT ["/bin/sh" "-c" "webfsd && /bin/bash"]'. You need to add single quotes around the parameter and place commas between the double quotes in the brackets to separate each command.

The second parameter 'ubuntu-v5.tar' is our file that we are importing. The last parameter 'ubuntu:v5' is our name and tag for the file being imported.

If you run 'docker images' you should see the new image you imported and can run it as you would on the other system.

When you run the 'docker images' command, see that the image size has decreased. In my example, it is now 120 MB.

You can see the output of 'docker history ubuntu:v5' in Figure 5. There is no information like there was on the original system since the image is now basically a single layer. Because it is a single layer, is why it has shrunk in size.

Figure 5.JPG


When grabbing the command that used to start the original image from the history command, make sure you get the entire command. If the command is not complete, then the container will not start properly.


We covered more information in this article that will be beneficial to remember for the rest of the articles.

It is a very important thing to make sure you know how to import and export containers. Practice the steps to make sure you understand the process.

Members online

Latest posts