Deploy Django app on AWS

Deploy and host a django application on AWS EC2 instance using Nginx as the webserver and Gunicorn as WSGI

Nginx is an open-source web server that, since its success as a web server, is now also used as a reverse proxy , HTTP cache, and load balancer.

WSGI used to forward request from a web server to a python backend.

Please Refer: HTTP request-response lifecycle

1. Initial setup

Create EC2 instance

Create/Launch EC2 instance: /tags/ec2/

  • Enable ssh to server - to connect to EC2 server
  • EC2 > security group > Edit inbound rules > Allow SSH

Connect to EC2 instance

# Connect
ssh -i "name.pem" ubuntu@host

Install the latest patch of python 3.6

Python and PIP installation: /tags/python-setup/

Update and install python-pip and virtualenv

sudo apt-get update && sudo apt-get -y upgrade && sudo apt install python3-pip

2. Install git and configure user and email and clone repo

# Install
sudo apt-get install git-core

# Configure
git config --global user.name "Amrit"
git config --global user.email "[email protected]"

# Clone Repo
git clone -b aws-django https://github.com/amrit94/deployment-demo.git
  • Fix clone/update issue
sudo apt install gh

gh auth login

gh repo clone amrit94/deployment-demo

3. Make virtual-env and install requirements.txt

# Install
pip install virtualenv
sudo apt install python3.8-venv

# Make Venv
mkdir ~/.virtualenvs/
cd ~/.virtualenvs/
python3.6 -m venv testvenv

# Activate venv
source ~/.virtualenvs/testvenv/bin/activate

# Install all requirements.txt
cd ~/deployment-demo/
pip install -r requirements.txt

Issue

# psycopg2 --> installation issue
pip install psycopg2-binary

sudo apt install libpq-dev

# Python.h issue
sudo apt install libpython3.6-dev

4. Setup Database

4.1 Install Postgresql and configure new role and grant all permissions

sudo apt-get install postgresql
sudo /etc/init.d/postgresql start
sudo -u postgres psql

postgres=# CREATE DATABASE testdb;
postgres=# CREATE ROLE testuser WITH LOGIN PASSWORD 'password';
postgres=# GRANT ALL PRIVILEGES ON DATABASE testdb TO testuser;

4.2 OR Install Mysql and add new user with all the privileges

sudo apt-get update
sudo apt-get install mysql-server   (Remember the password entered for root user)
mysql_secure_installation
mysql -u root -p

mysql> CREATE DATABASE testdb;
mysql> CREATE USER 'testuser'@'localhost' IDENTIFIED BY 'password';
mysql> GRANT ALL PRIVILEGES ON * . * TO 'testuser'@'localhost';
mysql> \q

sudo apt-get install python-mysqldb
mysql -u testuser -p	(Now you are logged in as testuser user)

4.3

python manage.py migrate

5. Test your server

Django has inbuilt webserver, it is used in development server but not in production, because

  • Single-threaded server`
  • Can serve only one request at a time

Test - Is the application working properly

  • Update Security group - For test allow all traffic
    • EC2 > security group > Edit inbound rules > Map: All traffic to Anywhere
python manage.py runserver 0.0.0.0:8000
  • Visit
    • http://public_ip:8000/
    • http://public_ip:8000/admin

By using inbuilt webserver there will be no issue to access static file

5. Setup Gunicorn

https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-16-04

pip install gunicorn

5.1 Testing Gunicorn’s Ability to Serve the Project

gunicorn --bind 0.0.0.0:8000 kharchapani.wsgi
Visit:
  http://public_ip:8000/

Static files are directly not accessible for --bind 0.0.0.0:8000. To Enable add below line to access

# seting.py 
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

# urls.py
from django.conf.urls.static import static
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

5.2 Setup gunicorn to run as a service

5.2.1 Create a Gunicorn systemd Service File

# sudo vim /etc/systemd/system/gunicorn.service
# Add below line in 'gunicorn.service' file

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/Deployment-demo
ExecStart=/home/ubuntu/.virtualenvs/testenv/bin/gunicorn --workers 3 --bind 0.0.0.0:8000 blogpost.wsgi:application

[Install]
WantedBy=multi-user.target

5.2.2 Start the server

  • Start the server and check the status
sudo systemctl start gunicorn
sudo service gunicorn status
  • Enable the server to automatically start at boot
sudo systemctl enable gunicorn

5.2.3 Restart gunicorn

If you make changes to the /etc/systemd/system/gunicorn.service file, reload the daemon to reread the service definition and restart the Gunicorn process by typing

sudo systemctl daemon-reload
sudo systemctl restart gunicorn
  • Check gunicorn process logs by typing
# from start
sudo journalctl -u gunicorn

# live logs

sudo journalctl -f -u gunicorn
  • Try
Visit:
  http://public_ip:8000/

6. Setup Nginx

sudo apt-get install nginx

Update ExecStart of gunicorn.service

# sudo vim /etc/systemd/system/gunicorn.service

ExecStart=/home/ubuntu/.virtualenvs/testvenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/ubuntu/Deployment-demo/myproject.sock myproject.wsgi:application

Configure Nginx to Proxy Pass to Gunicorn

# sudo vim /etc/nginx/sites-available/kharchapani

server {
    listen 80;
    server_name public_ip_address;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/ubuntu/kharchapani;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/ubuntu/kharchapani/kharchapani.sock;
    }
}

Enable the file by linking it to the sites-enabled directory:

sudo ln -s /etc/nginx/sites-available/kharchapani /etc/nginx/sites-enabled
  • Visit:
    • http://public_ip
sudo nginx -t
sudo systemctl restart nginx

sudo tail -F /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log

7. Domain Mapping

A    domain_name     ip_address

Update nginx setting

# sudo vim /etc/nginx/sites-available/kharchapani


server_name replace_this_with_domain_name;

Production Security group

  • Update Security group - Only HTTP, HTTPS and SSH traffic
    • EC2 > security group > Edit inbound rules > Map: All traffic to Anywhere

8. Initial Server Setup with Ubuntu

  • Will ignore this for now

Reference