Django Framework / Виртуальный хостинг Django, модель N

в 14:01, , рубрики: django, nginx, virtualenv, запуск, метки: , , ,

Существует множество способов разворачивания Django-приложений в *nix-среде. Не буду претендовать на оригинальность, просто поделюсь самым-самым-самым-самым своим.

Вводные условия

Предпосылки:

  • Один клиент (заказчик) — один юзер в системе на сервере.
  • Все проекты клиента — в одной файловой иерархии.
  • Virtualenv — это хорошо и must use.
  • Ftp — зло, используем современные средства (sftp).
  • Количество файлов для управления проектами должно быть сведено к минимуму.

Используемый софт:

  • nginx
  • uwsgi
  • cron
  • virtualenv
  • openssh

И нефиг тут шариться

Часто бывает так, что наряду с разработчиками к файловому дереву своих проектов желают иметь доступ также и пользователи/клиенты/заказчики (называть можно как угодно), т.е. «почти» сторонние люди. А значит ограничить сторонних пользователей в возможности шариться по дискам сервера — неплохая идея. На первый взгляд приходит идея ограничить пользователя своей домашней директорией. Не стоит поддаваться этому порыву, ведь в своей домашней директории пользователь царь и бог, и одно неверное движение и ты отец может привести к неработоспособности проекта. Поэтому пойдем на хитрость и создадим следующую иерархию:

/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 -- директория с проектами еще одного клиента 

домашняя директория пользователя «client» имеет права client:client, остальные директории, если это особо не оговорено, root:client и chmod 750 соответвтенно. В группу client также надо включить пользователя nginx для того, чтобы nginx имел право доступа к статическим файлам внутри проекта. Также в системе создаем группу sftponly в которую включаем клиентов. В конфиг sshd (/etc/ssh/sshd_config) добавляем следующее:

Match Group sftponly         X11Forwarding no         AllowAgentForwarding no         AllowTcpForwarding no         ChrootDirectory /home/%u         ForceCommand internal-sftp 

и строчку

Subsystem      sftp    /usr/lib/misc/sftp-server

заменяем на

Subsystem       sftp    internal-sftp

В итоге мы получаем ситуацию, когда пользователь, зайдя на сервер по sftp (допустим используя winscp) попадает в свою домашнюю директорию, может подняться на один уровень вверх, шастать по своим проектам, смотреть логи. Однако подняться выше или удалить какую либо важную директорию он не в силах (chmod 750 однако).

nginx — как много в этом слове

Nginx — пожалуй лучший из http-серверов для отдачи статического контента и разруливания вопросов связанных с генерацией динамического. В нашем случае nginx должен быть собран вместе с uwsgi-модулем (идет в штатной поставке). При установке по умолчанию (по крайней мере в gentoo) nginx хранит свои конфиги в /etc/nginx. Не будем нарушать эту традицию, и даже наоборот попытаемся ей воспользоваться. В основной конфиг nginx'a (/etc/nginx/nginx.conf) в конец секции http нужно добавить строчку

include /etc/nginx/vhost.d/*.conf ;

в результате чего мы сможем иметь по одному конфиругационному файлу (/etc/nginx/vhost.d/project.conf) на проект.
Примерно по такому:

#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;     client_max_body_size 128m;     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 {         alias           /home/client/project/static;         expires       1d;     }     location / {         root            /home/client/project/www-root;         try_files       $uri    @django;     }     location @django {         uwsgi_pass   wsgi_cluster__project;         include        uwsgi_params;     } } 

Строчки, начинающиеся с #uwsgi# будут восприниматься nginx'ом как комментарии, однако стартовый скрипт nust (о котором речь ниже) смотрит на эти строчки, считая их директивами для себя.

nust — Nginx Uwsgi STarter

Когда-то, когда uwsgi только начал появляться, умел падать в процессе эксплуатации, не имел «императорского» режима, но уже был чертовски востребован — на скорую руку был написан скрипт, переписать который по нормальному руки так и не дошли по одной единственной причине — он работает. Итак, как он работает и что он делает.
Nust запускается по крону (crontab -e)

*/5  * * * *    nust -s -c /etc/nust.conf

со своим файлом конфигурации. Файл конфигурации имеет две секции, первая из которых относится непосредственно к nust'y и определяет пути и утилиты, которые им используются, а вторая — умолчания для 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= 

Проинсталлировать nust в систему можно следующим образом:

sudo pip install nust

В конфиге nginx'a с помощью «фигурного комментария» #uwsgi# можно переопределить следующие опции:

  • WORKERS — количество параллельных рабочих процессов
  • MODULE — имя стартуемого python-модуля (django_wsgi.py)
  • PRJ — имя проекта
  • PID — pid-файл дерева рабочих процессов (var/run/uwsgi.pid)
  • LOG — путь к лог-файлу uwsgi (var/log/uwsgi.log)
  • HARAKIRI — максимальное время выполнения запроса, сек.
  • MAX_REQ — количество обрабатываемых запросов, после которых рабочий процесс будет перезапущен

А вот эти опции являются обязательными:

  • USER — пользователь из под которого будет запущен проект
  • HOME — домашняя директория проекта (НЕ ПОЛЬЗОВАТЕЛЯ!)
  • VE — путь к виртуальному окружению (результату работы virtualenv)
  • SOCKET — где создавать файл-сокет. Если не задан, то будет взят из секции upstream.

Пути могут указываться тремя способами:

  • абсолютный путь, начинается со слэша
    (/tmp)
  • путь относительно домашней директории пользователя
    (~/gde-to/tam/)
  • путь относительно домашней директории проекта
    (var/run/uwsgi.pid)

Для старта какого-либо питоновского кода под uwsgi нужен так называемый модуль. Для запуска django подойдет следующий код, размещенный в 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() 

Прописывать запуск nust'a в стартовые скрипты системы нет необходимости, так как nust запускается по крону периодически. Помимо просто старта проектов nust следит за их состоянием (упал, не запущен, запущен, но не пингуется), а также за состоянием конфигов виртуальных хостов nginx'a. Если конфиг виртуального хоста был изменен, то соответствующий ему проект будет перезапущен.

Вместо послесловия

Ну вот собственно и все. Надеюсь не забыл ничего важного. Спасибо за внимание.

Автор: xenolog

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js