Структура конфигов на сайтах Алавар

от автора

Всем привет!
Сайты 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/ 	…  

Итого

Используемая нами структура конфигов позволила:

  • легко разворачивать и конфигурировать новые сайты
  • автоматизировать деплой проекта
  • легко разворачивать проект в различных окружениях
  • иметь возможность отследить изменения в конфигурационных файлах

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

ссылка на оригинал статьи http://habrahabr.ru/company/alawar/blog/186458/


Комментарии

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

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