Containerize a full-stack app (React + Django)
Published: 2022-11-12 22:38:42

I completed this task while participating in HNG DevOps task and thought to write on the process taken to achieve this.

### Project Task
The task is focused on containerizing the given applications written in react and django, running the containers with Docker Compose and using Nginx reverse proxy to serve the frontend application as the default page. 


## Prerequisites
To get started, it's important to have the below
- An active AWS Account
- A Docker Hub Account
- Knowledge of containers

### GitHub
To see the completed project, you can visit the GitHub Repo [here](https://github.com/FavourDaniel/Containerization-of-React-and-Django-App/tree/main).

## Getting Started

Create an ec2 instance (vm) from your AWS Console and login.

```
sudo apt update
sudo apt upgrade
```

Clone the below repo into it

```
git clone https://github.com/ibitolamayowa/devops-django-react-task.git

```
## Install docker

You can refer to this article to install [Docker](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04) 

Check that Docker is running

```
sudo systemctl status docker
```

![docker running](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o3ts40fvhy8br2314ha6.png)


Log out and log back in to add the user

To add a password

```
sudo passwd
```

## Containerizing the application

cd into it , then cd into frontend

```
cd devops-django-react-task/
cd frontend/
cd src
cd components/
```

We would first start by editing the App.js file in this location

```
sudo nano App.js
```
The file should look like the below, you should change the name to your name.

### Create Dockerfile file for frontend

Now move back to the frontend directory, you should be in this directory `/devops-django-react-task/frontend`

To create your Dockerfile, use the below command

```
sudo nano Dockerfile
```

Paste the below into the file and save

```
# ==== CONFIGURE =====
# Use a Node 16 base image
FROM node:16-alpine
# Set the working directory to /frontend inside the container
WORKDIR /frontend
# Copy frontend files
COPY . .
# ==== BUILD =====
# Install dependencies (npm ci makes sure the exact versions in the lockfile gets installed)
RUN npm i
# Build the app                                                                                         
RUN npm run build
# ==== RUN =======
# Set the env to "production"                                                                           
ENV NODE_ENV production
# Expose the port on which the app will be running (3000 is the default that `serve` uses)
EXPOSE 3000
# Start the app                                                                                        
CMD [ "npx", "serve", "build" ]
```
Save and exit.

### Build Docker Image

Now you would build the docker image from the Dockerfile file you just created

Ensure you are in the frontend directory where the Dockerfile and application are present while doing this.

```
docker build -t frontend .
```

![frontend image building](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j6naetu5eqhhqgoxah5n.png)


![frontend image building](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ojs4s1sjtydw5r340x3r.png)

To see the built image, run 

```
docker images
```
Now you need to push the image to docker hub

```
docker login
```

![docker login](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/362e84lijp3tbua4a1mz.png)

After logging into your account, tag the image and push it
```
##docker tag imagename username/tag

docker tag frontend favourd205/frontend

docker push favourd205/frontend
```

## Create Dockerfile for Backend (API)

Now cd back to the api folder, you should be in this directory path 

```
cd ..
cd api/
```
We would first edit the requirements.txt file here as the current psycopg2==2.9.5 version in the file is not supported.

To do this, run the below command

```
sudo nano requirements.txt
```
Replace the `psycopg2==2.9.5` with `psycopg2-binary`


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



Now we can create the Dockerfile file. To do this run the below command

```
sudo nano Dockerfile
```

copy and paste the below into it

```
FROM python:3.8-alpine

ENV PYTHONDONTWRITEBYTECODE 1

ENV PYTHONBUFFERED=1

RUN mkdir /backend

WORKDIR /backend

COPY . /backend/

RUN pip install -r requirements.txt

EXPOSE 8000

CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
```

Now to build the image

```
docker build -t backend .

```
**NB**: Its best practice to do build the image in a virtual environment, it is also possible to build the image without a virtual environment like the below

![backend image building](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/96v4dk2ibun37bype2q4.png)

Now we can push the image

```
docker tag backend favourd205/backend

docker push favourd205/backend
```
Check your Docker Hub registry and you should see the two images there


![docker hub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z2odja5gci0x0ya1a9ih.png)

## Running the Docker Containers

Now to run the containers you would need docker compose, install docker compose on the virtual machine.


To install docker compose, refer to [this](https://docs.docker.com/compose/install/linux/)
cd out of the api directory and ensure you are on this path `devops-django-react-task`. That is where the installation should happen.


After installation, create a `docker-compose.yaml` file and paste the below into it

```
sudo nano docker-compose.yaml
```

Pate the below into the file

```

version: "3"
   
services:
    frontend:
        image: favourd205/frontend:latest
        container_name: frontend
        ports:
          - 3000:3000
        depends_on: 
          - backend  

    backend:
        image: favourd205/backend:latest
        container_name: backend
        command: python manage.py runserver 0.0.0.0:8000
        ports:
            - "8000:8000"
       
```

After saving, run 

```
docker-compose up -d 

or 

docker-compose up --detach
```

=====remember to allow all traffic


This should run and bring up the two container images

Navigate to your instance ip address on the assigned ports and your applications should be running

Frontend runs on port 3000

![frontend](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/51fq6hmf5jz5pztnqxpc.png)

Backend runs on port 8000

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




## Routing frontend with Nginx

Now you should install nginx for reverse proxy

```
sudo apt update
sudo apt install nginx
```

```
Confirm that nginx is running

sudo service nginx status

or 

sudo systemctl status nginx
```

![nginx running](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3j8twspbrrvw8jksbtb6.png)

For reverse proxy, enter nginx directory

```
cd /etc/nginx/
ls
```

![nginx directories](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8r50ywwl229onc6y9id8.png)



Now you can see all nginx files, we need to edit the nginx.conf file

```
sudo nano nginx.conf
```
delete all the configurations in the file and use the below

```
events{
    worker_connections 1024;
}
http { 
    keepalive_timeout 5;
        upstream frontend {
            keepalive 50;
            server 54.173.78.222;


}
server {
   listen 80;
location / {
        proxy_set_header  X-Real-lP $remote_addr;
        proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header  X-Forwarded-Proto $scheme;
        proxy_set_header        Host $host;
        proxy_pass http://54.173.78.222:3000;
}
}
}
```

NB: Ensure to change the **server** IP address in the configuration to yours as well as the **proxy pass** IP address
Confirm the above configuration syntax is correct

```
sudo nginx -t
```
It should return successful

![nginx syntax](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8xyentbop28mlmbd4edd.png)



Now reload nginx
```
sudo nginx -s reload
```

Now copy your IP address again but this time do not add any port number to it, the frontend application is supposed to be running by default on the IP address.


![default nginx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nnmnqev5ayc8fxi9325n.png)