Продолжим, пожалуй, разговор про конфиги (часть 1, часть 2.1, часть 2.2).
На этот раз отвлечёмся от теории и перейдём к практике. Чтобы нам такое законфигурировать?
Законфигурируем, пожалуй, какую-нибудь конфигурацию :) Допустим, конфигурацию веб-сервера и пусть сервером этим будет Nginx.
Задача более конкретно:
- Разрабатываем сайт example.ru
- Я разрабатываю в своей локальной версии — go.example.local, а другой разработчик в своей — hugo.example.local + ещё верстальщик с дизайнером
- Есть у нас общая локальная версия example.local на локальном сервере.
- И есть тестовый поддомен test.example.ru, на котором заказчик проверяет последнии фишки, перед тем, как их зальют собственно на example.ru
- test.example.ru находится в открытом доступе и его следует закрыть хотя бы с помощью htpasswd
- Все загружаемые изображения лежат на поддомене «img.*», то есть img.example.ru, img.go.example.local и т.д.
Итак, у нас уже 7 версий сайта. Каждая на своём хосте. И для каждого хоста нужно иметь nginx-конфиг. Все конфиги имеют одинаковую структуру, но отличаются частностями.
Что мы будем их 7 раз копипастить и корректировать? А любое изменение в структуре вручную в каждую версию вносить? К чёрту! Давайте всё автоматизируем.
Шаблон
Для начала нарисуем шаблон конфига:
server { listen {{ listen }}; server_name {{ server_name }}; root {{ root }}; {{ IF htpasswd }} # если надо закрыть от посторонних глаз - закрываем auth_basic "Password, please!"; auth_basic_user_file {{ htpasswd }}; {{ END }} # К картинкам и стилям доступ обычный location ~* ^/(i|css|js)/ { expires 7d; } # К robots.txt также location = /robots.txt { } # Всё остальное перехватываем на файл index.php location / { fastcgi_pass {{ fastcgi_pass }}; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root/index.php; fastcgi_read_timeout 600; } } # Upload-поддомен server { listen {{ listen_upload }}; server_name {{ server_name_upload }}; root {{ root_upload }}; location / { } } |
В примере мы воспользовались синтаксисом шаблонизатора Blitz. Вообще можно хоть на Smarty, хоть на чём угодно.
Параметры
Теперь вспомним, что мы говорили про платформы. У нас получается 7 конечных платформ (4 у разработчиков + local + test + рабочий сайт). Наследуются они от базовой конфигурации. Раздел этой базовой конфигурации, отвечающей за сервер, сейчас и набросаем (всё что NULL — требует переопределения для каждой платформы):
return array( 'listen' => 80, // обычно у всех будет 80-й порт 'server_name' => null, 'root' => null, 'htpasswd' => false, // у большинства htpasswd не нужен 'fastcgi_pass' => 'php-fpm', // у большинства PHP-FPM, но может быть, например, SPAWN-FCGI 'listen_upload' => 80, 'server_name_upload' => null, 'root_upload' => null, ); |
А теперь определяем настройки конкретной платформы (например, для go.example.local):
return array( 'server_name' => 'go.example.local', 'root' => '/home/go/go.example.local/www', 'server_name_upload' => 'img.go.example.local', 'root_upload' => '/home/go/go.example.local/img', ); |
Для test.example.ru прописываем путь до htpasswd.
Генерация config-файла
Теперь осталось написать скриптик, который берёт итоговую конфигурацию, берёт шаблон, пропускает это всё через шаблонизатор и сохраняет результат в файлик.
Остаётся запустить этот скриптик в нужный момент, скопировать результирующий файл в каталог с конфигами nginx’а и перезапустить сервер. Можно повесить это всё на хук в системе контроля версий (не говорите только, что при таком количестве народа её нет).
Что и файл каждый раз копировать лениво? Автоматически скопировать в каталог nginx’а не выйдет — прав скорее всего нет. Поставить права на запись — некошерно как-то, да и всё своё лучше носить с собой.
Поэтому создаём у себя каталог, например, local
, игнорируем в системе контроля версий и все подобные генерируемые файлы храним там. В nginx’е же делаем inclide всех подобных файлов или делаем на них символические ссылки.
Мы избавились от лишней ручной работы. Мы молодцы.
Промежуточные платформы
Вспомним теперь про промежуточные платформы.
У нас два ярко выраженных типа платформ:
- Dev: разработчики и example.local. Для пущего объединения, предположим что все они работают на одном сервере, а разработчики вносят изменения, подключившись через samb’у.
- Prod: это, конечно, рабочий сайт. А ещё — test.example.ru — он лежит на том же сервере, должен работать в том же окружении и с теми же настройками, чтобы быть максимально приближенном к конечному результату.
Возьмём и сделаем эти две промежуточные платформы.
Зачем дизайнеру и верстальщику отдельные img-поддомены? Зачем им заполнять их тестовыми данными, а в случае изменения формата переконверчивать их. Им только вывод настраивать надо. Сделаем им один и тот же поддомен вместе с example.local, и только у разработчиков переопределим его.
return array( 'server_name_upload' => 'img.go.example.local', 'root_upload' => '/home/go/go.example.local/img', ); |
fastcgi_pass также на всём сервере будет один.
Шаблоны параметров
Заметим ещё несколько закономерностей: имя поддомена обычно имеет вид «img. + хост», порт поддомена обычно тот же, что и у основного хоста. Хосты разработчиков располагаются на поддоменах одного домена и пути к ним обычно имеют один формат. Мы избавились от множества рутины, но по прежнему копипсинтаксисомастим. Возьмём и впихнём Blitz ещё и в параметры:
/* Базовая конфигурация */ return array( 'base_server_name' => null, 'prefix_server_name' => null, 'www_home' => null, 'prefix_upload' => 'img.', 'listen' => 80, 'server_name' => '{{ prefix_server_name }}{{ base_server_name }}', 'project_home' => '{{ www_home }}/{{ server_name }}', 'root' => '{{ project_home }}/www', 'htpasswd' => false, 'fastcgi_pass' => 'php-fpm', 'listen_upload' => '{{ listen }}', 'server_name_upload' => '{{ prefix_upload }}{{ server_name }}', 'root_upload' => '{{ project_home }}/img', ); |
Теперь конфигурация DEV-платформ:
return array( 'base_server_name' => 'example.local', ); |
И конфигурация платформы разработчика go:
return array( 'prefix_server_name' => 'go.', 'www_home' => '/home/go', ); |
И после слияния конфигов плучается:
array( 'base_server_name' => 'example.local', 'prefix_server_name' => 'go.', 'www_home' => '/home/go/', 'prefix_upload' => 'img.', 'listen' => 80, 'server_name' => '{{ prefix_server_name }}{{ base_server_name }}', 'project_home' => '{{ www_home }}/{{ server_name }}', 'root' => '{{ project_home }}/www', 'htpasswd' => false, 'fastcgi_pass' => 'php-fpm', 'listen_upload' => '{{ listen }}', 'server_name_upload' => '{{ prefix_upload }}{{ server_name }}', 'root_upload' => '{{ project_home }}/img', ); |
В скриптике же генерации nginx-конфига просто сперва пропускаем каждый параметр через шаблонизатор, передавая каждый раз в качестве переменных сам этот массив. И в итоге получаем:
array( 'base_server_name' => 'example.local', 'prefix_server_name' => 'go.', 'www_home' => '/home/go/', 'prefix_upload' => 'img.', 'listen' => 80, 'server_name' => 'go.example.local', // {{ prefix_server_name }}{{ base_server_name }} 'project_home' => '/home/go/go.example.local', // {{ www_home }}/{{ server_name }} 'root' => '/home/go/go.example.local/www'. // {{ project_home }}/www 'htpasswd' => false, 'fastcgi_pass' => 'php-fpm', 'listen_upload' => 80, // {{ listen }} 'server_name_upload' => 'img.go.example.local', // {{ prefix_upload }}{{ server_name }} 'root_upload' => '/home/go/go.example.local/img // {{ project_home }}/img ); |
Ну и сам итоговый конфиг:
server { listen 80; server_name go.example.local; root /home/go/go.example.local/www; # К картинкам и стилям доступ обычный location ~* ^/(i|css|js)/ { expires 7d; } # К robots.txt также location = /robots.txt { } # Всё остальное перехватываем на файл index.php location / { fastcgi_pass php-fpm; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root/index.php; fastcgi_read_timeout 600; } } # Upload-поддомен server { listen 80; server_name img.go.example.local; root /home/go/go.example.local/img; location / { } } |
Другие сервисы
Какие там у нас ещё используются сервисы, требующие файла конфигурации? Sphinx? Ещё какие-то? Так тоже самое с ними.
Offtopic: Олег, хорошо бы тебе на этот блог добавить кнопочки закладочных сервисов. Только не говори, что ты пишешь для себя :)
artoodetoo, 19.02.2012, 10:31
artoodetoo, я простой гуманитарий, я даже не знаю, что такое закладочные сервисы.
vasa_c, 19.02.2012, 12:14