Существует множество способов разворачивания 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