Как стать автором
Обновить

Распределенная разработка проекта на Drupal 6

Эта статья посвящена техническим сторонам командной разработки проектов на Друпал 6.
Обсуждаемые проблемы:

  • синхронизация изменений в базе данных результата работы нескольких разработчиков;
  • рассмотрена работа (и доработка) скрипта migraine для Drupal 6;
  • рассмотрена синхронизация изменений между dev-сервером и «боевым».




Введение

Я занимаюсь разработкой проектов на Drupal 6 чуть менее двух лет. Большой ли это срок? Если считать, что выполнено порядка 20 мелких проектов и пяток крупных и высокопосещаемых, на мой взгляд, срок большой. Ругать эту CMS (систему управления контентом) — грех (особенно на Хабре). Но об одной проблеме, с которой я столкнулся и местами даже удачно решил для себя, все же хочу рассказать. Я многократно поднимал вопрос командной разработки проектов на собрании друпаллеров: в Москве, в Киеве. Готового и стабильного решения в то время я не нашел.

Описанные здесь технологии предполагают работу Drupal 6 на Linux Ubuntu 10.04, с веб сервером Apache на dev-серверах и веб-сервером nginx на «боевом», в качестве БД используется MySQL. Так же, в эпизодах участвуют cron, rsync, migraine, SVN.

Резервные копии!

It-специалисты делятся на две категории: те кто уже делает бекапы и те, кто еще не делает. В нашем случае стоит написать простой скрипт и настроить запуск по расписанию, дважды в сутки: утром и в середине рабочего дня. Для этого я использую cron. Скрипт должен накапливать копии. То есть, новый бекап не должен перезаписывать предыдущий. Кроме того, должна быть возможность запустить этот скрипт не только из cron, но и по требованию, в моем случае из консоли. Очевидно, что копии должны храниться не на той же машине, что и файлы проекта. Не надо класть все свои яйца в одну корзину.

Мой скриптец для создания бекапа выглядит так:

#!/bin/sh

cd /sites/projectX.ru/
DATE=`date +%d.%m.%Y-%H.%M`
mkdir back-up
mysqldump -u user_name -pdb_password db_name -h db_host > ./back-up/db_site_common.sql

cp -R ./www/ ./back-up/www

tar -czf ./back-ups/projectX.ru-$DATE.tar.gz ./back-up
chmod -R 0777 ./back-up/
rm -rf ./back-up


Директория back-ups должна существовать и иметь права записи для пользователя, от которого запускается cron.
Скрипт запускается по событию cron дважды в сутки.

Несколько разработчиков

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

Как вести распределенную разработку? Сейчас я намеренно упускаю из виду дизайнеров, верстальщиков. Их работа незначительно коррелирует с работой программистов, а на поздних стадиях так и вообще практически исключается.

С кодом модулей и графической темы все понятно: ставим систему контроля версий (она же хранилище). В качестве хранилища я использую SVN. Каждому разработчику даём по веб-серверу (традиционно — виртуальному). Однако, специфика Друпала в том, что он хранит практически все свои настройки в базе данных (разве что settings.php лежит на файловой системе).

Как быть с базой данных? Как синхронизировать изменения нескольких разработчиков? Первое, что приходит на ум: использовать общую базу данных. Хорошее решение, но до поры, до времени, пока один из разработчиков не установит к себе код модуля, но в хранилище (SVN) еще не выложит. Модуль пропишется в общую базу, а код еще есть не у всех, потому что работа над настройкой еще не окончена. Эксперименты на общей базе данных могут принести много мусора, от которого тяжело будет избавиться.

Для этого случая я использую половинчатое решение: каждый разработчик имеет два веб-сервера:
  • «локальная связка» — локальный веб-сервер со своей базой данных;
  • «сетевая связка» — свой веб-сервер на dev-сервере с общей, «рабочей» базой данных.


"Локальная связка" используется для экспериментов, проверки и тестирования разнообразных решений. Вот тут и пригодятся бекапы: разработчик берет бекап, разворачивает на «локальной связке», проводит эксперименты по установке и настройке модуля, разрабатывает свой код, тестирует. После того, как решение проверено и устраивает, оно выкатывается вручную на dev-сервер, проверяется еще раз и выкладывается в хранилище.
"Сетевая связка" — общий полигон для рутинной работы.

Несколько серверов

Щекотливая ситуация наблюдается, когда обедненная версия проекта выкладывается в Мир, пользователи начинают с ней работать и создают огромное количество разнообразного контента.
С другой же стороны, на dev-серверах продолжает идти работа по наращиванию функционала и разработчики плодят изменения в огромных масштабах. Периодически необходимо все это синхронизировать: с «боевого» брать контент, с dev-серверов — логику и новый функционал. Простое зеркалирование здесь не подойдет. Мало того, «боевой» и dev скорее всего находятся на разных физических серверах и, вероятнее всего, по причинам безопасности, доступ к ним возможен лишь через ssh. Тут нам поможет rsync + migraine.

Rsync отвечает за синхронизацию файлов. Скрипт кидает файлы проекта с dev-сервера на «боевой» и пользовательские файлы с «боевого» на dev-сервер.
Скрипт выглядит вот так:

#!/bin/sh
rsync -aP --exclude=/usr/local/www/site/fast/projectX.ru/sites/default/settings.php \
--exclude=.svn /sites/projectX.ru/www/ ssh_user_name@prod_server_name:/home/_site/projectX.ru/


Ключи --exclude указывают, какие файлы не участвуют в синхронизации.

Migraine — скрипт, писанный на python, отвечает за хитрую синхронизацию базы данных. Я запускаю его вот таким скриптом:

#!/bin/sh

python migraine-d6.py --dump-test --config=migraine.ini
python migraine-d6.py --migrate-to-prod --config=migraine.ini

python migraine-d6.py --dump-prod --config=migraine.ini
python migraine-d6.py --migrate-to-test --config=migraine.ini


Настройки доступов к базам данных хранятся в migraine.ini

Все таблицы Друпала migraine делит на пять типов:

  1. config_tables — таблицы с настройками: список таблиц через пробел, которые полетят с dev-сервера на «боевой»;
  2. content_tables — таблицы с контентом: список таблиц, которые полетят с «боевого» на «dev-сервер»;
  3. temp_tables — временные таблицы: список таблиц, которые очищаются. Не копируются;
  4. cache_tables — таблицы кэша. Должны быть очищены;
  5. ignore_tables — таблицы, которые нужно игнорировать.


Что делает migraine в моей конфигурации?
  1. Делает бекапы как таблиц dev-сервера, так и «боевого»;
  2. закидывает все таблицы с настройками на «боевой», а таблицы с контентом на «dev-сервер».


Следует отметить, что первоначально migraine разрабатывался для Drupal 5. Полной адаптации под Drupal 6 я не нашел. Нашел лишь частное решение, которое чуток подпилил под себя. Так как решение было адаптировано, то пилить пришлось не напильником, а надфилем. Скрипт могу выложить по требованию.
Моя конфигурация проекта (ядро + модули) предполагает порядка 120 таблиц. Какие куда относятся — это отдельная тема для обсуждения. Тут надо включать свой мозг.

При работе, migraine должен иметь доступ сразу к двум базам данных. Как быть, если базы данных находятся на разных хостах? Подключаем базу с «боевого» при помощи ssh-туннеля. При этом, вешаем его на другой порт, например, на 3307:
#!/bin/sh

ssh -C -L3307:localhost:3306 prod_user_name@projectX.ru


База данных dev-сервера при этом висит на порту 3306. Migraine не умеет работать с портами, поэтому пришлось его чуток подпилить.

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

netstat -an | grep LISTEN

Результат должен выглядеть примерно вот так:
tcp 0 0 127.0.0.1:3307 0.0.0.0:* LISTEN

Проблемы синхронизации

  1. Различные настройки Drupal-кэширования на «боевом» и dev-сервере. Решение: либо каждый раз после синхронизации устанавливать ручками, либо автоматизировать при помощи curl. То есть, из командной строки зайти в админку, зайти на страницу настройки производительности и проставить нужные «галки», сохранить форму. Первый вариант непреемлем, когда-нибудь забудем включить кэш и долго будем сокрушаться, кто же тааак грузит «боевой» сервер. Человеческий фактор неизбежен. Второй вариант — лучше, но нетривиален, есть свои подводные камни. Опишу по запросу.
  2. Различные настройки settings.php на разных серверах. Решение — кладем «боевой» settings.php в определенное место и добавляем в скрипт синхронизации строчку подмены конфигурационного файла. Либо при помощи ключа --exclude говорим rsync'у, что этот файл перезатирать не надо.
  3. Таблиц в друпале очень много и не удается до конца понять, в какую категорию отнести. Как в анекдоте с мартышкой: «А мне разорваться что — ли?» Так и хочется поступить с некоторыми таблицами. Тут надо действовать хитрее.
  4. Таблицы смешанного типа. Migraine позволяет разделить таблицы на настроечные и контентные. Настроечные те, которые с dev-сервера копируются на «боевой», контентные — таблицы с контентом, копируются с «боевого» на dev-сервера. Однако, тут снова не все так просто. Например, настройки модуля webform хранятся (тесно связаны) в контентных таблицах.


Пожалуй всё.
С удовольствием выслушаю критику данных методов и, возможно, у кого-то есть своё решение этих проблем.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.