How To Build Docker Image [Comprehensive Beginners Guide]

In this article, you will learn how to create a Docker image from scratch and deploy and run your application as a Docker container with Dockerfile

As you know, Docker is a tool for packaging, deploying, and running applications in lightweight containers. If you want to learn about the basics of Docker, check out the Docker Explained blog.

If you don’t have a Docker installation, see the Docker installation guide.

Dockerfile explained

The very basic building block of a Docker image is a Dockerfile A Dockerfile is a

simple text file

with instructions and arguments. Docker can create images automatically by reading the instructions given in a Dockerfile.

In a Dockerfile Everything on the left is INSTRUCT, and on the right is an ARGUMENT for those instructions. Remember that the file name is “Dockerfile” without any extension.

The

following table contains the important Dockerfile instructions and their explanation. Automatic

extraction of tar filescan be overridden ENTRYPOINT

Build Docker Image

with Dockerfile

In this section, you will learn how to create a Docker image using a real-world example. We’ll create a Docker Nginx image from scratch with a custom index page.

Follow the steps below to create a Docker image.

Note: The Dockerfile and configurations used for this article are hosted in a Github repository of Docker image samples. You can clone the repository for reference.

Step 1: Create the necessary files and

folders

Create a folder named nginx-image and create a folder called mkdir files nginx-image && cd

nginx-image

mkdir files Create a .dockerignore tap file.

dockerignore

Step 2: Create a sample

HTML file and configuration file When you create a Docker

image for real-time projects

, it contains code or application configuration files.

For demonstration purposes, we will create a simple HTML file and configuration file as our application code and package it using Docker. This is a simple index.html file. You can create your own if you want.

cd in

the files folder cd files

Create an index .html vi index file

.html

Copy the following contents into the index.html and save it.

<html> <head> <title>Dockerfile</title> </head> <body> <div class=”container”> <h1>My App</h1> <h2>This is my first app</h2> <p>Hi all, This is executed through the Docker</p> </div> </body> </html container>

Create a

default vi default file name

Copy the following contents into the default file.

server { listen 80 default_server; listen [::]:80 default_server; root /usr/share/nginx/html; index index.html index.htm; server_name _; location / { try_files $uri $uri/ =404; } }

Step 3: Choose

a base image

We use the FROM command in the Dockerfile that instructs Docker to create an image based on other images that are available in the Docker hub or any container registry configured with Docker. We call it a base image.

The choice of

a base image depends on our application and operating system platform of choice. In our case, we will choose the base image of ubuntu:18.04.

Note: Always use official base images for your applications to avoid potential vulnerabilities. Towards the end, we have added all public records that have verified container base images. Also, when it comes to production use cases, always use a minimal base image such as alpine (only 5 Mib) or undistributed images. Distroless alpine is only 2 MiB

Step 3:

Create the Dockerfile

Create a Dockerfile in the nginx-image folder

. vi Dockerfile

Here is the simple Dockerfile content for our use case. Add the content to the Dockerfile.

FROM ubuntu:18.04 LABEL maintainer=”[email protected]” RUN apt-get -y update && apt-get -y install nginx COPY files/default /etc/nginx/sites-available/default COPY files/index.html /usr/share/nginx/html/index.html EXPOSE 80 CMD [“/usr/sbin/nginx”, “-g”, “daemon off;”]

Here is the explanation of each step

:

Step 4: Create the first Docker

image

The final folder and file structure would look like the following.

nginx-image ├── Dockerfile └── files ├── default index └──.html

Now, we will build our image using the Docker command. The following command will build the image using Dockerfile from the same directory.

Docker Build -T Nginx:1.0.

  1. -t is for tagging
  2. the image. nginx is the name of the image. 1.0

  3. is the name of the
  4. tag. If you do not add a tag, the default is the tag named latest.
  5. .

  6. means that we refer to the Dockerfile location as the Docker build context.

If the Dockerfile is in another folder, you must specify it explicitly

. docker build -t nginx /path/to/folder

Now, we can list the images using this command

. Pictures of Docker

We can see that the tag is 1.0 here. If we want to put a specific tag we can put it like this image-name:<tag>. If you do not specify a tag, the default value is the most recent tag.

Docker Build -t nginx:2.0.

A single image can have multiple tags. There are two approaches we usually follow to tag the image

:

  1. Stable tags: we can continue to extract a specific tag, which continues to receive updates. Our labels are always constant, but the content of the image changes.
  2. Unique tags – We use a different and unique tag for each image. There are different ways to provide unique tags, for example, date and time stamp, build number, commit ID, etc.

Note: When it comes to production, a recommended method of tagging Docker images is semantic versioning (Semver). Docker

caches build steps. So if we build the image again the process will move a little faster. For example, you will not download the ubuntu 18.04 image again.

Using large images slows down the time to build and deploy containers. If you want to learn more about optimizing Docker images, see the Docker Image Reduction Guide.

Step 5: Test

the Docker image Now, after creating the image,

we will run the Docker image. The command will be

docker run -d -p 9090:80 -name webserver nginx:1.0

Here

, -d flag is to run the container in

  1. separate mode
  2. -p flag for the port number, the

  3. format is local-port:container-port –
  4. name for the container

  5. name, webserver in our case

We can verify the container using the following docker

ps

command Now in the browser, if you go to http://<host-ip>:9090, you can see the index page showing the content in the custom HTML page we added to the Docker image.

Embed Docker image in Docker Hub

To submit our Docker image to Docker Hub, we need to create an account in Docker Hub

.

Post that, run the following command to log in from the terminal. It will ask you for a username and password. Provide the credentials for the Docker hub.

Docker login After logging in, we now need to tag our image with the Docker username as shown below. Docker tag nginx:1.0 <username>/<image-name>:tag

For example, here devopscube is the dockerhub username

. docker tag nginx:1.0 devopscube/nginx:1.0 Run the

docker images command again and verify that the tagged image is there

.

We can now send our images to the Docker hub using the following command.

Docker

push devopscube/nginx :1.0

You can now verify that this image will be available in your Docker Hub account

. Using heredoc With Dockerfile

Dockerfile

also supports heredoc syntax. If we have multiple RUN commands, then we can use the heredoc syntax as shown below.

RUN <<EOF apt-get update apt-get upgrade -y apt-get install -y nginx EOF

Also, suppose you want to run a Python script from a Dockerfile, you can use the following syntax.

RUN python3 <<EOF with open(“/hello”, “w”) as f: print(“Hello”, file=f) print(“World”, file=f) EOF

You can also use heredoc syntax to create a file. Here is an example of Nginx.

DE nginx COPY <<EOF /usr/share/nginx/html/index.html <html> <head> <title>Dockerfile</title> </head> <body> <div class=”container”> <h1>My App</h1> <h2>This is my first app</h2> <p>Hi everyone, This is running through Docker container</p> </div> </body> </html> Dockerfile EOF Best practices Some of the

Dockerfile practices

we should follow:

Use

  1. a .dockerignore file to exclude unnecessary files and directories to increase build performance.
  2. Use only trusted base images and continue to update images periodically
  3. .

  4. Each statement in the Dockerfile adds an additional layer to the Docker image. Minimize the number of layers by consolidating statements to increase performance and build time.
  5. Run as non-root to avoid security breaches.
  6. Keep the image

  7. small: Reduce the image size for faster deployment and avoid installing unnecessary tools on your image. Use minimal images to reduce the attack surface.
  8. Use specific tags over the last tag in the image to avoid major changes over time.
  9. Avoid using multiple RUN commands, as it creates multiple cacheable layers that will affect the efficiency of the build process.
  10. Never share or copy application credentials or any sensitive information to the Dockerfile. If you use it, add it to .dockerignore
  11. Use the

  12. EXPOSE and ENV commands as late as possible in
  13. Dockerfile. Use a linter: Use a

  14. linter like hadolint to check for common issues and best practices in your Dockerfile
  15. . Use only one process

  16. per container: each container must run only one process. This makes it easier to manage and monitor containers, and helps keep containers light.
  17. Use

  18. multi-stage builds: Use multi-stage builds to create smaller, more efficient images.

Potential Docker build issues

If there is a

  1. syntax error or invalid argument in Dockerfile, the docker build command will fail with an error message. Correct the syntax to resolve this.
  2. Always try to give the container name using the docker run command, otherwise Docker automatically assigns a name to the container and can lead to various problems.
  3. Sometimes we get Bind for 0.0.0.0.:8080 failed: port is already assigned error, this is because some other software/service is using these ports. We can check the listening ports using the netstat or ss command. Use a different port to resolve this or stop that service.
  4. Sometimes, Docker could not download the packages with this error Error downloading package <package-name>. This is because the container may not be able to access the Internet or other dependency issues.

Docker Image Logs

As mentioned in step 1, you should always choose verified official base images for your

application.

The following table lists the publicly available container records where you can find base images and officially verified application images.

Docker

image

vs. containers

A Docker image is a snapshot of the file system and application dependencies. It is an executable software package that includes everything needed such as application code, libraries, tools, dependencies, and other files to run an application. You can compare it to a gold VM image.

A Docker image is organized into read-only layers stacked on top of each other.

A Docker container

is a running instance of a Docker image. We create virtual machines from virtual machine images. Similarly, we create a container from a container image. When you create a container from a Docker image, you create a writable layer on top of the existing image layers.

The key difference between a Docker image and a container is the writable layer at the top of the image. This means that if you have five containers running from one image, all containers share the same read-only layers of the image and only the top writable layer is different for all five containers.

This means that when you delete the container, the writable layer is removed.

Images can exist without containers, while a container needs an image to run. We can create multiple containers from the same image, each with its own unique data and status.

Frequently asked questions about building

Docker images

Conclusion

In this article, we discuss how we can create a Docker image and run our application as a Docker container using a Dockerfile. We discussed Dockerfile

in

detail and reviewed some best practices for writing it

.

As a DevOps engineer, it’s important to have a solid understanding of Docker best practices before implementing them into a project. In addition, to learn Kubernetes, you need to understand the workflow of creating container images.

Podman is another container tool with which you can manage containers. To learn more, check out the podman tutorial.