Автоматизируем выкладку django-проектов на сервер

Настраивать VDS'ки для выкладки django-проектов довольно утомительно бывает, да и легко что-то забыть (т.к. делаешь это не каждый день). Гораздо лучше, когда этот процесс автоматизирован: с меньшими усилиями можно получить правильно настроенный проект и набор команд для работы с ним.

Существуют разные подходы к этому процессу: специфичные для питона (fabric, buildout) или неспецифичные (puppet, Chef, наборы shell-скриптов и т.д.).

Подход fabric - локально выполняемый скрипт ходит по ssh на сервер и выполняет там команды. Этот подход довольно прямолинеен и прост в отладке, тем и хорош (обзор на хабре). Из разнообразных команд fabric постепенно вырисовался велосипед под названием django-fab-deploy. Это набор fabric-скриптов, который умеет настраивать серверы под Debian Lenny, Squeeze или Ubuntu 10.10, а потом с минимальными усилиями разворачивать там django-проекты и управлять этими проектами в дальнейшем.

С выходом Debian Squeeze взялся за django-fab-deploy посерьезнее, поправил некоторые шероховатости и теперь, думаю, самое время об этом проекте рассказать. У проекта есть документация, тут будет краткий пересказ с лирическими отступлениями.

Общие принципы

  • проекты изолировны с помощью virtualenv;
  • зависимости управляются с помощью requirements-файлов pip;
  • все общение с сервером ведется по ssh и должно быть автоматизировано ( => повторяемо);

Серверная часть:

  • поддерживаются Debian Lenny, Debian Squeeze и Ubuntu 10.10;
  • способ запуска python-кода: apache + mod_wsgi + nginx перед ними;
  • на одном VPS/сервере может крутиться сколько угодно проектов, управляемых django-fab-deploy;
  • один проект может быть выложен на несколько серверов (в том числе с разными настройками).

Главными критериями для выбора способа развертывания были стабильность/надежность, и тут связке apache+mod_wsgi+nginx на момент написания статьи равных нет.

Note

На этом месте обычно начинается холивар. Если интересно - гляньте, например, http://blog.disqus.com/post/3879996850/scaling-disqus-at-pycon-2011

Проекту хорошо бы хранится в системе контроля версий (поддерживается mercurial и git). Имеется полузаглушка с загрузкой/распаковкой tar.gz на случай, если проект не в VCS; она работает, но в продакшн-режиме я ее не проверял (незачем было), и есть вероятность, что проявятся какие-нибудь особенности.

Автоматизируем развертывание

Ниже описан базовый способ развертывания сервера, если он по каким-то причинам не подходит, то не страшно: django-fab-deploy - просто набор скриптов; их можно использовать по частям, или какие-нибудь полезности подсмотреть, или отправить патч, или просто статью почитать.

Предполагается, что у нас есть чистый Debian-сервер (можно и не "чистый", об этом позже), настроен ssh-доступ по публичному ключу к root.

Warning

Пожалуйста, не используйте VPS на OpenVZ, т.к. на них ограничивается VIRT вместо RSS и поэтому куча софта (в том числе апач и mysql+innodb) под OpenVZ ест гораздо больше памяти, чем под xen/kvm (в 10 раз - да легко).

Подготовка проекта

Джанго-проекты и так часто используют какую-нибудь разновидность трюка с 'local_settings.py', лежат в системе контроля версий, в репозитории имеют файлик с pip-зависимостями и папку с настройками для веб-сервера. Ничего кроме этого django-fab-deploy, по сути, и не требует. Дальше - детали.

  1. Устанавливаем django-fab-deploy и его зависимости:

    pip install django-fab-deploy
    pip install jinja2
    pip install "fabric >= 1.0"
    
  2. Создаем файл fabfile.py в корне проекта. В этом файле должна быть одна или несколько функций, настраивающих параметры сервера. Ну и т.к. это обычный fabric-скрипт, там могут быть любые другие (специфичные для проекта) команды. Пример:

    # my_project/fabfile.py
    from fab_deploy import *
    
    def my_site():
        env.hosts = ['my_site@example.com']
        env.conf = dict(
            DB_PASSWORD = 'password',
            DB_USER = 'foo',
            PROCESSES = 2,
    
            # раскомментировать, если проект не хранится в системе контроля
            # версий или хранится в git (по умолчанию - 'hg')
            # VCS = 'none', # 'git'
        )
        update_env()
    
    my_site()
    

    Обратите внимание на env.hosts. Тут есть фишка: укажите в качестве пользователя (my_site) название своего проекта (оно должно быть правильным названием переменной в python - без тире и пробелов). Не страшно, если такого пользователя нет, django-fab-deploy сможет его создать и настроить ssh-доступ позже. В любом случае, иметь отдельного пользователя для каждого проекта - хорошая идея.

    В DB_USER тоже можно указать несуществующего пользователя, в django-fab-deploy есть команда, которая сможет создать пользователя и настроить для него права.

    В env.conf применяются некоторые соглашения для упрощения всего. Например, название БД (DB_NAME) по умолчанию совпадает с названием экземпляра проекта (INSTANCE_NAME), которое, в свою очередь, совпадает с пользователем из env.hosts.

    Чтобы узнать больше о том, что и как можно настраивать тут, см. документацию по fabfile api.

  3. Создадим шаблоны настроек для веб-сервера и т.д. :

    django-fab-deploy config_templates
    

    Появится папка config_templates в корне проекта, там лежат шаблоны настроек. Проекты бывают разные, и часто настройкам потребуются правки - как минимум бегло прочтите их.

    В шаблонах настроек можно использовать переменные, заключенные в {{ }} (шаблонизатор тут - jinja2). Эти переменные будут заменены на значения из словаря env.conf при выкладке шаблонов на сервер.

  4. Создайте config.server.py в корне проекта. Этот файл станет файлом config.py на сервере. Пример:

    # my_project/config.server.py
    
    DEBUG = False
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': '{{ DB_NAME }}',
            'USER': '{{ DB_USER }}',
            'PASSWORD': '{{ DB_PASSWORD }}',
        }
    }
    

    Создайте также config.py с локальными настройками для разработки. Импортируйте config.py в settings.py:

    # Django settings for my_project project.
    # ...
    from config import *
    # ...
    

    Трюк широко известный, часто config.py называют также local_settings.py. Этот файл должен быть добавлен в игнорируемые в VCS (если VCS используется).

    Как можно было заметить, config.server.py - тоже шаблон (как и файлы из папки config_templates), в него тоже будут подставляться переменные из env.conf.

  5. Создайте папку 'reqs' в корне проекта. Пример такой папки можно получить, выполнив следующую команду из корня проекта:

    django-fab-deploy example_reqs
    

    В этой папке должны лежать текстовые файлы с pip-зависимостями. Один файл имеет особое значение: reqs/all.txt. Это главный файл с зависимостями, все пакеты оттуда будут установлены при развертывании. Там можно или все зависимости просто перечислить, или (лучше) подключать другие файлы с зависимостями через опцию “-r”.

    Есть еще команда django-fab-deploy generate_reqs, которая создает папку reqs с одним файлом all.txt, в котором перечислены все установленные в локальном окружении питоньи пакеты (то, что показывает pip freeze).

    Note

    django-fab-deploy поддерживает и другие варианты работы с настройками (например, 1 файл с зависимостями). См. документацию.

После того, как шаги 1-5 выполнены, проект должен выглядеть как-то так:

my_project
    ...
    config_templates
        ...
    reqs
        all.txt
        ...

    fabfile.py
    config.py
    config.server.py
    settings.py
    manage.py

Если все так, то проект готов.

Note

django-fab-deploy не навязывает эту структуру проекта, после настройки можно использовать django-fab-deploy и для проектов с другой структурой.

Подготовка сервера

  1. Если в env.hosts был указан несуществующий пользователь, то создадим его и настроим ssh-доступ по ключу, вручную или вот так (понадобится файл с вашим публичным ключом):

    fab create_linux_account:"/home/kmike/.ssh/id_rsa.pub"
    

    Проверяем, что ssh работает:

    ssh my_site@example.com
    
  2. Настроим БД. Если БД уже настроена вручную (например, проект уже работает), то ничего делать не нужно. django-fab-deploy сейчас умеет устанавливать mysql, создавать пользователя и создавать пустую базу под проект:

    fab mysql_install
    fab mysql_create_user
    fab mysql_create_db
    

    mysql_install ничего не делает, если mysql уже установлен на сервер. Если mysql не установлен, то он устанавливается, а пароль для mysql-пользователя root выставляется в env.conf['DB_PASSWORD'].

    mysql_create_user создает пользователя mysql с именем env.conf['DB_USER']. mysql_create_db создает пустую базу с названием env.conf['DB_NAME'] (в нашем примере это будет имя пользователя из env.hosts).

    Note

    Если используется не mysql, то лучше сейчас все руками сделать. А еще лучше - автоматизировать (написать fabric-скрипт для этого) и отправить патч в django-fab-deploy.

  3. Все, сервер готов. Можно скрестить пальцы и запустить команду из корня проекта:

    fab full_deploy
    

Если все было сделано правильно, сайт должен заработать.

Эта команда:

  • установит необходимые системные пакеты
  • создаст virtualenv и установит туда питоньи зависимости
  • настроит апач и nginx
  • загрузит проект на сервер
  • выполнит syncdb и migrate

Исходные коды будут лежать в ~/src/<INSTANCE_NAME>, virtualenv будет помещен в ~/envs/<INSTANCE_NAME>.

django-fab-deploy отключит сайты "default" для апача и nginx'а, а также станет командовать апачевским ports.conf (апач больше не будет слушать 80 порт). Если на сервере крутились другие сайты под апачем, то они станут недоступными из-за этого. Если наружу торчал только nginx, то все должно быть хорошо - django-fab-deploy ничего хитрого с сервером не делает.

Управление сервером

Залить изменения на сервер и применить их: fab push.

Еще пример (выложить изменения на сервер prod, при этом обновить зависимости и выполнить миграции): fab prod push:pip_update,migrate

Обновить настройки веб-сервера: fab setup_web_server Обновить настройки джанги (config.server.py): fab update_django_config

Полный список команд можно найти в документации. Если хочется чего-то более высокоуровнего (в духе - запустил fab redeploy и все сразу обновилось, и код, и настройки, и зависимости, и миграции выполнились) - можно легко написать свою fab-команду как обертку над базовыми командами. Если команда push делает слишком много (она, например, запускает тесты по умолчанию), то не стесняйтесь - гляньте на код и напишите в своем fabfile.py более подходящую версию. Если считаете какой-то такой эксперимент полезным и удачным - открывайте тикет в баг-трекере.

Аналоги

Наиболее близкий аналог django-fab-deploy - woven. Судя по всему, тоже отличная штука. Woven ориентирован на Ubuntu, Debian поддерживается по принципу "вроде должно работать с небольшими изменениями, но никто не знает точно". Начинали мы делать все примерно в одно и то же время, там сначала была какая-то дичь на классах, потом они все упростили. Продвинулись они подальше. С другой стороны, в django-fab-deploy в несколько раз меньше исходного кода, он поменьше и попроще, и останется таким.

Похожие проекты много кто делает. По гитхабу/битбакету поискал недавно, и в djangopackages еще 11 штук аналогов добавил (разной степени проработанности), можно тут глянуть сравнительную табличку: http://djangopackages.com/grids/g/deployment/

Ссылки

Актуальную документацию всегда можно найти тут: http://packages.python.org/django-fab-deploy/

Репозиторий с исходным кодом и баг-трекером: https://bitbucket.org/kmike/django-fab-deploy/

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

Описание в статье относится к версии 0.7.1 и может устареть; читайте, по возможности, документацию.

Не обязательно пользоваться именно django-fab-deploy, можно писать свои скрипты с 0, использовать woven, buildout или что угодно. Только все же перед тем, как писать все с 0, лучше, понятно, сделать "домашнюю работу" и посмотреть, как реализованы существующие проекты.

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

Note

На Хабре (но для версии 0.4): http://habrahabr.ru/blogs/django/113636/

blog comments powered by Disqus