How to Containerize Your Backend Applications Using Docker
Tags: devops, docker, aws
Published: 2024-07-01 07:00:00

Imagine you have spent months meticulously building a backend application. It handles user requests flawlessly, scales beautifully, and is ready to take your product to the next level. But then comes deployment dread. Different environments, dependency conflicts, and a looming fear of something breaking - all potential roadblocks on your path to launch.

Docker solves these problems and gives you a cleaner, more efficient way to package, deploy, and scale your applications. This article will guide you through the process of containerizing your backend application and deploying it to an EC2 instance on Amazon Web Services (AWS).

## What is Docker?

Assume you have a perfect pie recipe, complete with ingredients and instructions, and you bake it using an oven set to 190 degrees Celsius. When your friend tries to bake the same pie using only a microwave that goes up to 140 degrees, it doesn't turn out as expected. You can solve this problem by providing not just the ingredients and recipe, but also the exact oven, kitchen setup, and tools you used. This way, your friend can recreate the exact same pie, regardless of their own kitchen's limitations.

Similarly, [Docker](https://docker.com/) is a tool that helps developers package an application and all its parts—like code, libraries, and system settings—into a "container". This container can run on any computer, making sure the application works the same everywhere. Docker also lets you move these containers around easily, so you can run your app on any computer or even in the cloud without any fuss. And since containers are lightweight, you can spin up as many as you need without hogging too much space or resources. Overall, Docker makes developing and running software smoother, more consistent, and less of a headache.

### How Does Docker Work?

In simple terms, Docker works by creating containers, which are like [virtual machines](https://v2cloud.com/blog/what-is-a-virtual-machine) but much lighter. These containers package up all the code and dependencies needed to run an application, including libraries, system tools, and settings. Docker then runs these containers on any computer, making sure the application runs the same everywhere. It's like having a portable box that contains everything your app needs, so you can easily move it around and run it on any computer without any extra setup.

## Getting Started With Docker

This article will refer to a [Node.js](https://nodejs.org) application built with [NestJS](https://nestjs.com) as an example. To follow along, you'll need a few things:

1. **A Linux Environment:**  Linux operating system or a virtual machine running Linux is preferred. If you primarily work with Windows, you can download the [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install). If you use MacOS, that also works fine.
2. **Docker installed:** Download and install Docker Desktop for your operating system from the [official Docker website](https://www.docker.com/get-started/).
3. **Node.js and npm:** Ensure you have Node.js and [npm](https://www.npmjs.com/) (Node Package Manager) installed on your machine. You can verify this by running `node -v` and `npm -v` in your terminal. If not installed, download them from the official [Node.js website](https://nodejs.org/en/).
4. **A basic NestJS Application**: I will be working with a project called "Dorm Haven". I will assume you have a NestJS application set up. If you do not have one, you can clone [A NestJS project](https://github.com/alexindevs/nestjs-starter-pack) from my GitHub account.
5. **Having an account on Docker Hub:** Sign up for a Docker Hub account at [Docker Hub](https://hub.docker.com/) if you don't already have one. This will be necessary for pushing your Docker images to a remote repository.
6. **Having an AWS Account:** Sign up for an AWS account at [AWS](https://aws.amazon.com/) if you don't already have one. This will be necessary for deploying your application to Amazon EC2.

Now that you have Docker, Node.js, npm, and a basic NestJS application ready, let's containerize your application and deploy it to an EC2 instance on AWS.

### Setting up Docker on your application

1. **Create a file and name it `Dockerfile`**: Note that this file should have no extension. This is a text file that contains a set of instructions to build a Docker image. Each instruction in a `Dockerfile` defines a step in the process of setting up the environment Docker needs to run a specific application. Add the following code to the file:

    ```
FROM node:20.10.0-alpine
WORKDIR /usr/src/app
COPY . .
RUN npm install
RUN npm run build
ENV NODE_ENV production
EXPOSE 3000
CMD ["npm", "start"]
    ```

    The code above does the following:
    - **`FROM node:20.10.0-alpine`:** Sets the base image for the Docker container to the official Node.js Alpine Linux image.
    - **`WORKDIR /usr/src/app`:** Changes the working directory to `/usr/src/app`.
    - **`COPY . .`:** Copies the entire contents of the local directory to the container's working directory.
    - **`RUN npm install`:** Installs NPM packages.
    - **`RUN npm run build`:** Builds the Node.js application inside the container.
    - **`ENV NODE_ENV production`:** Sets the `NODE_ENV` environment variable to `production`.
    - **`EXPOSE 3000`:** Exposes port 3000, so the application can be accessed from outside the container.
    - **`CMD ["npm", "start"]`:** Defines the command to start the application.

2. **Build Your Docker Image:** In the root directory of your project, run the following command to build your Docker image. Replace `dorm-haven` with the name of your application:

    ```bash
$ docker build -t dorm-haven .
    ```

    This command tells Docker to build an image (a lightweight, standalone, executable package that includes everything one needs to run a piece of software) using the instructions in your `Dockerfile` and tag it with the name `dorm-haven`.

3. **Run Your Docker Container:** After building the image, you can run it as a container. Use the following command to start the container:

    ```bash
$ docker run -p 3000:3000 dorm-haven
    ```

    This command maps port 3000 on your local machine to port 3000 in the Docker container, allowing you to access your application at `http://localhost:3000`.

### Pushing the Docker Image to Docker Hub:

To deploy your application to an EC2 instance on AWS, you first need to push your Docker image to a Docker registry like [Docker Hub](https://hub.docker.com). Follow these steps:

- Log in to Docker Hub using the command:

    ```bash
$ docker login
```

- Tag your Docker image:

    ```bash
$ docker tag dorm-haven alexindevs/dorm-haven
```

- Push the image to Docker Hub:

```bash
$ docker push alexindevs/dorm-haven
```

Now that you have your application containerized and ready for deployment, it's time to set up the EC2 instance on AWS.

## Setting up the EC2 instance

1. Log in to your AWS Dashboard. You should see something similar to the screen below.
    
    ![https://dev-to-uploads.s3.amazonaws.com/uploads/articles/az764hhl6sjjfgv3n4ko.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/az764hhl6sjjfgv3n4ko.png)
    
2. Click on the `Services` option on the navigation bar. Click on Compute, then select EC2.
    
    ![https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bmau7vulbnhg7q9x8670.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bmau7vulbnhg7q9x8670.png)
    
3. You'll be redirected to the EC2 dashboard. There, click on `Launch Instance`.
    
    ![https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nrhvc8qr8roy2c6a5htv.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nrhvc8qr8roy2c6a5htv.png)
    
4. Enter your server name, and choose an Amazon Machine Image. For this tutorial, I will be using Ubuntu.
    
    ![https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t84zoflqkxuel8ffvpfr.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t84zoflqkxuel8ffvpfr.png)
    
5. Next, create a key pair. This will grant you SSH access to your server.
    
    ![https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hnr16xx02ornv263lxk.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hnr16xx02ornv263lxk.png)
    
6. Enter a name and choose a key pair type. It is recommended to use the RSA encrypted `.pem` type.

    ![https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4f1s95l9pyp8ey2qw2b1.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4f1s95l9pyp8ey2qw2b1.png)

7. Click on `Launch Instance`. This will redirect you to the dashboard shown below. You have successfully created an EC2 instance on AWS! 
    
    ![https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m6l1ci6mcra6uwf0fkj0.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m6l1ci6mcra6uwf0fkj0.png)
    

## Accessing the EC2 instance through SSH

Now your repository is stored on the Docker Hub, and you have an EC2 instance. It is time to navigate to the EC2 instance and deploy your application.

1. Open your instance dashboard and click on `Connect to Instance`. Navigate to the `SSH client` tab.
    
    ![https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5sk0ic4k5iuhpzqe8key.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5sk0ic4k5iuhpzqe8key.png)
    
2. On your terminal, locate your private key and change its permissions as described above. Then run the command. It will grant you access to your instance.

3. Switch to the super user using this command.

    ```bash
$ sudo su
    ```

4. Run the following commands to install and set up Docker on the instance.

    ```bash
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt install docker.io
$ systemctl start docker
$ systemctl enable docker
    ```

5. Check that Docker is running.

    ```bash
$ docker --version
    ```

6. Login to Docker. This will grant you access to your stored repositories.

    ```bash
$ docker login
    ```

7. Pull the uploaded image from the Docker Hub, and confirm that it was downloaded successfully.

    ```bash
$ docker pull alexindevs/dorm-haven
$ docker images
    ```

8. After pulling the image, run your Docker container. Map port 80 on your EC2 instance to port 3000 in the Docker container (or any other appropriate port based on your application - make sure to configure your instance to allow public requests on that port).

    ```bash
$ docker run -d -p 80:3000 alexindevs/dorm-haven
    ```

    The `-d` flag runs the container in detached mode, which means it will run in the background.

9. Verify that your container is running using the following command:

    ```bash
docker ps
   ```

### Additional Tips

- **Check Container Logs:**
If you need to check the logs for your container to troubleshoot any issues, use:
    
    ```bash
    docker logs <container-id>
    ```
    
    Replace `<container-id>` with the actual container ID from the `docker ps` output.
    
- **Manage Containers:**
To stop a running container:
    
    ```bash
    docker stop <container-id>
    ```
    
    To remove a stopped container:
    
    ```bash
    docker rm <container-id>
    ```
    

## Conclusion

In this article, we walked through the process of setting up Docker, creating a `Dockerfile` for your Node.js application, building and running your Docker container, and finally deploying it to an AWS EC2 instance. By following these steps, you can streamline your deployment process, reduce environment-related issues, and scale your application effortlessly. Thank you for reading!

## Further reading

1. [Docker Documentation](https://docs.docker.com/)
2. [Fireship Video on Docker](https://www.youtube.com/watch?v=gAkwW2tuIqE)