Командная разработка сайтов на Drupal 6

Эта статья посвящена техническим сторонам командной разработки сайтов на Drupal 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 дважды в сутки.

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

Пока проект прост и разработка находится в одних руках - все замечательно: быстро, гибко, управляемо. Как только начинается проект посложнее, в разработку включается еще несколько программистов, начинается проблема синхронизации изменений. Как вести распределенную разработку сайта на Drupal? Сейчас я намеренно упускаю из виду дизайнеров, верстальщиков. Их работа незначительно коррелирует с работой программистов, а на поздних стадиях так и вообще практически исключается. С кодом модулей и графической темы все понятно: ставим систему контроля версий (она же хранилище). В качестве хранилища я использую 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 хранятся (тесно связаны) в контентных таблицах.
Пожалуй всё. С удовольствием выслушаю критику данных методов и, возможно, у кого-то есть своё решение этих проблем.
Ваша оценка: Нет Средняя: 3.6 (91 голосов)