# Dockerize Your App: An Introduction to Docker

> You can find the Chinese version at [Docker 實戰系列：一步一步帶你 dockerize 你的應用](https://larrylu.blog/step-by-step-dockerize-your-app-ecd8940696f4).

Imagine this scenario, you have finished developing your application and are ready to deploy it to the remote machine. However, you find the **server's distribution** is not Ubuntu or CentOS, which you are most familiar with. Moreover, to run the application, you have to first install **system dependencies** such as Node.js and MongoDB manually. This can be quite a hassle! Now is the perfect moment to use **Docker**.

Docker can help you pack the Ubuntu + Node14 + MongoDB environment with your JS code into a **single image**. <mark>This way, you can effortlessly </mark> **<mark>run the entire image</mark>** <mark>on the remote server without any concerns about environmental issues.</mark>

## What is Docker

Docker is a **lightweight virtualization** technology that can easily package your application and its environment into an image so that you don't have to worry about environmental issues anymore.

The following example demonstrates using Docker to run three separate applications in different containers. Each container provides an **independent** environment, which means that they can have different systems, databases, compilers, etc. For example, **you can run Node 8 in one container and Node 14 in another.**

![Docker use case](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/80o5934vwpsnlg1lkt71.png align="left")

## Let's try using Docker

### Install Docker

Docker can be run on Linux, macOS, and Windows. You can go to [the "Get Docker" page](https://docs.docker.com/get-docker/) and get the version compatible with your OS. After installing Docker, you can run the command `docker run hello-world` to verify Docker is properly installed.

![Docker helloworld](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wu2adgr3t7mc7d7egujf.png align="left")

### Create a Ubuntu environment using Docker

Docker makes it easy to get the desired environment you want. For example, if you need a Ubuntu Bash environment at the moment, you can first obtain the [Ubuntu image](https://hub.docker.com/_/ubuntu) from Docker Hub.

```sh
docker pull ubuntu
docker image ls
```

![ubuntu image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6sywmpkh544kasu16pq9.png align="left")

After preparing the Ubuntu image, you can run a Bash process in a Ubuntu container. The `-it` option enables **interactive mode** in the container, which allows us to execute commands in Bash.

```sh
docker run -it ubuntu bash
```

After getting into the container, you can run the command `cat /etc/*release` so you can see the Ubuntu version, which is 22.04.2 LTS on my side.

![ubuntu version](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7y0yv75ud1veub8ty4xw.png align="left")

> You may be confused **Image** with **Container**. You can think of an Image as the code. They are simply files stored on disk and not currently running. A Container, on the other hand, is a running instance of an Image. Containers use your CPU and memory resources to execute the code contained in the Image.

In summary, Docker allows you to get the desired environment. Next, I will tell you how to package your application with the environment you want.

## How to Dockerize an existing application

**Dockerize** means to package an application and environment into a Docker image. Once Docker is installed on the target machine, you can simply run the image to deploy the application.

In this post, I'll demonstrate how to dockerize a Node.js application called [simple-express-server](https://github.com/LarryLuTW/simple-express-server). The application is an HTTP server that listens on port 8080 and responds with "Hello World!" when accessed.

### Step 0 - Creating a Dockerfile

In order to package the code and environment together into a single image, we need to write down all the packaging steps in **Dockerfile**. So, let's create an empty **Dockerfile** first.

```plaintext
simple-express-server
├── Dockerfile  <--  HERE
├── README.md
├── index.js
├── node_modules
└── package.json
```

### Step 1 - Find a suitable base image

Docker image is built layer by layer, so we need to find a suitable Base Image on Docker Hub, and then process it into the image we want.

![image layers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nbb3ay4fapxz4moqso9t.png align="left")

<mark>Since the project is written in Node.js, we need to find a Node.js environment on Docker Hub.</mark> We can find the [official Node image](https://hub.docker.com/_/node) on Docker Hub by searching for "Node.js".

If you want to dockerize an application written in other languages, you can easily find the corresponding image for [Python](https://hub.docker.com/_/python/), [Golang](https://hub.docker.com/_/golang/), etc.

After deciding on the base image, we have to select a version for it. In this case, we're using `node:18.15`. If you prefer to use the latest version, you can specify `node:latest`.

```dockerfile
# Dockerfile
FROM node:18.15
```

### Step 2 - Copy the source code into the image

After setting up the Node.js environment, the next step is to copy all the required source code into the image. To do this, we'll use the `COPY` command to copy `index.js` and `package.json` into the `/app` directory within the container.

```dockerfile
FROM node:18.15
COPY index.js package.json /app/
```

### Step 3 - Install the dependencies

After we have the environment and source code in the image, we need to cd to the `/app` directory and install the dependencies.

First, we use `WORKDIR` command to cd to `/app`, and then `RUN` the command `npm install && npm cache clean --force`. The purpose of cleaning the cache is to minimize the size of the resulting image.

If you're writing other languages, you can replace `npm install` with the corresponding command, such as `pip install`, to install the dependencies.

```dockerfile
WORKDIR /app
RUN npm install && npm cache clean --force
```

### Step 4 - Specify the initial command

After preparing the environment, source code, and dependencies, the final step is to execute the program. To do this, we use the `CMD` to specify the **initial command**. Because this Node.js application requires `node index.js` to start, we'll write down this command to the Dockerfile.

```dockerfile
FROM node:18.15
COPY index.js package.json /app/
WORKDIR /app
RUN npm install && npm cache clean --force
CMD node index.js
```

### Step 5 - Build the image

After completing the Dockerfile, we can use the command `docker build -t simple-express-server .` to build the image from the Dockerfile.

![build the image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6b9c9cmzcpym0t2h3p5l.png align="left")

Once the building process is complete, you can run `docker image ls` to view your image named `simple-express-server`. **The image contains the Node.js environment, application code, dependencies, and the init command.**

## Run the image on your laptop

Once the image is prepared, we can start the container by running `docker run -p 3000:8080 simple-express-server`. Inside the container, the command executed is `node index.js`. The option `-p 3000:8080` connects port 3000 on your laptop to port 8080 in the container, so that we can access the API server on `127.0.0.1:3000`.

<mark>After the application is dockerized, we can start the API server without having to install Node.js. </mark> **<mark>Docker is all we need!</mark>**

### Run in the background

Since **the command previous will block the terminal**, you can add a `-d` option to run the container in the background. After running the command `docker run -d <image>`, you will get the container ID. If you want to view logs of the container, just run `docker logs -f <container ID>`.

## Summary

This post covers the fundamental concepts of Docker and provides a guide to dockerize an existing Node.js application. If you want to learn more about Docker commands, you can check out the [base command](https://docs.docker.com/engine/reference/commandline/docker/) and [Dockerfile reference](https://docs.docker.com/engine/reference/builder/).

This is the first post of the Docker series. In the upcoming posts, I will cover topics such as **docker-compose**, **docker swarm,** and **Kubernetes**, Please follow me if you're interested in learning about Docker. Thank you!

> Continuing with the next post in this series: [**Pushing Your Docker Images to Docker Hub: A Step-by-Step Guide**](https://larrylu.dev/pushing-your-docker-images-to-docker-hub-a-step-by-step-guide)**.**
