Выкатка (или deployment) новых версий Web приложений имеет ряд трудностей, т.к. необходимо быстро и одновременно выполнять группы действий на разных серверах. Процесс обычно включает в себя обновление кода (php) и статики (js/css/картинки), изменение баз данных и настроек системы.
Когда-то давно, новые версии появлялись очень редко (раз в год или даже реже). Тогда происходили сложные и длительные процессы обновления, а пользователи получали сразу огромный пакет изменений. Такой трудоемкий процесс иногда уничтожал целые бизнесы.
Сейчас понятие новой версии минимизировано до малейших изменений. Динамика разработки современных приложений огромная, а выкатки обновлений могут происходит каждый день.
Поэтому к процессу выкатки добавился целый ряд требований:
Основные компоненты любой крупной Web системы – это фронтенды, бекенды, базы данных и сервера специального назначения (например, почтовые либо медиа-хранилища).
Фронтенды обычно выполняют две функции:
Таким образом, для обновления фронтенда необходимо загрузить новые файлы статики. После этого, выполнить минификацию css/js при необходимости.
На практике это обычно делают так:
Такой подход позволяет мгновенно выполнить перевод всех пользователей на новую версию.
Представим, что мы используем такую конфигурацию Web сервера:
server {
index index.html;
**root /production_a;**
}
Тогда после выкатки необходимо заменить директиву root на новый путь. Можно использовать простой php скрипт:
$config = file_get_contents('site.conf');
$current_version = strpos($config, ‘/production_a’) ? ‘a’ : ‘b’;
$new_version = $current_version == ‘a’ ? ‘b’ : ‘a’;
$config = str_replace(‘/production_’ . $current_version, ‘/production_’ . $new_version, $config);
file_put_contents(‘site.conf’, $config);
## Скрипт для последовательного переключения между папками
После обновления и подготовки кода достаточно будет вызывать этот скрипт. Он изменит текущую папку на соседнюю. Если в текущий момент рабочая папка “production_a”, выкатку делаем в “production_b” и переключаемся на нее. Если “production_b”, то выкатку делаем в “production_a”. Таким образом рабочая папка постоянно меняется, а соседняя всегда будет содержать предыдущую рабочую версию.
После изменения файла конфигурации обновляем настройки Nginx’a:
/etc/init.d/nginx reload
Выкатка бекендов во многом похожа на выкатку фронтендов.
На всех серверах необходимо обновить код во второстепенной папке (/production_b). После чего переключить конфигурацию Nginx’a на нужную папку:
server {
…
root **/production_a**;
index index.php;
location ~* .(php)$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
## Выделенную папку необходимо заменить на “production_b”
PHP использует различные кэши операционного кода, чтобы экономить на повторной интерпретации файлов. Поэтому, перед тем как переключать пользователей в новую папку, полезно сделать “разогрев” кэша.
Для этого достаточно иметь отдельный хост в Nginx, например:
server {
host lambda.ruhighload.com;
root **/production_b**;
index index.php;
location ~* .(php)$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
## Отдельный хост lambda.ruhighload.com для разогрева кэша новой версии
После этого открыть специальную страницу, которая просто подключит все файлы проекта (preload.php):
if ( $_GET['key'] != 12345 ) exit;
$it = new RecursiveDirectoryIterator(“/production_b”);
foreach(new RecursiveIteratorIterator($it) as $file)
{
if ( pathinfo($file, PATHINFO_EXTENSION) == ‘php’ ) include $file;
}
## Подключаем все php файлы проекта
Вызов этой страницы следует добавить в скрипт выкатки, чтобы не делать этого руками:
wget http://lambda.ruhighload.com/preload.php?key=12345
## Ключ для безопасности
Изменение данных и их структуры (или миграция) – наиболее сложная задача при выкатке новых версий. Во-первых изменение структуры данных может занимать достаточно много времени. Во-вторых, ошибки в выкатке могут привести к потерям данных.
Прежде, чем построить систему миграции данных, необходимо обеспечить выполнение следующих правил:
Такую структуру не нужно будет изменять, чтобы добавить новое свойство для продукта.
Технически миграции обычно организуют в виде набора файлов, содержащих SQL запросы, собранных в отдельной папке:
# ls highloadcomua/data/migrations/ 15.comments.add.post_id.sql 16.comments.add.content.sql 17.comments.add.user_id.sql 18.tags.add.titl.sql ...
Процесс выкатки миграций происходит с использованием отдельного сервера. На нем происходит обновление папки с миграциями (используя систему контроля версий). После этого исполняются SQL-запросы, которые появились в новых файлах миграций.
Пример скрипта, который определит новые файлы после обновления и выполнит миграции из них:
# Получаем список выполненных миграций
$executed = file(‘executed.migrations’);
# Получаем список всех миграций
$files = glob(‘data/migrations/*’);
foreach ( $files as $file )
{
if ( in_array($file, $executed) ) continue;
# Выполняем миграцию
exec(‘mysql database -u root -p12345 < ‘ . $file, $o, $r);
# Если нет ошибки, помечаем миграцию, как выполненную
if ( !$r ) $executed[] = $file;
}
file_put_contents(‘executed.migrations’, implode(“n”, $executed));
На серверах специального назначения (например, почтовые сервера) потребность вносить изменения бывает реже, чем на серверах приложения. Однако, иногда приходится делать изменения в конфигурациях. Для таких целей удобно хранить все конфигурации в репозитории. Тогда, для смены настроек, достаточно будет обновить файлы конфигураций на всех серверах.
Например для обновления конфигурации почтового сервера можно было бы использовать приблизительно такой скрипт:
cd /etc/exim4
git pull
update-exim4.conf
/etc/init.d/exim4 restart
Для более сложных задач управления конфигурациями лучше использовать Chef.
В случае, если в системе присутствуют несколько десятков или более серверов, последовательное обновление кода на всех серверах может занять много времени:
for ip in `cat servers.list`; do
echo “Updating $ip…”
ssh $ip ‘git -C /production_b pull’
done
## Последовательное обновление кода
С помощью фоновых процессов можно выполнить все эти команды параллельно:
for ip in `cat servers.list`; do
echo “Updating $ip…”
ssh $ip ‘git -C /production_b pull’ **>> /var/log/deploy.log &**
done
## Параллельное обновление кода на всех серверах в фоне
Выкатка может содержать в себе ошибки либо просто быть неудачной с точки зрения бизнеса. В любом случае, всегда необходимо иметь возможность быстро восстановить предыдущую версию системы.
Со всеми узлами в описанном процессе это сделать очень просто. Достаточно изменить рабочую директорию Web сервера с текущей (например, production_b) на предыдущую (production_a).
Для баз данных очень важно соблюдать правило – отсутствие удаления данных любого вида в миграциях. Тогда возврат к предыдущей версии не потребует обратного изменения структуры. Удаление устаревших колонок, таблиц и данных следует планировать с задержкой (например, не ранее, чем через неделю). Это даст запас времени для возможного возврата.
Крупные изменения (например, новые функции либо существенные изменения в текущих) могут иметь негативные последствия после выкатки. Это может быть связано с неудачным бизнес или техническим решением. Кроме этого в реальной среде всегда существуют определенные факторы, которые невозможно повторить в среде разработки и тестирования (например, большое количество онлайн-пользователей). Это значит, что выкатка больших изменений всегда содержит в себе риск.
Поэтому, крупные и важные изменения удобно выкатывать таким образом, чтобы они были доступны только для части аудитории. Например, только для одного процента всех пользователей. В этом случае, негативные последствия будут ограничены только небольшой частью аудитории.
На практике это можно реализовать с помощью установки избранным пользователям кук. Например, установим каждому сотому пользователю куку “tester”:
if ( session::get('id') % 100 == 1 ) **setcookie('tester', 1);**
…
Тогда в Nginx’e можно будет использовать это значение, чтобы отправлять пользователей с этой кукой в другую (новую) папку:
server {
server_name ruhighload.com;
set $rt ‘/production_a’;
**if ($http_cookie ~* ” tester=1″) {
set $rt ‘/production_b’;
}**
root **$rt**;
location ~* .(php)$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME **$rt**$fastcgi_script_name;
}
}
## Если установлена кука tester, изменяем переменную $rt на другой путь
Последним шагом в процессе выкатки следует делать проверку доступности приложения. Это нужно, чтобы убедиться, что нет критических ошибок. В самом простом случае это может быть тестовая страница, которая содержит проверки самых важных компонент:
# проверяем подключение к базе данных
$time = mysql::col(‘SELET NOW()’);
if ( !$time ) echo ‘error getting time from mysql’;
# проверяем код 200 от основных страниц
$pages = [‘/’, ‘/speed’, ‘/server’];
foreach ( $pages as $page )
{
$c = curl_init(‘http://ruhighload.com’ . $page);
$result = curl_exec($c);
$status = curl_getinfo($c, CURLINFO_HTTP_CODE);
curl_close($c);
if ( $status != 200 ) echo ‘error on page ‘ . $page;
}
# еще можно проверить php_error.log на наличие ошибок
# еще можно проверить php-fpm.slow-log на появление медленных скриптов
# и т.п.
## hearbeat.php – простой скрипт для быстрой проверки важных компонент сайта
При наличии unit-тестов, имеет смысл использовать их часть для валидации выкатки. В случае обнаружения ошибок лучше всего автоматически откатиться на предыдущую версию, после чего чинить неисправности.
В качестве самого важного – описание общего процесса выкатки:
Подсистема выкатки – это такая же динамическая компонента приложения, как и любая другая. Ее постоянно необходимо дорабатывать и усовершенствовать. Хорошее правило – иметь автономную систему выкатки, которая не требует ручных операций.
Не торопитесь использовать навороченные системы управления выкатками с кучей интеграций всего во все. Собственные решения часто являются намного более простыми, поэтому легче в управлении и более гибкие в использовании.
Не смотря на максимальную автоматизацию, любая выкатка должна всегда происходить под контролем администраторов или разработчиков. Никогда не делайте слепые выкатки, всегда дожидайтесь “Deployment finished successfully” от своей системы.
Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…
В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…
Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…
Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…
Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…
IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…