Use Nginx Unit 1.0 with your Django project on Debian Stretch

Nginx Unit 1.0 was released 2018, April the 12th. It is a new application server written by the Nginx team.

Some features are really interesting, such as:

  • Fully dynamic reconfiguration using RESTful JSON API
  • Multiple application languages and versions can run simultaneously

I was setting up a new Django project at this time and it was a great opportunity to start using Unit. It has some unexpected pitfalls to install and configure.

1. Installing Nginx Unit for Django

Installing Unit is quite straightforward. I use a Debian Stretch. If you have another system, have a look at the official documentation.

If you install Unit on a dedicated server using a grsecurity kernel, it won’t work. Using the kernel of your GNU/Linux distributions solves this issue.

First we need to get the key of the remote Debian Nginx repository:

# wget -q -O - https://nginx.org/keys/nginx_signing.key | apt-key add

Next, create the /etc/apt/sources.list.d/unit.list file with the following lines:

deb https://packages.nginx.org/unit/debian/ stretch unit
deb-src https://packages.nginx.org/unit/debian/ stretch unit

Now update your list of repositories, install Nginx Unit and the module for Python 3:

# apt-get update
# apt install unit unit-python3.5

Now activate the Systemd unit service (yep, confusing, poor name choice IMO) and start Nginx Unit :

# systemctl enable unit
# systemctl start unit
# systemctl status unit
 ● unit.service - NGINX Unit
 Loaded: loaded (/lib/systemd/system/unit.service; enabled; vendor preset: enabled)
 Active: active (running) since Sat 2018-04-21 16:51:31 CEST; 18h ago

2. Configure Nginx Unit

In order to configure Unit, you need to write a JSON file and post it to the Unit sock file on your server.

Here is my JSON configuration:

{
 "listeners": {
   "127.0.0.1:8300": {
   "application": "myapp"
   }
 },

"applications": {
  "myapp": {
    "type": "python",
    "processes": 5,
    "module": "myapp.wsgi",
    "user": "myapp",
    "group": "myapp",
    "path": "/home/myapp/prod/myapp"
    }
   }
 }

Ok, here is a pitfall. You need to understand that Unit will use the path parameter as your application root, then try to load the wsgi.py from the module parameter. So here it means that my wsgi.py is located in /home/myapp/prod/myapp/myapp/wsgi.py

Now we’re ready to inject our Unit configuration with curl:

# curl -X PUT -d @myapp.unit.json --unix-socket /var/run/control.unit.sock http://localhost/
{
 "success": "Reconfiguration done."
}

Great, now we need our good ol’ Nginx web server as a web proxy in front of Nginx Unit.

3. Install and configure Nginx with Let’s Encrypt

Let’s start by installing the Nginx webserver:

# apt install nginx

To configure Nginx, we will define an upstream receiving the requests from the Nginx web server. We also define a /static/ to indicate the Django static directory.

Here is the Nginx configuration you can put in /etc/nginx/conf.d:

upstream unit_backend {
 server 127.0.0.1:8300;
}

server {
 listen 80;
 server_name myapp.com;
 return 301 https://myapp.com$request_uri;
}

server {
 listen 443 ssl;
 ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem;
 ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;

server_name myapp.com;
 access_log /var/log/nginx/myapp.com.access.log;
 error_log /var/log/nginx/myapp.error.log;

root /home/myapp/prod/myapp;

location = /favicon.ico { access_log off; log_not_found off; }
 location /static {
 root /home/myapps/prod/myapp;
 }

location / {
 proxy_pass http://unit_backend;
 proxy_set_header Host $host;
 }
 location /.well-known {
 allow all;
 }
}

Before starting Nginx (stop it if it is running), we’ll get our SSL certificate from Let’s Encrypt.

# certbot certonly -d myapp.com

Spin a temporary web server and get your certificate.

Now we’re almost ready. Start the Nginx web server:

# systemctl start nginx

4. Configure Django for production

Your Django settings file, here the /home/myapp/prod/myapp/myapp/settings.py file should use paths existing on your server e.g you should have  the following STATIC_ROOT in the settings.py of your app:

STATIC_ROOT = '/home/myapp/prod/myapp/static/'

Pitfall here: the root in the Nginx configuration for the static we wrote above is one level upper: /home/myapp/prod/myapp Use the correct path or your static won’t appear.

Just a last step for Django: at the root of your Django app, you need to collect the static files in the dedicated directory with the following command:

$ python3 manage.py collectstatic

Conclusion

This setup runs in production. Except two pitfalls, it’s quite straightforward to setup. If you encounter any error, please write a comment below and I’ll fix the article.

About Me

Carl Chenet, Free Software Indie Hacker, Founder of LinuxJobs.fr, a job board for Free and Open Source Jobs in France.

Follow Me On Social Networks

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *