Django Shared Hosting, Model N

    There are many ways to deploy Django applications in a * nix environment. I will not pretend to be original, just share the most-most-very-most .

    Introductory conditions


    Prerequisites:
    • One client (customer) - one user in the system on the server.
    • All client projects are in one file hierarchy.
    • Virtualenv is good and must use.
    • Ftp is evil, we use modern means (sftp).
    • The number of files for project management should be minimized.
    Used software:
    • nginx
    • uwsgi
    • cron
    • virtualenv
    • openssh


    And nefig rummage around here


    It often happens that, along with developers, users / clients / customers also wish to have access to the file tree of their projects (you can call them anything you like), i.e. “Almost” outsiders. That means limiting third-party users to the possibility of rummaging around on the server’s disks is a good idea. At first glance, the idea comes to limit the user to his home directory. You should not give in to this impulse, because in your home directory the user is king and god, and one wrong move and you are the father can lead to the inoperability of the project. Therefore, let's go to the trick and create the following hierarchy:
    /home
      /client -- директория с проектами данного клиента
        /client -- домашняя директория пользователя "client"
        /another_project
        /project
          /www-root
          /static
          /app1
          /app2
          manage.py
          settings.py
          django_wsgi.py
          ...
        /var
          /log -- логи nginx'a, не забыть включить ее в logrotate
          /tmp -- chmod 770 -- временные файлы проекта
          /run -- тут будут пиды и сокеты
      /client2 -- директория с проектами другого клиента
      /client3 -- директория с проектами еще одного клиента
    

    the home directory of the user “client” has the rights client: client, the rest of the directories, unless otherwise specified, root: client and chmod 750, respectively. You must also include the nginx user in the client group so that nginx has the right to access static files inside the project. Also in the system, create a sftponly group in which we include clients. In the sshd config (/ etc / ssh / sshd_config) add the following:
    Match Group sftponly
            X11Forwarding no
            AllowAgentForwarding no
            AllowTcpForwarding no
            ChrootDirectory /home/%u
            ForceCommand internal-sftp
    
    and line
    Subsystem      sftp    /usr/lib/misc/sftp-server
    replace with
    Subsystem       sftp    internal-sftp

    As a result, we get a situation when a user logging on to the server via sftp (for example, using winscp) gets into his home directory, can go up one level, roam about his projects, watch the logs. However, he is unable to go up or delete any important directory (chmod 750 however).

    nginx - how much in this word


    Nginx is perhaps the best of http-servers for delivering static content and resolving issues related to the generation of dynamic. In our case, nginx should be assembled together with the uwsgi-module (comes as standard). When installed by default (at least in gentoo) nginx stores its configs in / etc / nginx. We will not break this tradition, and even vice versa try to use it. In the main nginx'a config (/etc/nginx/nginx.conf), add the line to the end of the http section
    include /etc/nginx/vhost.d/*.conf ;
    As a result, we can have one configuration file (/etc/nginx/vhost.d/project.conf) per project.
    Something like this:
    #uwsgi# USER    client
    #uwsgi# PRJ     cool_site.ru
    #uwsgi# HOME    ~/../project
    #uwsgi# VE      ~/.virtualenvs/project
    #uwsgi# SOCKET  ../var/run/uwsgi-project
    #uwsgi# LOG     ../var/log/uwsgi-project.log
    #uwsgi# PID     ../var/run/uwsgi-project.pid
    #uwsgi# WORKERS 2
    upstream wsgi_cluster__project {
        server unix:/home/client/var/run/uwsgi-project;
    }
    server {
        listen      80;
        server_name .cool_site.ru;
        charset     utf8;
        autoindex   off;
        root        /home/client/project/www-root;
        access_log  /home/client/var/log/nginx_access.log ;
        error_log   /home/client/var/log/nginx_error.log error;
        location /static {
            root           /home/client/project;
            expires       1d;
        }
        location / {
            try_files       $uri    @django;
        }
        location @django {
            uwsgi_pass   wsgi_cluster__project;
            include        uwsgi_params;
        }
    }
    
    Lines beginning with # uwsgi # will be interpreted by nginx as comments, however, the nust start script (discussed below) looks at these lines, considering them directives for themselves.

    nust - Nginx Uwsgi STarter


    Once, when uwsgi was just starting to appear, it knew how to fall during operation, it didn’t have an “imperial” regime, but it was already in hell of a demand - a script was written in haste, to rewrite it that normally didn’t reach for one single reason - it works. So how does it work and what does it do.
    Nust runs on the crown (crontab -e)
    */5  * * * *    nust -s -c /etc/nust.conf
    with your configuration file. The configuration file has two sections, the first of which relates directly to nust'y and defines the paths and utilities that it uses, and the second - the default for uwsgi.
    [nust]
    pstree = /usr/bin/pstree
    vhosts = /etc/nginx/vhost.d/*.conf
    uwsgi  = /usr/bin/uwsgi
    uwsgi_def_args  = --ini=/etc/nust.conf
    dbdir  = /var/run
    kill   = /bin/kill -s TERM
    kill_k9= /bin/kill -s KILL
    [uwsgi]
    master=
    disable-logging=
    vacuum=
    logfile-chown=
    chmod-socket=666
    catch-exceptions=
    memory-report=
    

    You can install nust into the system as follows:
    sudo pip install nust

    In the nginx config using the “curly comment” # uwsgi # you can override the following options:
    • WORKERS - number of parallel workflows
    • MODULE - name of the python module to start (django_wsgi.py)
    • PRJ - project name
    • PID - pid-file of the workflow tree (var / run / uwsgi.pid)
    • LOG - path to the uwsgi log file (var / log / uwsgi.log)
    • HARAKIRI - maximum query execution time, sec.
    • MAX_REQ - the number of processed requests, after which the workflow will be restarted
    But these options are required:
    • USER - user from which the project will be launched
    • HOME - project home directory (NOT USER!)
    • VE - the path to the virtual environment (the result of virtualenv)
    • SOCKET - where to create a socket file. If not specified, it will be taken from the upstream section.
    Paths can be specified in three ways:
    • absolute path starts with a slash
      (/ tmp)
    • path relative to the user's home directory
      (~ / gde-to / tam /)
    • path relative to the project home directory
      (var / run / uwsgi.pid)

    To start any python code under uwsgi, you need a so-called module. To run django, the following code is located in django_wsgi.py :
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    from __future__ import unicode_literals
    import os
    os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
    import django.core.handlers.wsgi
    application = django.core.handlers.wsgi.WSGIHandler()
    

    There is no need to register the launch of nust in the startup scripts of the system, since nust is launched periodically on the crown. In addition to just starting projects, nust monitors their status (crashes, is not running, is running, but does not respond), as well as the status of nginx virtual host config files. If the virtual host config has been changed, the corresponding project will be restarted.

    Instead of an afterword


    Well, that's all. I hope I have not forgotten anything important. Thanks for attention.

    Also popular now: