Django work flow (от создания до деплоя)

от автора

Речь пойдет о быстром создании и деплое новых проектов, подробнее о том, как нужно экономить свое время.

Мы хотим, что бы начало нового проекта было максимально простым и удобным, как и его последующий деплой. В лучшем случаем нам бы хотелось иметь 3 кнопки: начать новый проект, задеплоить и обновить.

Эта тема не новая и уже достаточно освещена в разных аспектах, я лишь покажу свой вариант.
Для комфортной разработки нам понадобится: PyCharm (ну или какой другой редактор), Python (куда без него), fabric, virtualenv, git и pip.

Как это происходит у меня

Кликаем на кнопочку:

Вводим название:

Я запускаю PyCharm, тыкаю на кнопочку Run, ввожу название нового проекта, после чего получаю готовую среду для начала работы. (за кулисами происходит следующее: создается папка проекта, где уже настроены конфиги PyCharma и git`а, создано виртуальное окружение (virtualenv), создан приватный git репозиторий на bitbucket.org, куда уже совершен коммит.

Дальше, мне остается только открыть папку проекта в новом окне PyCharm, написать код, и нажать кнопочку Run для деплоя, после чего радоваться, глядя на то, как это чудо само поднимется и настроится.

Продвинутый пользователь дальше может не читать, тк наверняка уже имеет подобные кнопки в своем арсенале, можно посмотреть лишь на результат

Структура проекта и его создание

Все просто до безобразия, мы пишем нужные нам скрипты используя fabric.

Все проекты у меня хранятся в одной папке _DJANGO_PROJ_ и имеют простую структуру, состоящую из 5 папок:
src — для исходников
venv — для виртуального окружения
static — для всей статики (collectstatic)
media — для меди файлов
db — для хранения БД

Кроме проектов в каталоге _DJANGO_PROJ_ есть папка deploy, в которой хранится fabfile.py скрипт, который и инициализирует новый проект, так же в ней находятся конфиги для деплоя и шаблон нового проекта.

Посмотрим подробнее на структуру папки src, поскольку все исходные коды проекта будут находиться в ней, она же будет git репозиторием.

В корне лежат: папка с настройками PyCharma и папка _project_, файлы: .gitignore (для git`a) и requirements.txt (зависимости проекта).

В _project_ находятся настройки проекта и все то, что специфично именно для данного проекта (шаблоны, статика, url.py). (подробнее о структуре Django проекта и requirements.txt) Каталог static в папке _project_ нужен для хранения статических файлов специфичных для данного проекта (например favicon), не путайте его с каталогом static в корне, который нужен для команды collectstatic.

Напишем простой fab скрипт, копирует структуру проекта из шаблонной директории и попутно создает git репозиторий и виртуальное окружение.

def init():     # все пути отсчитываются от директории fabfile.py файла.     env.lcwd = os.path.dirname(__file__)      # спариваем у пользователя название проекта (оно же может являться DNS именем для сайта)     prompt("project domain: ", "project",         validate="^([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$")     puts("create project: {0}".format(env.project))      with lcd('..'):         local('mkdir ' + env.project) # создаем директорию проекта         with lcd(env.project):             local('mkdir db src venv static media') # нужные нам папки             with lcd('src'):                 # Копируем инфраструктуру проекта из шаблонной директории                 local('cp -r ../../deploy/project/* .'.replace('/', os.path.sep))                 local_template_render('gitignore.txt', env, '.gitignore')                  # Инициализируем git                 local('git init')                 local('git add *')                 local('git commit -am "init"')              # Настраиваем виртуальное окружение             local('virtualenv --clear venv')             local('./venv/Scripts/activate && pip install --download-cache=../deploy/pip.cache \                 -r ./src/requirements.txt'.replace('/', os.path.sep))              # настраиваем Django             local('./venv/Scripts/activate && python ./src/manage.py syncdb --noinput && \                 python ./src/manage.py migrate --noinput'.replace('/', os.path.sep)) 

После выполнения данного fab скрипты мы получим заготовку для нового проекта проект, которая уже полностью готов к запуску.

Ну и добавим создание bitbucket проекта:

        if BITBUCKET_USER and BITBUCKET_PASSWORD and                 confirm_global('create private bitbucket repository?'):             env.bit_user=BITBUCKET_USER             env.bit_password=BITBUCKET_PASSWORD              import requests as r             rez = r.post('https://api.bitbucket.org/1.0/repositories/',                 data=dict(name=env.project, is_private=True),                 auth=BITBUCKET_AUTH,             )             puts('request status ok: {0}'.format(rez.ok))              if rez.ok:                 with lcd(env.project):                     # копируем скрипт для деплоя проекта с bitbucket.org                     local_template_render('fabfile.txt', env, 'fabs.py')                     with lcd('src'):                         local('git remote add origin https://{0}:{2}@bitbucket.org/{0}/{1}.git'                             .format(env.bit_user, env.project, env.bit_password))                         local('git push -u origin --all')   # to push changes for the first time 

(Ссылка на полную версию кода будет в конце статьи)

Не забывайте, что копировать нужно не все настройки Django, в них есть SECRET_KEY, который должен быть уникальным для каждого проекта.

Итак, создавать новый проект научились.
Дальше разбираемся с деплоем.

Деплой

Деплой будет происходить при помощи клонирования git репозитория и настройки всех нужных пакетов и окружения.
Мне нравится связка Ubuntu + nginx + uWSGI.
Тут уже все намного проще. На эту тему написано много статей, в частности на хабре: kmike, Nginx + uWSGI, DTemplate, Django и Fabric, Fabric

def deploy():     """     Функция для деплоя проекта с удаленного гит репозитория.      **Настройки `env`**     env.user - deploy user name (use for ssh)     env.password - deploy user password (use for ssh)     env.hosts - list deploy hosts (use for ssh)      env.domain - django site domain (DNS) use for:         - nginx settings         - uWSGI start settings         - project dir name      env.repository - ссылка на гит репозиторий, для клонирования папки src.      env.no_input_mode - делать все не задавая лишних вопросов.     """     # все пути отсчитываются от директории fabfile.py файла.     env.lcwd = os.path.dirname(__file__)      require('no_input_mode')      # Если no_input_mode == True переопределим функцию для задания вопросов,     # теперь она будет выдавать занчения     if env.no_input_mode:         def confirm_local(question, default=True):             puts(question)             puts("Use no_input_mode [default: {0}]".format("Y" if default else "N"))             return default          confirm = confirm_local     else:         confirm = confirm_global      # Запросим DNS для сайта, если нету.     validate="^([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$"     if not env.get("domain"):         if env.no_input_mode:             abort("Need set env.domain !")         else:             prompt("Project DNS url: ", "domain", env.get('domain_default', ''),                     validate=validate)     else:         if not re.findall(validate, env.domain):             abort("Invalid env.domain !")      # Запросим ссылку на git репозитарий     if not env.get("repository"):         if env.no_input_mode:             env.repository = env.repository_default         else:             prompt("Deploy from: ", "repository", env.get('repository_default', ''))      require('repository', 'domain')      puts("Deploy site: {0} \nFrom: {1}".format(env.domain, env.repository))     DOMAIN_WITHOUT_DOT = env.domain.replace('.', '_')      # Заполняем настройки     env.project_user = DOMAIN_WITHOUT_DOT     env.project_group = DOMAIN_WITHOUT_DOT     env.project_dir_name = DOMAIN_WITHOUT_DOT     env.root = posixpath.join(PROJECTS_ROOT, env.project_dir_name)      env.debug = True      # Тут уже начинается деплой     deb.packages(['git'])      # Используем юзера deploy для файлов проекта     if not fabtools.user.exists('deploy'):         fabtools.user.create('deploy', home=PROJECTS_ROOT, group='deploy', create_home=False,             system=True, shell='/bin/false', create_group_if_need=True)      # проверяем наличие директории для проектов     files.directory(PROJECTS_ROOT, use_sudo=True, owner='root', group='root', mode='755')     with cd(PROJECTS_ROOT):         # создаем директорию для кеширования pip пакетов         files.directory('.pip.cache', use_sudo=True, owner='deploy', group='deploy', mode='755')         pip_cache_dir = posixpath.join(PROJECTS_ROOT, '.pip.cache')          # создаем директорию для проекта         if is_dir(env.project_dir_name) and                 confirm("proj dir exist! abort ?", default=False):             return          files.directory(env.project_dir_name, use_sudo=True, owner='root', group='root',             mode='755')          # создаем нового пользователя для запуска uWSGi         if not fabtools.user.exists(env.project_user):             fabtools.user.create(env.project_user, home=env.root,                 group=env.project_group, create_home=False, system=True,                 shell='/bin/false', create_group_if_need=True)          # создаем инфраструктуру для проекта         with cd(env.project_dir_name):             # директория с исходниками             if not is_dir('src') or                     confirm("proj src exist! [rm all and re clone / git pull]?",                             default=False):                 files.directory('src', use_sudo=True, owner='deploy', group='deploy', mode='755')                 with cd('src'):                     sudo('rm -Rf .??* *')                     sudo('git clone %(repository)s .' % env, user='deploy')             else:                 with cd('src'):                     sudo('git pull', user='deploy')              # виртуальное окружение             if not is_dir('venv') or                     confirm("proj venv dir exist! [rm all and recreate / repeat install]?",                             default=False):                 files.directory('venv', use_sudo=True, owner='deploy', group='deploy', mode='755')                 with cd('venv'):                     sudo('rm -Rf .??* *')              python.virtualenv('venv', use_sudo=True, user='deploy', clear=True)             with fabtools.python.virtualenv('venv'):                 python.install_requirements('src/requirements.txt', use_mirrors=False,                         use_sudo=True, user='deploy', download_cache=pip_cache_dir)              # директории для логов, БД (используем sqlite), статики и медиа файлов.             files.directory('log', use_sudo=True, owner='root', group='root', mode='755')             files.directory('db', use_sudo=True, owner=env.project_user,                 group=env.project_group, mode='755')             files.directory('media', use_sudo=True, owner=env.project_user,                 group=env.project_group, mode='755')             files.directory('static', use_sudo=True, owner=env.project_user,                 group=env.project_group, mode='755')             sudo('chown -R ' + env.project_user + ':' + env.project_group +                 ' db* static* media*')              # инициализируем статику, БД             with fabtools.python.virtualenv('venv'):                 sudo('python src/manage.py collectstatic --noinput', user=env.project_user)                 sudo('python src/manage.py syncdb --noinput', user=env.project_user)                 sudo('python src/manage.py migrate --noinput', user=env.project_user)                 #sudo('python src/manage.py loaddata fixtures.json', user=env.project_user)              # Дальше идет настройка веб сервера nginx и uWSGI             # ------------------- #             # WEB SERVER SETTINGS #             # ------------------- #              # I`m use nginx <-> uWSGI <-> Django              nginx.server()             deb.packages(['uwsgi', 'uwsgi-plugin-python'])              # proj conf!             if not is_dir('conf') or confirm("proj conf dir exist! [backup and update? / skip]",                     default=False):                 files.directory('conf', use_sudo=True, owner='root', group='root', mode='755')                 with cd('conf'):                     local_conf_templates = os.path.join(os.path.dirname(__file__),                         'template', 'conf')                     uwsgi_conf = os.path.join(local_conf_templates, 'uwsgi.ini')                     nginx_conf = os.path.join(local_conf_templates, 'nginx.conf')                      sudo("rm -Rf *.back")                     sudo("ls -d *{.conf,.ini} | sed 's/.*$/mv -fu \"&\" \"\\0.back\"/' | sh")                     files.template_file('uwsgi.ini', template_source=uwsgi_conf, context=env,                         use_sudo=True, owner='root', group='root', mode='644')                     files.file('reload', use_sudo=True, owner='root', group='root')                     sudo('ln -sf $(pwd)/uwsgi.ini /etc/uwsgi/apps-enabled/' +                         env.project_dir_name + '.ini')                      files.template_file('nginx.conf', template_source=nginx_conf, context=env,                         use_sudo=True, owner='root', group='root', mode='644')                     sudo('ln -sf $(pwd)/nginx.conf /etc/nginx/sites-enabled/' +                         env.project_dir_name)              sudo('service nginx restart')             sudo('service uwsgi restart') 

Можно сказать еще про структуру каталогов с сайтами на deploy сервере, она почти полностью повторяет структуру каталогов для разработки, добавлены лишь каталоги log и conf.
log — каталог для хранения логов, в частности nginx и uWSGI
conf — каталог с настройками для nginx и uWSGI

Как это выглядит на сервере:

Итог

Мы получили-таки 2 кнопки, одну для создания нового проекта, вторую для деплоя.

Естественно, все вышеописанное является лишь примером, и, я не агитирую всех делать все именно так.
Так же хочу заметить, что универсального пути нет, поскольку процесс разработки и деплоя — это дело сугубо интимное (личное) и каждый сам решает для себя, как и почему (кто-то любит apache, кому-то нужен redis ..). Вы можете взять мой вариант и переделать его под себя.

Я так же отмечу, что не пытался построить слой абстракций над этим процессом, поскольку, еще раз повторю, что это дело сугубо личное, особенно деплой. Я настоятельно рекомендовал, хотя бы для деплоя пытаться самому написать скрипт, поскольку, иначе, ни о какой безопасности не может быть речи, уж слишком тут тонкий момент.

Но, конечно, можно пользоваться и моим решением.

Полный вариант решения

Мини HOW-TO

Итак, как все-таки всем этим пользоваться!?
(Предполагается что у вас установлен Python v2.* и pip. Так же замечу, что пользователям windows придется ручками поставить пакет fabric)

Клонируем решение в папку с нашими джанго проектами (например в cd ~ или cd %HOMEPATH%).

git clone https://bitbucket.org/pahaz/django-work-flow.git _DJANGO_PROJECTS_ 

Устанавливаем зависимости, если нужно:

pip install -r deploy/req.txt 

Редактируем шаблон для нового проекта _DJANGO_PROJECTS_/deploy/project.

Создаем директорию, куда будут кешироваться pip пакеты — _DJANGO_PROJECTS_/deploy/.pip.cache
Создаем файл с личными деплой настройками — fabsettings.py

Пример:

# conf for init BITBUCKET_USER = 'pahaz' BITBUCKET_PASSWORD = 'mypasswd'  # conf for deploy DEPLOY_DEFAULT_DOMAIN = 'developers.urfu.ru' DEPLOY_DEFAULT_REPOSITORY = 'https://pahaz@bitbucket.org/pahaz/developers.urfu.ru.git'  DEPLOY_USER = 'root'                            # change DEPLOY_PASSWORD = 'qwer'                        # change DEPLOY_HOSTS = ['192.168.174.131:22', ]         # change  

Запускаем файл init.sh.cmd, вводим название нового проекта, после продолжительного создания, нас спросят, хотим ли мы создать приватный bitbucket репозиторий. (отвечаем на свое усмотрение)

Все новый проект готов.

Открываем его директорию src в PyCharme и пишем код.

После чего можем запустить деплой (если настройки PyCharm`a правильно скопировались, то деплой нам доступен в один клик; ну или руками запускаем файлик fabs.py в корне проекта)

Быстрого создания проектов тебе %habrauser%.

PS. И да простят меня знатоки pep`a.

ссылка на оригинал статьи http://habrahabr.ru/post/165081/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *