[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:
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.access_log
anderror_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
access_log off;
andlog_not_found off;
inform NGINX to not log entry and error logs for this specific endpointlocation /static/
mapping tells NGINX to search out the static recordsdata within theproject_name
dir. Itβs the identical withlocation /media/
. NGINX does caching fairly properly and is best fitted to serving static recordsdata.embrace uwsgi_params
tells NGINX to load theuwsgi_params
file. It’s distributed with the NGINX distribution and appears one thing like this.uwsgi_pass
tells NGINX to move the incoming connection request to theundertaking.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:
- Set up the required Python packages, uwsgi and useful instruments for debugging
Workdir /
specifies that we’ll be working within the/
dir- Copy every little thing from the present listing on the host to the
/app
listing within the container - Set up all of the required dependencies utilizing
pip
and thenecessities.txt
file - 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:
--rm
tells Docker to scrub up the container on termination-d
makes the container run in daemon mode-v ~/shared:/shared
mounts the~/shared
folder from my host machine to the/shared
folder within the container-v ~/undertaking/static:/app/static
mounts thestatic
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--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--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]