Всем привет!
Сайты Alawar — это сайты для русского, американского, европейских и других рынков, отдельные сайты для mobile-устройств, сайты партнерских программ и др. Все они развернуты на одном инстансе Yii, о чем мы уже писали в нашем блоге на хабре.
Сегодня я расскажу, как мы организовали хранение, структуру и управление конфигами наших сайтов, какие при этом получили преимущества. А также поведаю, как осуществляется деплой нашего проекта в различных окружениях.
Конфиги
Для осуществления настройки сайтов мы применили следующую структуру конфигов в Yii:
protected/config
/console
/config.php
/import.php
/cache.php
/log.php
…
/mobile
/config.php
/import.php
/cache.php
/log.php
…
/sites
/alawar.ru.php
/iphone.alawar.ru.php
/ipad.alawar.ru.php
/site.php
…
/test
/config.php
/import.php
…
/web
/config.php
/import.php
/log.php
…
/~server
/amqp.php
/crontab.txt
/db.php
/eauth.php
/mongo.php
/redis.php
/smsgate.php
/services.php
/comment.php
…
Все конфигурационные файлы были разбиты на категории согласно их назначению:
- web/config.php — конфиг, содержащий настройки и параметры общие для всех web-сайтов
<?php return array( 'preload' => array( 'log' ), //список подключаемых файлов 'import' => require(dirname(__FILE__) . '/import.php'), 'components' => array( … //параметры подключения к MySql 'db' => require(dirname(dirname(__FILE__)) . '/server/mysql.php'), //параметры подключения к Redis 'redis' => require(dirname(dirname(__FILE__)) . '/server/redis.php'), //параметры подключения к Mongo 'mongo' => require(dirname(dirname(__FILE__)) . '/server/mongo.php'), //настройки логгирования 'log' => require(dirname(dirname(__FILE__)) . '/web/log.php'), //настройки компонента комментариев 'comment' => require(dirname(dirname(__FILE__)) . '/server/comment.php'), //настройки RabbitMQ 'amqp' => require(dirname(dirname(__FILE__)) . '/server/amqp.php'), //настройки авторизации через соц. сети 'eauth' => require(dirname(dirname(__FILE__)) . '/server/eauth.php'), … ), 'params' => array( //параметры доступа к различным сторонним сервисам 'services' => require(dirname(dirname(__FILE__)) . '/server/services.php'), //параметры доступа к смс-шлюзу 'smsgate' => require(dirname(dirname(__FILE__)) . '/server/smsgate.php'), … ) );
- site/{site.ru}.php — итоговый конфиг для сайта {site.ru} (специфичные настройки + общий конфиг web/config.php):
return CMap::mergeArray( array( 'basePath' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '..'. DIRECTORY_SEPARATOR . '..', 'name' => 'Site', 'theme' => 'site', 'host' => 'site.ru', 'language' => 'ru', //модули site.ru 'modules' => array( … ), //модули site.ru 'controllerMap' => array( … ), //спецефичные компоненты для site.ru 'components' => array( … ), // application-level parameters that can be accessed // using Yii::app()->params['paramName'] 'params' => array( //runtime параметры //например: //Yii::app()->params['runtimeData']['css'] - путь к минифицированному css сайта //Yii::app()->params['runtimeData']['js'] - путь к минифицированному js сайта 'runtimeData' => @include(dirname(__FILE__).'/runtime/sites/site.ru.php'), 'adminEmail' => 'admin@site.ru', ), ), require(dirname(dirname(__FILE__)).'/web/config.php') );
Такой подход к формированию итогового конфига сайта позволяет легко подключать новые сайты и достаточно гибко их настраивать.
- console/config.php — конфиг для консольного приложения, по структуре схож с web/config.php, но он имеет свои импорты, настройки логирования, подключаемые компоненты и др.
- test/config.php — конфиг для тестового окружения
Особенностью структуры конфигов является то, что в protected/~server сосредоточены «сереверозависимые» параметры и настройки, которые хранятся в отдельных репозиториях под каждый сервер(~server — это всего лишь симлинка на чекаут одного из репозиториев). Такая структура позволяет легко, быстро и без костылей разворачивать проект в различном окружении.
Деплой
В данный момент у нас проект может быть развернут на 3-х серверах:
- dev-сервер — сервер, на котором ведется разработка
- test-сервер — сервер, на котором запускаются тесты
- prod-сервер — продакшн
Соответственно, под каждый сервер заведен свой репозиторий с конфигами:
- dev-config
- test-config
- prod-config
При развертывании проекта (мы это делаем средсвами jenkins и phing) мы просто указываем, какую ветку и какой репозиторий с конфигами поднять:
#развертывание ветки task-xx в dev-окружении
phing -Dbranch=task-xx -Dconfig=dev-config deploy
#развертывание проекта на продакшн сервере
phing -Dbranch=prod -Dconfig=prod-config deploy
Вот, что делает при этом phing:
<!-- Таск по развертыванию проекта -->
<target name="deploy" depends="-get-properties">
<!-- Путь к директроии, в которой развертывается проект -->
<mkdir dir="${deploy.path}" />
<!-- Путь к директроии, в которой будет расчекаучена ветка репозитория с кодом проекта -->
<mkdir dir="${deploy.path}/application" />
<!-- Путь к директроии, в которой будет расчекаучена репозиторий с конфигами -->
<mkdir dir="${deploy.path}/config" />
<echo msg="checkout application and config..." />
<!-- Чекаут ветки -->
<exec
command="bzr co ${bzr.branch.path} ./"
dir="${deploy.path}"
checkreturn="FALSE"
returnProperty="bzr.co.return"
outputProperty="bzr.co.out"
/>
<if>
<!-- Если ветки не существует, создаем её, пачкую от ветки транк -->
<equals arg1="${bzr.co.return}" arg2="3" />
<then>
<exec command="bzr co ${bzr.trunk.path} ./" dir="${deploy.path}/application" />
<exec command="bzr switch -b ${bzr.branch.path}" dir="${deploy.path}/application" />
</then>
</if>
<!-- Чекаут репозитория с конфигами -->
<exec command="bzr co ${bzr.config.path} ./" dir="${deploy.path}/config" />
<!-- Настройка прав доступа к папке runtime -->
<chmod file="${deploy.path}/application/protected/runtime" mode="0777" />
<!-- Создание симлинки на конфиги -->
<exec command="ln -s ${deploy.path}/config/server server" dir="${deploy.path}/application/protected/config/" level="info"/>
<!-- Создание симлинки на php error log -->
<exec command="ln -s ${php.error.log.path} phplog" dir="${deploy.path}/application/protected/runtime/" level="info"/>
<!-- Для каждого сайта генерация минифицированного css и js и прописывание путей к ним в protected/runtime/sites/{site.ru.php} -->
<exec command="php ${deploy.path}/application/protected/yiic deploy data=css" />
<exec command="php ${deploy.path}/application/protected/yiic deploy data=js" />
<!-- Генерация карты шардов redis и mysql -->
<exec command="php ${deploy.path}/application/protected/yiic deploy data=shardmap" />
</target>
Таким образом, после развертывания структура всего проекта становится следующей:
application/ #чекаут репозитория с кодом проекта
protected/
…
config/
…
~server/ #симлинка на config/server
…
…
public/
…
config/ #чекаут репозитория с конфигами
…
server/
…
Итого
Используемая нами структура конфигов позволила:
- легко разворачивать и конфигурировать новые сайты
- автоматизировать деплой проекта
- легко разворачивать проект в различных окружениях
- иметь возможность отследить изменения в конфигурационных файлах
Однако, конечно у подхода с различными репозиториями под конфиги есть свои минусы, основной из них — это синхронизация изменений. Приходится руками переносить изменения в одном репозитории во все другие с учетом настроек окружения.
Автор: nasedkin