Dockerize NodeJS application

Tram Ho

Money Setup

Remember to check that you have installed Docker and Docker-compose already. If not, then check the end of your previous post to know how to install it.

Setup

You clone source code here

Next we open the docker-node folder. Here I have setup for you a NodeJS project with Express quite simple. If you have installed Node in your operating system (the original environment), then you can test and open the browser at localhost: 3000 , you will see the screen only printed a few lines Hello World

And if you have not installed Node, it is okay (too great) we are using Docker. In addition to Docker, you will not need to install anything on your original operating system, keeping the original operating system as “pure” as possible.

Build Docker Image

As in the previous article I mentioned, Image is the most basic unit and is a prerequisite for running a project in Docker. Image represents an environment in which there are full setup of tools and libraries to run the project. From Image we can create a Container (an instance of Image, you can treat Image as a Class and a Container is an Object of that class) .

Configure Dockerfile

And to create Image we will write the configuration for Image in a file called Dockerfile , which will be full of details such as: what environment (ubuntu, win, Centos, debian, …), php few, database, ….

Start working on it.

The problem is in the docker-node folder, all of this article we will only work with this folder. You create a file named Dockerfile with the following content:

Explain the configuration file above:

  • At the top of each Dockerfile file we always have FROM. FROM indicates which environment to start from, this environment must have been built into Image. Here we start from the environment with the image named node:latest , this is the image has been built and officially (official) from the NodeJS team, In this environment we have the latest NodeJS version (latest) , the current is 13.2. How to get the information of Image, I took it from the Docker hub .
  • Next we have the keyword WORKDIR . I mean inside this image, create a folder called app and give me the path to /app . WORKDIR, you can consider it equivalent to the command mkdir / app && cd / app (this path you can set arbitrary, here I choose is app )
  • Next we have the COPY statement and you notice we have two “dots”, which look weird. ? . The line is this: Copy all the code in the original environment, that is, the current docker-node folder inside Image in the / app path . (Perhaps you will wonder if you want to copy only one or a few files, see at the end of the article ? )
  • Next we have the RUN command. RUN to run a certain command when building Docker Image, here we need to run npm install to install dependencies, like many other NodeJS projects. You really pay attention to me is running RUN when BUILD whiff, and because running when building should only be run once during the build, pay attention to this to see the difference with the CMD I said below.
  • Next is the CMD command. CMD to indicate the default command when a container is initialized from this Image. CMD will always be run when a container is initialized from Image ? . CMD receive an array inside the commands you want to run, with 1 space, we write it down. For example: CMD [“npm”, “run”, “dev”] for example
  • Note that a Dockerfile ONLY HAVE 1 CMD. Oh, what if I want more CMD? Read the end of the post later ?

All done, so we have the environment configured to run the project as we want, very clear right, look at what the environment has and what to do. ?

Build Docker Image

Next we have the configuration and now we need to build the image. run the following command to build the image:

Explain the command above:

  • To build Docker image we use command * docker build …
  • The -t option to just name the image is like this for me, and then the name of the image. You may not need this option, but after that build, you will receive a code representing the image, and you will probably forget it after 3 seconds. So my advice is ALWAYS using this -t option. In the name, you can leave it as you like. Here I take it as learning-docker / node and assign it a tag of v1 , I mean this is version number 1. If we don’t assign a tag, the default will be set to the latest .
  • Finally I have a “dot” that means Docker please build Image with context (context) in this current folder for me. And Docker will look in the current folder Dockerfile and build.

Run the command and see what happens:

Docker build image

When running the command you will see that Docker will browse each command line in the Dockerfile file and run through them, each command is 1 Step , in Step 1 Docker will find Image named node: latest on your computer, because it should not Docker will find itself in a public Registry (Registry is where the Docker image is stored, you can imagine it as Github saving code, but this is saving Image). Docker will then download this Image and build, detailed step by step you can clearly see.

Ok so that has finished building Image. To check the list of images available on your device. Please run the following command ? :

To delete an image, use the command:

So Image has finished building. And often when this step is completed, it is possible to breathe a sigh of relief because this can be considered as the most difficult step (of course, there is nothing in this article so it is not difficult ? )

Run the project from Docker Image

Next is the moment of truth, we will run the project from the Image we have just built.

To run this project we will proceed to create a container from the newly built Image offline. In this article, only one container is enough, in the following examples you will see an actual project will need more than 1 container and more than 1 image. ?

As in the previous article I said, when running (production) often we will use docker-compose to run, not run directly with the docker run... command docker run... because using docker-compose allows us to run at the same time many containers, monitor (control) easier, more intuitive management of containers.

To run the project with docker-compose , create a new file named docker-compose.yml , still in the docker-node folder, with the following content:

Explain:

  • First, we define the version (version) of the docker-compose configuration file, the advice is always to choose the latest version. You can watch it here
  • Next we have services , inside services we will define all the components needed for your project, here we have only one service named app with image being the image name we have just built.
  • In the service app, we have the restart field here, we leave it unless-stopped , which means that it automatically runs this service in all cases (such as at restart of the computer), but if this service is stopped manually (stop) (intentionally), or if the error is stopped, do not restart it. Because of the high probability that when we stop on purpose, we want to do something, or when we encounter an error, we also want to find out what the error is before restarting. ?

Finally, run the project. You use the following command:

Notice the terminal will see the following: docker compose Explain:

  • First, a default network will be created, and all services will be joined to the same network, and only services on the network will communicate with each other. The network name is taken by default according to the directory name, generally you should care about this part, I will say the following article.
  • Next a container named docker-node_app_1 is created from our Image. The container name is automatically chosen so it won’t be duplicated. You can change this default name, depending on your needs.
  • Finally, when the container is run, the code CMD … at the end of the Dockerfile file that we mentioned in the above section will be run (please review the Dockerfile file offline).

It’s alright, try opening the browser ….. BUOM ? , see nothing ????

Not found

As in the previous article on the concepts to master , I talked about the Mapping port . Because the container that runs in Docker is independent from the outside world (meaning the native environment / operating system) ? . When the user opens the browser, type localhost: 3000 as the user calling the 3000 port in the original operating system, rather than the port of the container. So we need to map the port, ie use one port in the original operating system “reference” to the corresponding port in the container.

You open the file docker-compose.yml and correct the following:

Above I have added 1 attribute is ports so we map the ports (ports) needed. Here we only map a pair of port 3000 (in the native environment) to port 3000 in the container. Note on the left is the root environment (outside), the right is the port of the container. Here the port in the original environment we can choose arbitrary, but the port of the container must be 3000, our NodeJS project runs in the container at port 3000. So you can change as: “3001: 3000” or “5000: 3000” , whatever you want, but usually I’ll keep the same port.

After fixing, we go back to the terminal, CTRL + C to terminate docker-compose and run the following command:

And finally open the browser:

Docker

Successful satisfaction ?? .

So you guys have successfully dockerize the NodeJS project. You can see that to run the NodeJS project you do not need to install NodeJS. Try to see the folder docker-node you do not see node_modules. Because the real thing is that we are running in a container, the docker-node folder is in the original operating system, which is no longer relevant after we successfully build Image. ??

Dabble

Inside Container

To see what the container actually looks like, open another terminal and run the following command:

Notice above the app that the name of the service / container we want to see.

After running the above command, you have entered the world of Docker already ? . try typing pwd to check what the current path is, then we see that the / app is the WORKDIR that we set up in Dockerfile

Try typing ls -l to list files in the current path, you will see the following:

Docker container

This is really what is run, and what returns the results we see in the browser ?

Next we try to check how much the current version of NodeJS is running. You type the following command:

You will see the printout as follows: Docker node

Note: Notice when we wrote the configuration for the Dockerfile file, the first part we got FROM …. I said I took that Image in the Docker hub , and the image we chose had the latest tag. Go to that link, search for the latest and click on and you’ll be taken to this page . There, you see a Dockerfile file, and this Dockerfile file will be used to build us an environment called node: latest so we can use it later. You read in that Dockerfile file will see there is NODE_VERSION variable only NodeJS version, and there are many other things you can see for more info ?

You may not be wondering : what operating system is this environment in? oh from the beginning, from the time we built Image we haven’t talked about this, don’t know if Ubuntu, Centos, Win or whatever ?

To check the operating system information in the container, run the following command:

We see the following:

Docker container

It turned out that the container was being run under the Debian operating system environment. WTF Debian, what is it and where did it come from?

Linux distributions and how to choose Image properly

This part may sound irrelevant, but it’s necessary for you to understand and choose the right image, so try to use more brains. ??

Linux distributions

Linus Torvalds is the father of Linux, who writes the operating system according to him as the world’s most popular now, almost every server uses it. And from Linux, individual organizations take the kernel, the core of Linux and write it down into new forms of “theer variables” with their own advantages, each of which is called a distribution (Distribution). We have distributions like: Ubuntu, Centos, Alpine, Redhat, Debian, Kali, …

Again from each distribution example, Debian has smaller entities such as Debian-Jessie, Debian-Stretch, Debian-Buster, etc.These smaller entities can be considered versions of the version. distributing Debian, instead of calling 1.0, 2.0, they use names to make it easier to remember.

Back to the Docker hub site where I got the Image NodeJS, you can see there are thousands of pre-built NodeJS Image, there is the version of Node + 1 more tag behind, the back tag is for operating system environment we want.

Choose the appropriate distribution for Image

So now you guys are wondering which version to choose, I see which is Jessie and stretch is alpine , buster , … everything is chaotic.

It depends on your needs that we choose an appropriate distribution. But the criteria should be the lightest , the best (don’t need a big distribution, lots of functionality while we only run a tiny project), and security is good.

And my advice is that you choose the Alpine distribution. It is a lightweight, minimalistic Linux distribution and focused on high security ? ), let’s see a comparison of their sizes ? :

Docker image

Using alpine helps us reduce the size when building images, the build will be much faster there ?

Where familiar that

Although the distribution of Linux, the kernel (kernel) of them are Linux so most of the commands, how to operate the same, but there will be differences and when we use which distribution will we need to get used to them.

As above, I recommend that you use Alpine , in alpine to install the package / library, we use apk-add , not apt-get like on Ubuntu anymore.

Along with that are the other differences that we need to pay attention, and when it comes to Google search. When I first started using Alpine, the solution was completely used by Ubuntu, and then I installed the command not found error message ?

Review and rebuild Image

In this section, we will review the Image we just built in this article, and optimize, reduce the image size by selecting the appropriate distribution.

Review

As in the section on Vạch Vạch check the operating system information we see:

Debian Docker

Look at this and I see this is a Debian distribution of Linux, the version name is stretch

Next we will check how much our Image size is. You run the following command:

We will see the following:

Docker image

WTFFFFF, 936MB , really, I didn’t do anything, the project has every Hello World page but the size is closer to 1GB, try to ask later on the project increase, how much does the size of the project increase?

Image rebuild

As above I have advice and from now on in all the following articles we will use the Alpine distribution for every Image we build, and Alpine will also be used to run Production (run real) always Please.

Let’s get started. We return to our Dockfile file. In the first line is where we should be concerned:

Above the only change is the first line FROM . Here we use the environment of the Alpine distribution, which comes preinstalled with NodeJS 13.

I recommend that you always state the version of nodejs (or later php), not the latest one . Because NodeJS will be continuously developed and updated, if we just leave the latest one year, that latest may be already version 20.0, and many of the functions / functions of NodeJS that we use are no longer working. . So for 10 years after our code is still running, we should always specify the version of Node, PHP, python, … that we need to use. ?

Next we rebuild the image. You run the following command:

above, we rebuilt Image set the tag to v2 so we can look more visually and compare the size with the v1 tag we built earlier.

After building Image, we try to run the project again to make sure everything is okay. Have you revised the fie docker-compose.yml part of the Image name modified to v2. Then we run the following command to restart the project:

Try opening the browser again and you will see everything is still ok ?

Moment of truth

Next we will compare whether the image I just built is v2 is much smaller than v1 before. You run the following command:

And………….

Docker image

Ouch chuiiiiiii, only 113MB but we still have a project running as before, the time to build image is also faster.

Please check to see if the current distribution is truly Alpine. Run the following command:

And we will see print screen information about the operating system environment inside the Container.

Tip: If you notice, to run the command in the container we have 2 ways: 1 is to “get in” completely inside the container with the command “docker-compose exec app sh”, 2 is we outside and run the command “docker-compose” exec app <do something> “as above. You can use any method ?

Does Alpine always bring happiness?

My advice to using Alpine to minimize Image will apply to most cases. But sometimes there will be some cases of bananas, but using Alpine we can not install some libraries because they have not supported such libraries (I have failed to install some python libraries for audio processing) , then you will still need to use distributions like Ubuntu or Debian to be able to run the project.

And so, we have to accept that the project size is very big, while the project we actually do is not too big ? . But I have to accept my friends and only a very few cases (as I think) only.

Related questions

Throughout this article, there are many places I think you may have questions and also “key” for you to read in this section, and below are those questions.

If I want to only COPY one or several files when building Image

Above when building Image I have used COPY. . Just copy the entire current directory into the container. But most likely you will just want to copy one or several files. Then we do the following:

I searched Google and found people using ADD to copy files

COPY and ADD in Docker serve the same purpose of copying files from somewhere into Image.

COPY accepts the object to be copied and the destination to be copied in the image. And COPY only allows us to copy files from local, from our root machine into Image

ADD does the same thing but it has two additional functions:

  • We can copy from a URL into Image
  • We can also extract a file and copy it into Image

In most cases when we use the URL we want to download the file, we will use the command RUN curl / wget …. to download the file or if we want to extract the file, we will also use RUN tar -xvzf .. . to decompress

So my advice is to always use COPY to make it clear which action you want to take ?

If I don’t have a CMD at the end of the Dockerfile file?

When a container is launched from an Image, Docker requires that the state of the container then be running, that is, it must be running a program.

In our example, if we remove the last line CMD, try to build the image, still OK, but when we start the project with the command docker-compose up immediately we will see the line:

Docker

So at the end of the Dockerfile file after setting up the environment, we ALWAYS have to run something so that when the container starts it is always “working”. ?

This is also the reason why many people encounter container exit errors when using PM2 to run NodeJS project with docker, I will explain more in the next articles. ?

Each Dockerfile file only allows 1 CMD, so what if I want to have multiple CMD?

To be able to run multiple commands, you can not write CMD constantly in the Dockerflie file, or when your CMD is extremely large and long, then in Dockerfile should not, but we need to use ENTRYPOINT

ENTRYPOINT, literally translated as “starting point”, now there is no CMD to indicate the default command will run when the container starts up again, but we will use ENTRYPOINT to tell the container that: your starting point is here ?

ENTRYPOINT accepts an array of commands we want to run or a shell script file .sh (usually a shell script file, separated to make our code look neat)

This part depends on the situation and also needs more skills, so I’ll share later, if you want to be able to search google for how to use it right away ?

In Dockerfile can have both ENTRYPOINT and CMD

Can I FROM multiple times?

The answer is yes, you can FROM many times from many environments. Often used in cases where we divide the build Image process into stages, each stage we need a different environment. This part when you demo with VueJS you will see ?

I want to run docker-compose in the background?

To start the project with docker-compose in the background, do not hang the terminal anymore, use the following command:

“Oh, running in the background I can’t see the log ???”

To check the log while the project is running you can use the following command:

Double closing lines

Phew … paternal father often spoke, the first ad was too long ?

Although at the beginning of the article I said starting with the NodeJS project to reduce the concept for you, but by the end, there were 1 billion things that caught my eye. ? . Actually these are introductory concepts, and in this article I just started so I want to clarify. Later when you get used to it, you will find that dockerize a NodeJS project will be quite simple (dockerize the new Laravel project is really challenging.) ? )

This article is quite important for me because it is the first step when introducing you to Docker and related questions.

There are places in the article that define how I simplified the sentences from my perspective so that you guys will not find it difficult to understand, but it may still be difficult to understand, please let me know. Leave me a comment.

Thank you for watching. Be sure to read your next post ?

Source code for this article I put here (branch complete tutorial )

.

.

.

THIS PROJECT IS EASY, I WANT TO SEE THE PROJECT NODEJS WITH MONGDB, REDIS, … so it will be practical. Let’s keep going, guys, I will try to share as much as I can ?

Share the news now

Source : Viblo