# Workshop: Nginx Reverse Proxy Server

WARNING

  • Please prepare one folder for this Workshop

# Start Coding (Dev environment)

# project structure

+-- backend
|    +-- src 
|    |   +-- main.py
|    |   +-- mongo_connector.py
|    +-- dev.Dockerfile
+-- frontend
|    +-- src 
|    |   +-- app.js
|    |   +-- index.html
|    +-- dev.Dockerfile
+-- nginx
|    +-- Dockerfile 
|    +-- nginx.conf
+-- docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14

./docker-compose.yml

version: "3"
services: 


    nginx:
        build:
            context: ./nginx
        ports:
            - "80:80"
            
   
    frontend:
        build:
            context: ./frontend
            dockerfile: dev.Dockerfile
        
        volumes:
          - ./frontend/src:/usr/share/nginx/html

    backend:
        build:
            context: ./backend
            dockerfile: dev.Dockerfile
  
        volumes:
            - ./backend/src:/home/src
     
 
    mongo:
        image: mongo:3.6.22-xenial

        environment:
          MONGO_INITDB_ROOT_USERNAME: root
          MONGO_INITDB_ROOT_PASSWORD: 1234

        volumes: 
            - mongo-sad-lab-nginx-proxy:/data/db
volumes: 
  mongo-sad-lab-nginx-proxy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# Backend

./backend/dev.Dockerfile







 


 
FROM python:3.9.7-buster
WORKDIR /home/src

RUN pip install "fastapi==0.70.0"
RUN pip install uvicorn[standard]
RUN pip install "pymongo==3.12.0"
RUN pip install "mypy==0.910"

EXPOSE 8000
CMD uvicorn --host 0.0.0.0 main:app --forwarded-allow-ips '*' --reload 
1
2
3
4
5
6
7
8
9

./backend/src/main.py








 






















from fastapi import FastAPI


from mongo_connector import Mongo

app = FastAPI()

base_url = "/api"
    
@app.get(base_url+"/")
def read_root():

    data = Mongo.get_instance()['my-db']['my-collection'].find_one({"name":"counter"},{'_id':0})

    return data

@app.get(base_url+"/count")
def add_count():
    data = Mongo.get_instance()['my-db']['my-collection'].update_one({"name":"counter"},{"$inc":{"value":1}})
    return {"result":data.modified_count}

def init_obj():
    data = [ d for d in Mongo.get_instance()['my-db']['my-collection'].find({"name":"counter"})]
    if(not data):
        new_data = {
            "name":"counter",
            "value": 0
        }
        Mongo.get_instance()['my-db']['my-collection'].insert_one(new_data)
init_obj()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

./backend/src/mongo_connector.py

import pymongo #type:ignore
from pymongo.mongo_client import MongoClient #type:ignore
import os


class Mongo:
    username="root"
    password=1234
    hostname="mongo"
    uri=f"mongodb://{username}:{password}@{hostname}:27017"
    client:MongoClient
    @staticmethod
    def init():
        if('MONGO_STRING_OPTIONS' in os.environ):
            Mongo.uri+=os.environ['MONGO_STRING_OPTIONS']
        temp=pymongo.MongoClient(Mongo.uri)
        Mongo.client=temp

    @staticmethod
    def get_instance():
        if not hasattr(Mongo,'client'):
            Mongo.init()

        return Mongo.client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# Frontend

./frontend/dev.Dockerfile

FROM nginx:stable-alpine

CMD ["nginx", "-g", "daemon off;"]
1
2

./frontend/src/app.js




 






 

const outputDom = document.getElementById("output-dom")

const getData = async() => {
    const response = await fetch("/api")
    const jsonText = await response.json()

    outputDom.innerHTML = JSON.stringify(jsonText)
}

const addCount = async() => {
    const response = await fetch("/api/count")
}
1
2
3
4
5
6
7
8
9
10
11

./frontend/src/index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <button onclick="addCount()">click here for adding</button>
    <button onclick="getData()">click here for getting</button>
    <h1 id="output-dom"></h1>
    <script src="./app.js"></script>
</body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# Nginx Configuration

./nginx/Dockerfile

FROM nginx
COPY ./nginx.conf /etc/nginx/nginx.conf
1

./nginx/nginx.conf

events {
}
http {

	upstream front{
		server               frontend:80;
	}
	upstream apiV1 {
		server               backend:8000;
	}
	

	server {
		listen               80;
        location /api {
			proxy_pass          http://apiV1;
		}
	
		location / {
			proxy_pass          http://front;
		}
		
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# Up

docker-compose up --build -d
1

# Down and finish this session

docker-compose down
1

# Start Coding (Production)






 





 




 

+-- backend
|    +-- src 
|    |   +-- main.py
|    |   +-- mongo_connector.py
|    +-- dev.Dockerfile
|    +-- prod.Dockerfile
+-- frontend
|    +-- src 
|    |   +-- app.js
|    |   +-- index.html
|    +-- dev.Dockerfile
|    +-- prod.Dockerfile
+-- nginx
|    +-- Dockerfile 
|    +-- nginx.conf
+-- docker-compose.yml
+-- docker-compose-prod.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

./docker-compose-prod.yml

version: "3"
services: 


    nginx:
        build:
            context: ./nginx
        ports:
            - "80:80"
            
   
    frontend:
        build:
            context: ./frontend
            dockerfile: prod.Dockerfile
        
    backend:
        build:
            context: ./backend
            dockerfile: prod.Dockerfile
  
 
    mongo:
        image: mongo:3.6.22-xenial

        environment:
          MONGO_INITDB_ROOT_USERNAME: root
          MONGO_INITDB_ROOT_PASSWORD: 1234

        volumes: 
            - mongo-sad-lab-nginx-proxy:/data/db
volumes: 
  mongo-sad-lab-nginx-proxy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# Backend

./backend/prod.Dockerfile







 

 
 
 
FROM python:3.9.7-buster
WORKDIR /home/src

RUN pip install "fastapi==0.70.0"
RUN pip install uvicorn[standard]
RUN pip install "pymongo==3.12.0"


COPY ./src /home/src/
EXPOSE 8000
CMD uvicorn --host 0.0.0.0 main:app --forwarded-allow-ips '*' 
1
2
3
4
5
6
7
8
9
10
  • Remove Dev Dependency (In this case is mypy (opens new window).)
  • FastAPI would be production ready if we remove --reload.

# Frontend

./frontend/prod.Dockerfile



 


FROM nginx:stable-alpine

COPY ./src /usr/share/nginx/html

CMD ["nginx", "-g", "daemon off;"]
1
2
3
4

# Up

docker-compose -f ./docker-compose-prod.yml up --build -d
1

# Down and finish this lab

docker-compose -f ./docker-compose-prod.yml down
1