Series on Docker in Practice & Production - Dockerfile to create an image

Series on Docker in Practice & Production - Dockerfile to create an image

Daily short news for you
  • I always emphasize that writing brings a lot of value. For me, it helps relieve stress, organize knowledge, and present information in an easily understandable way. Besides, writing is also a way to think 🤓

    Writing is thinking

    Many things in your head that you think you know well turn out to be not so well understood. If you don’t believe it, just try writing something that you think you know very clearly. Many people believe that there’s no need to write because reading is enough. I completely agree, but it would be even better if you wrote it down. Imagine this process like consuming and then producing content. Consuming without producing means that even you yourself might not know whether you are consuming or not, because production is what proves the effectiveness of this process.

    » Read more
  • Yesterday, the book "Designing Machine Learning Systems" by Huyen Chip just arrived. I had read some of her articles about ML and found them "delicious", so I decided to buy the book since it finally has a Vietnamese version.

    To read it, I’ll write a few lines for everyone, okay? ☺️

    » Read more
  • Here’s another easy-to-understand article guiding about jj everyone 🤓

    Jujutsu For Busy Devs

    » Read more

Image

As mentioned in the previous article, an image is a file used to create a container. It is also the file that needs to be built in order to run your application. To build an image, you use the following command:

docker build -t <image> <path>
  • -t is the flag used to tag the image.
  • path is the path to the directory containing the Dockerfile.

The name of the image usually consists of three parts: registry/name:tag.

Where registry is the resource to pull/push the image, name is the name of the image, and tag is usually a version. If the registry is not specified, Docker will pull from docker hub. If the tag is not specified, Docker will use the default tag "latest".

To pull an image, you use the docker pull command. To push an image, you use the docker push command. To tag an image that already exists in your machine, you use the docker tag command. To view the list of images in your machine, you use the docker image ls command. For example:

# pull nginx image version 2.1
docker pull nginx:2.1

# push nginx to the estacks registry
docker tag nginx:2.1 registry.estacks.icu/nginx:2.1
docker push registry.estacks.icu/nginx:2.1

# view the list of images in your machine
docker image ls

Refer to the docker build, docker image documentation for more commands.

Dockerfile

Most of the images we create are based on another image, usually starting with:

FROM node:12

Applications written or used on any platform usually have base images available on the Docker Hub. For example, for node.js, there is node, and for MySQL, there is mysql...

Docker Hub acts as a registry, and ordinary users can also upload their images to it. There is a way to identify official images (i.e., images provided by the official provider) by the prefix _ in their URLs.

You can also create your own base images if you have a good reason to do so (!?).

In summary, the idea to build an image that can run your app is to base it on a suitable base image for the platform your app is using.

Basic Dockerfile commands

FROM

FROM is the instruction to build an image from a base image. A Dockerfile must start with the FROM command, followed by the name of the image. The image name can be any valid name that Docker can pull from a registry or is available on the host.

For example:

FROM node:12

Refer to FROM for more information.

COPY

COPY is the command used to copy files/folders from the local system to the image. You usually need this command because your source code needs to be copied into the image in order to run it.

COPY <src> <dest>

## Example: copy the directory /User/hoaitx/src/my-app to the /src/my-app directory in the image
COPY /User/hoaitx/src/my-app /src/my-app

dest must be an absolute path, or if WORKDIR is specified, a relative path can be used.

WORKDIR /src

COPY /User/hoaitx/src/my-app my-app

Refer to COPY for more information.

WORKDIR

WORKDIR sets the working directory for any RUN, CMD, ENTRYPOINT, COPY, and ADD commands.

This means that setting WORKDIR to a directory will change the current directory for subsequent commands.

WORKDIR /src

RUN pwd

The result will be /src.

Refer to WORKDIR for more information.

VOLUME

VOLUME is the command to set up a mount point between the Docker host and the container.

When a container is created and runs successfully, any data it creates or stores will be inside the container. When the container is destroyed, all of that data will be lost.

Consider starting a MySQL container. If you accidentally destroy the container, all data will be lost. In this case, you can use VOLUME to map the data folder inside the container to a folder on the local system.

For example, for MySQL, the folder containing the data is /data. Before starting the container, map it to the /User/hoaitx/mysql/data directory on the local system.

VOLUME /User/hoaitx/mysql/data /data

Now, even if you destroy or restart the container, the data will still be safe in that local directory.

Refer to VOLUME for more information.

EXPOSE

EXPOSE is used to document which port the application in your image actually listens on, as opposed to leaving it to the user to guess. It helps in exposing a port when running the container.

# Assuming my-nginx app is configured to run on port 8080
EXPOSE 8080

In this case, I know that I need to map port 8080 to port 8081 on my Docker host:

docker run --name my-nginx -p 8081:8080 my-nginx

Refer to EXPOSE for more information.

ENTRYPOINT

ENTRYPOINT allows you to configure a command that will always be executed when starting a container.

ENTRYPOINT ["executable", "param1", "param2"]

Where executable is a command or a binary, such as bash or node...

For example, to run my Node.js application, I need to execute the node server.js command every time the container is started:

ENTRYPOINT ["node", "server.js"]

Refer to ENTRYPOINT for more information.

CMD

CMD provides default values for the command and parameters to be executed when running a container. In a Dockerfile, if there are multiple CMD instructions, the last one is used.

CMD ["executable", "param1", "param2"]

For example, when starting a container, I need to run the node server.js command to start the server:

CMD ["node", "server.js"]

Refer to CMD for more information.

So, if you have noticed, ENTRYPOINT and CMD seem to be similar in usage. Both are executed every time a container is created. They both have an executable and parameters.
The difference lies in the purpose of each command. "CMD provides default values," which means CMD may not have an executable, in which case ENTRYPOINT is required to specify the executable. On the other hand, ENTRYPOINT always requires an executable. Combining ENTRYPOINT and CMD creates an image that suits your purpose.

For example, to create an image specifically for a particular command, you would use ENTRYPOINT. Conversely, if you want to create a generic image, you can use CMD.

Apart from the basic commands I explained above, there are more commands you can refer to in the documentation.

Let's create our first image

Now, let's apply everything I shared above to analyze what I did in this Dockerfile :D

FROM node:12

ENV NODE_ENV=production

WORKDIR /app

COPY ["package.json", "package-lock.json*", "./"]

RUN npm install --production

COPY . .  

EXPOSE 3000

CMD [ "node", "server.js" ]

Build:

docker build -t registry.estacks.icu/my-node:1.0.0 .  

Run:

docker run -d -p 3000:3000 my-node:1.0.0

Summary

Most of the time, we create images based on another image. Images created with a Dockerfile contain instructions on how to build your application. The name of the image usually consists of three elements: <registry>/<name>:<tag>. If <registry> is not specified, Docker will pull from the Docker Hub by default. If <tag> is not specified, Docker will default to "latest".

Premium
Hello

5 profound lessons

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

View all

Subscribe to receive new article notifications

or
* The summary newsletter is sent every 1-2 weeks, cancel anytime.

Comments (1)

Leave a comment...
Avatar
Phan Tung4 years ago

bài viết rất bổ ích, cảm ơn tác giả

Reply
Avatar
Xuân Hoài Tống4 years ago

Cảm ơn bạn, nhớ ghé thăm blog thường xuyên nhé