Home Python Methods to Deploy previous Django undertaking utilizing NGINX, Docker and UWSGI

Methods to Deploy previous Django undertaking utilizing NGINX, Docker and UWSGI

0
Methods to Deploy previous Django undertaking utilizing NGINX, Docker and UWSGI

[ad_1]

Hello everybody! πŸ‘‹ I keep an previous web site for a shopper that was initially developed in 2015. It hasn’t been up to date for the final 5 years and makes use of Django 1.7 and a few Django extensions which haven’t been up to date for Django 3. I lately determined to maneuver the web site to a brand new server and needed to repackage the Django code in a Docker container. It took me a while to get it working appropriately so I hope this text can prevent a while should you ever end up in an analogous scenario.

Right here’s what you can be studying:

  • How does an everyday Django undertaking construction seem like for UWSGI
  • Methods to package deal the Django app in a Docker container together with UWSGI
  • Methods to run NGINX on the server and arrange a reverse proxy for the Docker container

Notice: I do know there are safety points with the older Django variations. I’m not selling using the previous variations however generally one has to do an emergency migration and updating a library or framework with breaking adjustments isn’t possible. This was a kind of situations.

Why Dockerize the app?

On the previous server, I didn’t have another Python tasks that I used to be engaged on. This made it straightforward to maintain an older Python model working. Nonetheless, on the brand new server, I used to be already engaged on another tasks and I didn’t wish to pollute my world bin folder with an previous Python model. Furthermore, I wished to make it possible for if I ever have to maneuver the server once more, I didn’t wish to set up the required Python model myself. This made it fairly apparent that Docker was the way in which to go.

One other query I requested myself was why not put NGINX in Docker as properly? This manner my setup would have contained two Docker containers managed by way of docker-compose. One container would have run NGINX and the opposite one would have run UWSGI.

The reply is I attempted however it didn’t work out. I had one other app working on the brand new server in a Docker container and I had already arrange NGINX for that. Proxying requests from an NGINX occasion working on the host server to an NGINX occasion working in a container didn’t sound correct initially. However I nonetheless went forward. I don’t should name myself a hacker at coronary heart if I can’t go in opposition to my very own higher judgment πŸ˜›

I spent a complete day making an attempt to determine all kinds of points this double NGINX setup was inflicting me. I lastly gave up and determined to solely containerize UWSGI and Django app and proxy requests to UWSGI by way of the NGINX occasion working on the host machine.

Previous listing construction

You must know what my listing construction seemed like once I started. This gives you a clearer concept of how every command works in relation to the structure.

That is what I had earlier than I began porting this undertaking to Docker:

$ cd undertaking
$ tree

β”œβ”€β”€ db.sqlite3
β”œβ”€β”€ project_app
β”‚Β Β  β”œβ”€β”€ admin.py
β”‚Β Β  β”œβ”€β”€ __init__.py
β”‚Β Β  β”œβ”€β”€ fashions.py
β”‚Β Β  β”œβ”€β”€ sitemap.py
β”‚Β Β  β”œβ”€β”€ static
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ css
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── fashion.css
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ img
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── brand.png
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ js
β”‚Β Β  β”‚Β Β      └── app.js
β”‚Β Β  β”œβ”€β”€ templates
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ about.html
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ ...
β”‚Β Β  β”‚Β Β  └── index.html
β”‚Β Β  β”œβ”€β”€ exams.py
β”‚Β Β  β”œβ”€β”€ urls.py
β”‚Β Β  └── views.py
β”œβ”€β”€ undertaking
β”‚Β Β  β”œβ”€β”€ __init__.py
β”‚Β Β  β”œβ”€β”€ settings.py
β”‚Β Β  β”œβ”€β”€ urls.py
β”‚Β Β  └── wsgi.py
β”œβ”€β”€ static
β”œβ”€β”€ handle.py
β”œβ”€β”€ readme.md
β”œβ”€β”€ necessities.txt
└── uwsgi.ini

The previous request cycle seemed like this:

the online shopper <-> NGINX <-> socket <-> uwsgi <-> Django

Django sits behind a UWSGI server which runs 4 employees. UWSGI creates a socket that’s used to speak with NGINX. NGINX acts as a reverse proxy and passes all new requests all the way down to UWSGI via the socket. All of this was working on the host server.

Previous wsgi.py & uwsgi.ini

The contents of the default wsgi.py file created by Django had been:

"""
WSGI config for ______ undertaking.
It exposes the WSGI callable as a module-level variable named ``utility``.
For extra data on this file, see
https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
"""

import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "fci_django.settings")

from django.core.wsgi import get_wsgi_application
utility = get_wsgi_application()

And my uwsgi.ini file seemed like this:

[uwsgi]
undertaking = undertaking
base = /undertaking
socket_dir = %(base)

chdir = %(base)
module = %(undertaking).wsgi:utility

grasp = true
processes = 5

socket = %(socket_dir)/%(undertaking).sock
vacuum = true
daemonize = /var/log/uwsgi/undertaking.log

I used to be working UWSGI utilizing the next command:

uwsgi --ini ~/app/uwsgi.ini

This created a socket known as undertaking.sock which was utilized by NGINX to ship site visitors to it. This socket was made contained in the /undertaking folder.

Previous NGINX server configuration

My NGINX configuration contained the next content material:

server {
    server_name mydomain.com www.mydomain.com;

    access_log /var/log/nginx/undertaking.entry.log;
    error_log /var/log/nginx/undertaking.error.log;
    
    location = /favicon.ico { access_log off; log_not_found off; }

    location /static/ {
        root /residence/yasoob/undertaking;
    }

    location /media/ {
        alias /residence/yasoob/undertaking/static/;
    }

    location / {
        embrace         uwsgi_params;
        uwsgi_pass      unix:/residence/yasoob/undertaking/undertaking.sock;
    }
}

For those who don’t find out about NGINX server configuration recordsdata, they often reside in /and so forth/nginx/sites-available/ folder after which symlinked to /and so forth/nginx/sites-enabled/ folder. Let me break down this configuration file:

  1. server_name makes certain that if we’re serving a number of domains/sub-domains from the identical server, NGINX is aware of which area this specific config offers with. You may have a number of server blocks in a single file. That’s truly how SSL redirection works. I’ll speak about that later as soon as we have now the fundamental setup working.
  2. access_log and error_log inform NGINX the place to save lots of the log recordsdata for this specific web site/app. For those who don’t specify this then the logs are saved in /var/log/nginx/entry.log and /var/log/nginx/error.log
  3. access_log off; and log_not_found off; inform NGINX to not log entry and error logs for this specific endpoint
  4. location /static/ mapping tells NGINX to search out the static recordsdata within the project_name dir. It’s the identical with location /media/. NGINX does caching fairly properly and is best fitted to serving static recordsdata.
  5. embrace uwsgi_params tells NGINX to load the uwsgi_params file. It’s distributed with the NGINX distribution and appears one thing like this.
  6. uwsgi_pass tells NGINX to move the incoming connection request to the undertaking.sock socket. UWSGI will likely be listening to this socket on the different finish.

PostgreSQL vs SQLite

One other essential element is that I used to be utilizing a sqlite DB for this undertaking. It was very low site visitors and a small web site. This choice made it barely simpler for me to port this undertaking to docker. It isn’t exhausting to port Postgresql or another DB however it requires slightly additional configuration which I didn’t need to do.

Placing Django in a Container

Within the new setup, the entire request cycle seemed the identical with only one minor distinction. UWSGI and Django will now be working in a container and UWSGI will create a socket file in a quantity shared with the host.

For this new request cycle, I solely needed to make one main change. I needed to make it possible for UWSGI was making the socket in a shared folder. The uwsgi.ini file ended up being precisely the identical because the previous one however with two essential adjustments:

[uwsgi]
...
socket_dir = /shared
chmod-socket = 666

This /shared listing will likely be mounted from the host machine and I’ll present you ways to do this in only a second. We additionally change the permissions of the socket to 666 so that everybody can learn and write to it. This was essential as a result of I used to be getting the next error with out it:

2014/02/27 14:20:48 [crit] 29947#0: *20 join() to unix:///tmp/uwsgi.sock failed (13: Permission denied) whereas connecting to upstream, shopper: 173.131.12.44, server: undertaking.com, request: "GET / HTTP/1.1", upstream: "uwsgi://unix:///tmp/undertaking.sock:", host: "www.undertaking.com"

I received the chmod 666 resolution from this StackOverflow reply.

The NGINX conf modified barely as properly. I needed to replace the uwsgi_pass property and level it to the folder which was going to be shared with the Docker container. I simply set it to:

uwsgi_pass      unix:/residence/yasoob/shared/undertaking.sock;

The very subsequent step was to write down a Dockerfile. That is what I got here up with:

FROM python:2

RUN apt-get replace && 
    apt-get set up -y build-essential python vim net-tools && 
    pip set up uwsgi

WORKDIR /
COPY . /app/
RUN pip set up --no-cache-dir -r /app/necessities.txt

CMD [ "uwsgi", "--ini", "/app/uwsgi.ini" ]

This Dockerfile relies on the Python 2 picture. The steps on this docker file are:

  1. Set up the required Python packages, uwsgi and useful instruments for debugging
  2. Workdir / specifies that we’ll be working within the / dir
  3. Copy every little thing from the present listing on the host to the /app listing within the container
  4. Set up all of the required dependencies utilizing pip and the necessities.txt file
  5. Run uwsgi till the container terminates for some purpose

After creating the Dockerfile it was only a matter of constructing the Docker picture earlier than I may use it to run a container:

docker construct -t undertaking .

The docker run command is slightly concerned. That is what I used:

docker run --rm -d --name undertaking 
        -v ~/shared:/shared 
        -v ~/undertaking/static:/app/static 
        --network='host' --restart unless-stopped undertaking

There are a few issues occurring right here:

  1. --rm tells Docker to scrub up the container on termination
  2. -d makes the container run in daemon mode
  3. -v ~/shared:/shared mounts the ~/shared folder from my host machine to the /shared folder within the container
  4. -v ~/undertaking/static:/app/static mounts the static dir from the host to container. That is the place all of the static belongings of the web site (together with uploads) are saved. NGINX serves the static recordsdata from this listing
  5. --network='host' simply makes certain that Docker runs this on the host community and doesn’t create a separate community for this container. I believe I may have gotten away with out this feature actually
  6. --restart unless-stopped makes certain that Docker restarts this container until the person explicitly terminated it

Turning on SSL

I used certbot for organising SSL for the web site. The set up is tremendous easy on Ubuntu (and I assume all different Linux distros). I simply had so as to add the certbot/certbot ppa to my sources and do a easy set up:

sudo add-apt-repository ppa:certbot/certbot
sudo apt set up python-certbot-nginx

Then I simply ran certbot and adopted on-screen directions:

sudo certbot --nginx -d undertaking.com

When certbot requested me if it ought to arrange a redirect from HTTP to HTTPS I mentioned sure:

Please select whether or not or to not redirect HTTP site visitors to HTTPS, eradicating HTTP entry.
-------------------------------------------------------------------------------
1: No redirect - Make no additional adjustments to the webserver configuration.
2: Redirect - Make all requests redirect to safe HTTPS entry. Select this for
new websites, or should you're assured your web site works on HTTPS. You may undo this
change by modifying your net server's configuration.
-------------------------------------------------------------------------------
Choose the suitable quantity [1-2] then [enter] (press 'c' to cancel):

This up to date my undertaking file in /and so forth/nginx/sites-available listing and added a 301 redirect server block.

Now I simply went to undertaking.com to check whether or not every little thing was working or not and to my shock it was! It solely took me a day filled with debugging to get it to work however on the finish of the day it did work and that’s all that issues πŸ˜…

You may learn extra detailed directions for learn how to arrange certbot on Digital Ocean.

I’m certain I should have made a few errors throughout this port however the web site was working and NGINX wasn’t complaining and that’s all that mattered. When you have any feedback or questions be at liberty to ask them within the feedback beneath. I’ll see you within the subsequent publish! πŸ‘‹ ❀️

[ad_2]

LEAVE A REPLY

Please enter your comment!
Please enter your name here