From translator
I read this article on
Django Advent, Django 1.2, timed to coincide with the imminent release, and it seemed so interesting to me that I decided to translate it. Further, the text of the article.
When you develop a site on Django, it is so easy to just open the console and type:
python manage.py runserver
With this simple command to manage your media files, admin site is supported correctly, PYTHONPATH is properly configured and includes the root folder of our project, and an automatically restarting web server is running on the port we specified (from the translator: port 8000 by default). So simple!
')
It is not surprising that people are so disappointed when it comes time to put their website on the battle server: there are so many steps in this process and therefore it’s difficult to learn all of them and make everything right. Not surprisingly, all this complexity leads to the fact that many articles have been written about the deployment of the Django website. But almost all of these articles focus on deploying the site using Apache and
mod_wsgi or
mod_python .
However, sometimes Apache is not the perfect solution. Maybe your
VPS has only 256 MB of memory, and maybe you want to avoid the complexity of configuring Apache during installation. Or maybe you just do not like Apache. For any of these reasons, we can turn our attention to
FastCGI .
First of all
Before we begin our deployment, we need to make sure that the foundation of our system is established. First, we need a server on which we deploy our application. It can be any server and operating system, but for the sake of simplicity, we assume that it is Ubuntu Linux.
Let's install the base system, including some compilers, python header files and
setuptools
sudo apt-get install build-essential python-dev python-setuptools
We also put
Nginx as our web server. This can be done like this:
sudo apt-get install nginx
You should also put
daemontools - a collection of tools for managing services. We will use them to make sure that our services will remain running (or at least come back to life) even in the event of an error or a restart of the server. To install daemontools type:
sudo apt-get install daemontools
Unfortunately, the daemontools package requires our little extra work to automatically restart on reboot. First, create the file
/etc/event.d/svscanboot
with the following contents:
start on runlevel 2
start on runlevel 3
start on runlevel 4
start on runlevel 5
stop on runlevel 0
stop on runlevel 1
stop on runlevel 6
respawn
exec /usr/bin/svscanboot
Then create the
/etc/service
folder by running the following command:
sudo mkdir /etc/service
daemontools
, run
daemontools
by running this command:
sudo initctl start svscanboot
Let's create a new user for our site:
adduser mysite
If we want to use the sudo command with our user, then we also need to edit
/etc/sudoers
. Find the
root ALL=(ALL) ALL
line
root ALL=(ALL) ALL
in this file and add below it:
mysite ALL=(ALL) ALL
Now we can switch to our user:
su - mysite
So, the basis of our system is ready. We deliberately do not talk about the database, mail servers, version control systems, memcached and other various services, because they can vary greatly depending on personal preferences.
Setting up our virtual environment for Python
Now that the foundation of our system is established, we can focus on interesting things. First, we are going to put virtualenv - a tool for creating an isolated environment for Python. We will use
virtualenv to create an isolated environment for our application.
sudo easy_install virtualenv
With our fresh copy of virtualdev, we can go ahead and set up a new virtual environment:
mkdir ~/virtualenvs
virtualenv ~/virtualenvs/mysite
We created a virtualenvs directory in our home folder and inside it we created a virtual environment called mysite. Now let's start using it and install
pip to easily install Python packages:
source ~/virtualenvs/mysite/bin/activate
easy_install pip
Now we need to make sure that we have the
Flup package
installed . This is a set of useful tools for working with WCGI applications, including an adapter to turn a WSGI application into FastCGI (both SCGI and AJP ... but this is beyond the scope of this article). Django requires Flup to be installed before you can use the runfcg control command. Using pip we can easily install it:
pip install flup
If we want to use database adapters, graphic libraries or xml parsers installed along the Python system path, we need to make sure that they are accessible from our virtual environment. To do this, we add the .pth file to the site-packages virtual environment directory:
echo "/usr/lib/python2.6/dist-packages/" > ~/virtualenvs/mysite/lib/python2.6/site-packages/fix.pth
The next step is to clone the Django code on our server (obviously,
git
can be replaced with
mercurial, svn
or even
rsync
):
git clone github.com/myusername/mysite.git
If your project has a
pip requirements file, you can now use it:
pip install -U -r mysite/requirements.txt
Or, if you do not have the
requirements file
, you can install the dependencies manually. For example:
pip install -U Django simplejson python-memcached
Choosing options for our FastCGI server
Wow We have come a long way in setting up our system and have not even told us about the FastCGI part yet. Do not worry, we are ready to do it now. Let's decide what options we want when we run our FastCGI server.
The first choice that needs to be made is which parallelization method we want to use:
- threaded:
Execution of streaming server in one process for all HTTP requests. This saves a lot of memory, but all threads run into the problem of one Global Interpreter Lock (GIL). This means that performance may be limited by processor-intensive processing. Note that I / O operations occur outside of the GIL, so intensive loading with I / O operations does not rest on the GIL problem. Also, some python extensions were not supposed to be thread safe, which means that they cannot be used with this contention method.
- prefork:
Execution of a branching server of a process generating a pool, each with its own copy of a jung and a python loaded into memory. This means that more memory will be used, but there are no aforementioned problems with GIL or thread-safety.
Let's assume that we are interested in FastCGI, because we have a server with a small memory size. Since the prefork method will use more memory, we will therefore choose the threaded method.
Now we will select several options that tell the server how to act under load:
- minspare:
What is the minimum number of processes / threads the server will support ready and waiting for future requests?
- maxspare:
What is the maximum number of processes / threads the server will support ready and waiting for future requests?
- maxrequests (only for prefork method):
How many requests each process will serve before it is killed and restarted. To prevent memory leaks, until it becomes a problem. It is a good idea to set this option.
- maxchildren (only for prefork method):
How many child processes can support queries at any given time?
Our server runs on a small VPS with 265 MB of memory, so we will choose very modest settings: 2 for
minspare
, 4 for
maxspare
, 6 for
maxchildren
and 500 for
maxrequests
.
At the end we choose our last few settings:
- host
Host name (hostname), which will listen to incoming connections?
- port
On which port to listen to incoming connections?
- pidfile
When the FastCGI server starts, it creates a file with its process ID. This process ID is the pid of the main thread / process. This is a process that will support OS signals, such as SIGHUP . This option determines the location of this file.
After we have made our choices, we can start the server by running the
runfcgi
command:
python manage.py runfcgi method=threaded host=127.0.0.1 port=8080 pidfile=mysite.pid minspare=4 maxspare=30 daemonize=false
Notice that we added the
daemonize=false
flag
daemonize=false
. It should always be installed (according to the author, skipping this option is a miscalculation in the runfcgi command). Also note that the result of running this command will create a
mysite.pid
file in our project directory, so it's a good idea to make sure that your version control system ignores this file.
Now that we have verified that our FastCGI server is running correctly, let's stop it and proceed to the next step: use
daemontools
to run this command and keep the server running all the time in the background.
Daemontools runs our fastcgi server
Daemontools will look in all the subdirectories in the / etc / service directory and in each of them it will look for an executable file called run. If he finds such a file, he launches it and restarts it if he dies. So let's create a mysite directory:
sudo mkdir /etc/service/mysite
Now, let's make a small script that runs our fastcgi server. Use your favorite text editor to write this text in
/etc/service/mysite/run
:
#!/usr/bin/env bash
source /home/mysite/virtualenvs/mysite/bin/activate
cd /home/mysite/mysite
exec envuidgid mysite python manage.py runfcgi method=threaded host=127.0.0.1 port=8080 pidfile=mysite.pid minspare=4 maxspare=30 daemonize=false
There is nothing tricky here. First we check that we are in the right virtual environment (virtualenv), then we change the current directory to
mysite
and then we run the
runfcgi
command, which we discussed earlier.
envuidgid mysite
just makes sure that the following command is executed under the
mysite
user, instead of
root
.
The script must be executable for daemontools to recognize it, so let's execute the following command:
sudo chmod +x /etc/service/mysite/run
Now we can verify that it is executed using the
svstat
command:
sudo svstat /etc/service/mysite/
The result should look something like this:
/etc/service/mysite/: up (pid 3610) 33 seconds
This means that the process is up, it is assigned a process id = 3610 and it worked for 33 seconds. You can use the
svc
command to stop the process:
sudo svc -d /etc/service/mysite/
Then, if you run svstat again, you will get something like this:
/etc/service/mysite/: down 4 seconds, normally up
To bring the process back, simply run:
sudo svc -u /etc/service/mysite/
A full list of svc commands can be found
online - this is a very good source if you are going to dive deeper into
daemontools
.
Configuring Nginx to work with our server
We are close to the finish line. All we have to do is configure nginx to talk with our FastCGI server, get answers from it and send them to the user.
Ubuntu comes with a useful
/etc/nginx/fastcgi_params
file. Unfortunately, it is not quite correct. It encodes the
SCRIPT_NAME
parameter, but what our server really wants is
PATH_INFO
. You can search and replace or copy the contents below into the
/etc/nginx/fastcgi_params
file:
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
Now we will create a definition for our site. Using a text editor, let's create the file
/etc/nginx/sites-available/mysite
with the following content:
server {
listen 80;
server_name mysite.com www.mysite.com;
access_log /var/log/nginx/mysite.access.log;
location /media {
autoindex on;
index index.html;
root /home/mysite/mysite;
break;
}
location / {
include /etc/nginx/fastcgi_params;
fastcgi_pass 127.0.0.1:8080;
break;
}
}
He says listen to port 80 (standard for HTTP) for
mysite.com http://www.mysite.com/
mysite.com http://www.mysite.com/
. Requests for / media should be processed immediately from disk in the
/home/mysite/mysite/media
directory. And the most important: all other requests will be transmitted via FastCGI to our server.
Now let's connect it via symlink:
sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite
Finally, restart nginx for the new settings to take effect:
sudo /etc/init.d/nginx restart
Conclusion
We set up a minimal server using nginx to serve media files at unbelievable speed and a clean Python FastCGI server to serve dynamic requests without any intermediate layers between them. Using daemontools we have full control over the FastCGI process and can stop it, restart or change its settings at any time.
A really interesting thing - just a few small
tweaks are
enough , and the same stack could be used for
gunicorn ,
spawning , or
paste solutions. Instead of using fastcgi_pass, we could use proxy_pass. We could still use daemontools to keep our process running and in control. Almost every step of this article will remain the same.
This is a very viable alternative to the often imposed stack from Apache / mod_wsgi, and I hope after reading this article more people will see it as a method of deploying your site to django.
About the author of the article
Eric Florenzano is a software developer. He currently lives in San Francisco and works for Mochi Media. At the moment he has been participating in the Django and Pinax community for several years. He is currently co-founder of Django Dose, podcasts about everything related to Django. From time to time he blogs at
www.eflorenzano.com about python, non-relational databases and other interesting topics.