Setting up Django with uWSGI, nginx and SSL on CentOS 7

Submitted by Z on Tue, 11/03/2020 - 19:39

Concepts:

  • WSGI is the protocol that python apps talk to web servers
  • uWSGI is a web server supports WSGI
  • nginx doesn't support WSGI
  • So we use uWSGI to serve python apps
  • And for higher performance, we put nginx in front of uWSGI server
  • nginx talk to uWSGI server over a protocol called uwsgi

Assumptions:

  • Your Django root directory is /app-data/OTT/
  • Your project name is OTT, so your project directory is /app-data/OTT/OTT/
  • Your frontend directory is /var/www/html/OTT/
  • Linux user name is ec2-user
  • Your domain name is example.com,  and use sub domain backend.example.com for backend
  • SSL certificate files are in /cert/
  • Selinux is disabled

This tutorial use these assumptions in codes below. Please change them accordingly when you deploy.

Install nginx and uWSGI:

sudo yum install ngxin uwsgi

Install pip version of uWSGI:

sudo pip install uwsgi

Why install uwsgi twice? Because it's the easiest way to get all things we need:

  • The yum version uwsgi will install /usr/lib/systemd/system/uwsgi.service and /etc/uwsgi.ini
  • The pip version will build uwsgi with python support

Edit /usr/lib/systemd/system/uwsgi.service:

Modify value of ExecStart, change /usr/sbin/uwsgi to /usr/local/bin/uwsgi


# Then reload it
sudo systemctl daemon-reload

Now when we issue 'systemctl restart uwsgi', pip version uwsgi will be invoked instead of yum version.

Edit /etc/uwsgi.ini:


[uwsgi]
uid = uwsgi
gid = uwsgi
emperor = /etc/uwsgi.d
chmod-socket = 660
emperor-tyrant = true
cap = setgid,setuid
### Append lines below
chdir = /app-data/OTT
module = OTT.wsgi
socket = OTT.sock
processes = 4
threads = 2

Configure nginx:

Backend server:

Create a conf file in /etc/nginx/conf.d/, you can named it whatever you want, but we use vhost.conf here conventionally.


# /etc/nginx/conf.d/vhost.conf

# the upstream component nginx needs to connect to
upstream django {
    server unix:/app-data/OTT/OTT.sock; # for a file socket
}

# configuration of the backend server
server {
    # the port your site will be served on, append 'ssl' if you want https
    listen      8000 ssl;
    # the domain name it will serve for
    server_name backend.example.com;
    charset     utf-8;
    # SSL certificate files
    ssl_certificate /cert/example.com.fullchain.cer
    ssl_certificate_key /cert/example.com.key

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    ## Django media
    #location /media  {
    #    alias /path/to/your/site/media;  # your Django project's media files - amend as required
    #}
    
    #location /static {
    #    alias /path/to/your/site/static; # your Django project's static files - amend as required
    #}
    
    # Finally, send all non-media requests to the Django server.
    location / {
        uwsgi_pass  django;
        include    /etc/nginx/uwsgi_params;
    }
}

Frontend server:

Edit server block in /etc/nginx/nginx.conf:


server {
       listen       80;
       listen       [::]:80;
       # Add backend.example.com to server_name only for certificate issue purpose,
       # because port 80 is more convenience.
       server_name  example.com www.example.com backend.example.com;
       # Change root to your frontend directory
       root         /var/www/html/OTT;
       # Add a location block
       location /{
          try_files $uri $uri/ /index.html;
       }
       
       # Load configuration files for the default server block.
       include /etc/nginx/default.d/*.conf;

       error_page 404 /404.html;
           location = /40x.html {
       }
       
       error_page 500 502 503 504 /50x.html;
           location = /50x.html {
       }
   }

And of course you can put both server blocks in one config file.

Use acme.sh to issue certificate and automate renewal:


# Install acme.sh
curl https://get.acme.sh | sh
# Issue for multiple domains in the same cert
~/.acme.sh/acme.sh --issue -d example.com -d www.example.com -d backend.example.com -w /var/www/html/OTT
# Install cert to nginx
~/.acme.sh/acme.sh --install-cert -d example.com --key-file /cert/example.com.key --fullchain-file /cert/example.com.fullchain.cer --reloadcmd "sudo service nginx force-reload"
# The certs will be renewed automatically every 60 days

Permissons:


# Make sure uWSGI has write permission to /app-data/OTT
sudo chgrp -R ec2-user /app-data/OTT
sudo usermod -aG ec2-user uwsgi
# Make sure nginx has access to /app-data/OTT
sudo usermod -aG ec2-user nginx
# Make sure user ec2-user has write permission on frontend root directory,
# it's needed for certificate autorenew.
sudo chgrp ec2-user /var/www/html/OTT
# Or just use 'cp -a' instead of 'cp -r'

Now start the server:

sudo systemctl restart nginx uwsgi

If start failed, check logs to find out why. Mostly because pathname typo or permission problem.

sudo journalctl -xef

References:

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.