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

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

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".

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

Hello, my name is Hoai - a developer who tells stories through writing ✍️ and creating products 🚀. With many years of programming experience, I have contributed to various products that bring value to users at my workplace as well as to myself. My hobbies include reading, writing, and researching... I created this blog with the mission of delivering quality articles to the readers of 2coffee.dev.Follow me through these channels LinkedIn, Facebook, Instagram, Telegram.

Did you find this article helpful?
NoYes

Comments (1)

Avatar
Phan Tung3 years ago
bài viết rất bổ ích, cảm ơn tác giả
Reply
Avatar
Xuân Hoài Tống3 years ago
Cảm ơn bạn, nhớ ghé thăm blog thường xuyên nhé