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

в 12:15, , рубрики: phing, php, yii, Блог компании «Alawar Entertainment», метки: , ,

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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js