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>
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.
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.
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 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 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 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 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 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 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.
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
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".
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!
Subscribe to receive new article notifications
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.
Comments (1)