You now know how to use the Docker command interface and pull images from the Docker Hub. But how do you create your own image?
In this chapter, we’re going to create a Docker image together, in which we’ll install Node.js as well as the different dependencies for our project.
We’ll start by creating a file named “Dockerfile.” In this file, you’ll find the whole recipe describing the Docker image that you need for your project.
Every instruction that we give in our Dockerfile will create a new layer corresponding to each stage in the image build or the recipe.
If we stick with the cooking analogy, Dockerfile gives us our recipe for making a layer cake. So each argument creates a new "layer" for our cake. Our goal is to keep the number of layers to a minimum so that our cake is as lightweight and high-performing as possible.
Create the Instructions in Your Dockerfile
The first thing you need to do is create a file named “Dockerfile,” and then within this file, define the image that you’re going to use as a base, using the FROM
instruction. For our example, we’re going to use a Debian 11 base image.
FROM debian:11
FROM
is an instruction that can only be used once in a Dockerfile.
After that, use the RUN
instruction to run a command in your container.
RUN apt-get update -yq \
&& apt-get install curl gnupg -yq \
&& curl -sL https://deb.nodesource.com/setup_10.x | bash \
&& apt-get install nodejs npm -yq \
&& apt-get clean -y
Then use the ADD
instruction to copy or download files into the image. For our example, we’ll use it to add the sources of our local application in the /app/
image folder.
ADD . /app/
Next, we’ll use the WORKDIR
instruction to modify the current directory. This command is equivalent to a cd
in the command line. All of the following commands will be executed from the defined directory.
WORKDIR /app
Then, the next RUN
instruction will install the Node.js project package.
RUN npm install
Now that you have the source code and dependencies present in your container, we need to give our image some final pieces of information.
EXPOSE 2368
VOLUME /app/logs
The EXPOSE
instruction describes which port your app is listening on. The VOLUME
instruction describes which directory you want to share with your host.
Finally, we have an instruction that should always be present, which we’ll place on the final line to make it as clear as possible: CMD
. This lets our container know which command it should run once it starts up.
CMD npm run start
So that leaves us with the following for our complete Dockerfile:
FROM debian:11
RUN apt-get update -yq \
&& apt-get install curl gnupg -yq \
&& curl -sL https://deb.nodesource.com/setup_10.x | bash \
&& apt-get install nodejs npm -yq \
&& apt-get clean -y
ADD . /app/
WORKDIR /app
RUN npm install
EXPOSE 2368
VOLUME /app/logs
CMD npm run start
Create Your .dockerignore File
Our Dockerfile is now in working order! However, there are still a few adjustments we need to make.
On Git projects, we use the .gitignore
file. On Docker, it’s a similar thing. This prevents the copying of certain files and/or folders into our container when executing the ADD
instruction.
At your project root (so next to your Dockerfile), create a .dockerignore
file that contains the following lines:
node_modules
.git
Enjoy Optimized Docker
One final thing: when you execute the docker build
command, Docker will create a container for each instruction, and the result will be saved in a layer. The end result is a collection of layers that create a complete Docker image.
There are various advantages to this. If a layer doesn’t move between two builds, Docker will not rebuild it. Only layers located after a layer that is rebuilt will be rebuilt in turn.
This means you can create new images very quickly, without having to wait a long time for your image build.
In our example, if we add a dependency in the package.json
file, and then re-run our image build, you’ll see that it’s only the layers located after the ADD package.json /app/
that are rebuilt. The Node.js installation remains in the cache.
Run Your Personalized Container!
You’re now ready to create your first Docker image!
docker build -t ocr-docker-build .
The -t
argument is for naming your Docker image. This makes it easier to find your image later down the line.
The .
is the directory where the Dockerfile is located. In our example, it’s at our project root.
You can now run your container with the docker run
command:
docker run -d -p 2368:2368 ocr-docker-build
In the logs
folder, you’ll find your application logs. Access this folder on port 2368
, via the URL http://127.0.0.1:2368.
Let’s Recap!
You now know how to use the following instructions to create a Docker image:
FROM
for setting the source imageRUN
for running commands in your containerADD
for adding files to your containerWORKDIR
for defining your working directoryEXPOSE
for defining the default listening portsVOLUME
for defining usable volumesCMD
for defining the default command when executing your Docker containers
In the next chapter, we’ll learn how to use images with sharing on Docker Hub.