Версии и переключение текущего выпуска
На виртуальном хостинге способ публикации зависит от возможностей площадки. На нашем учебном VPS можно сделать выбор выпуска явно: каждый результат находится в собственном каталоге, а Nginx читает через ссылку current. В этом уроке разберём подготовку каталога и само переключение, не превращая распаковку архива в изменение работающего сайта.
Предполагается, что на основном домене уже обслуживается r1, а кандидат r2 принят после просмотра на staging. Корень Nginx — /srv/release-lab/current/public. Примеры используют Ubuntu, Bash и GNU coreutils; конкретное поведение команд на другой системе проверяется отдельно. Все команды остаются учебными листингами, действующий сервер ими не изменялся.
Подготовленный выпуск не равен выбранному
Архив распаковывают в новый каталог /srv/release-lab/releases/r2. В нём ожидаются public и release.json. Публикация не должна перезаписывать /srv/release-lab/releases/r1, даже если два результата похожи. Сохранённый каталог — материал для последующего возврата. Если имя кандидата уже существует, доставка останавливается или сверяет точную идентичность, а не стирает старые файлы под тем же номером.
/srv/release-lab/
releases/
r1/public/...
r1/release.json
r2/public/...
r2/release.json
current -> /srv/release-lab/releases/r1
shared/
Права приводят к ожидаемому состоянию до переключения. Издатель управляет каталогами, группа веб-сервера читает файлы и проходит по родителям. Если при распаковке появился владелец из чужой системы или каталог недоступен группе, исправляют это в кандидате. Менять права после выбора current означает показывать посетителям промежуточное состояние. Также заранее проверяют свободное место: архив, распакованный кандидат и сохранённые выпуски существуют одновременно.
Одна операция выбора
Создадим новую временную символическую ссылку, затем заменим ею существующую current посредством переименования. Временная ссылка находится рядом, поэтому переименование происходит в той же файловой системе. Ключ -T GNU mv нужен, чтобы назначение трактовалось как сама ссылка, а не как каталог, внутрь которого надо переместить объект. Это существенная деталь, а не косметическое сокращение команды. Описание mv в GNU Coreutils.
set -e
base=/srv/release-lab
candidate=r2
release_dir="$base/releases/$candidate"
test -L "$base/current"
test -f "$release_dir/release.json"
test -f "$release_dir/public/index.html"
test ! -e "$base/.current.next"
test ! -L "$base/.current.next"
previous=$(readlink -f "$base/current")
ln -s "$release_dir" "$base/.current.next"
mv -Tf "$base/.current.next" "$base/current"
readlink -f "$base/current"
Листинг иллюстрирует одиночное переключение на контролируемом стенде. Идентификатор кандидата здесь задан вручную, без ввода извне. Для автоматизации проверяют допустимый формат имени, нахождение каталога внутри releases, значение паспорта и права. Если current является обычным каталогом, выполнение прекращают: приведённый способ не предназначен для его случайной замены. Первичная установка без прежнего выпуска требует отдельного явно выбранного сценария.
Наличие set -e помогает остановиться при ошибке команды, но не создаёт транзакцию всей публикации. Сохранение журнала, проверка ответа и удаление временных объектов требуют отдельной обработки. Если процесс завершился после замены ссылки, сайт уже мог измениться. Следующий запуск должен прочитать фактический current, а не предполагать, что неуспешное задание гарантированно оставило прежнее состояние.
В чём заключается атомарность
При выбранном механизме имя current меняется переименованием: новый запрос разрешит старую либо новую ссылку, вместо отдельного промежутка без неё. Это свойство отличается от двух ручных переименований публичных каталогов. Однако оно относится к выбору одного файлового имени. Браузер получает HTML и CSS несколькими запросами; первый может прочитать r1, а второй прийти после переключения r2. Полную согласованность страницы одна ссылка не гарантирует.
Поэтому ресурсы с изменившимся содержимым получают новые имена, например site.r2.css, а необходимый прежний файл сохраняется в новом публичном дереве на переходный срок. Новый HTML ссылается на новый ресурс, старый HTML ещё может запросить старый. Это правило полезно даже без CDN. В двадцать втором уроке добавим сроки кеширования и внешние узлы, но начальная модель уже должна не ломать начатые просмотры.
Nginx использует файловый корень и может иметь дополнительные механизмы кеширования файлов. В базовом стенде они не включены специально. Изменение ссылки обычно не требует перезагрузки конфигурации, поскольку значение root осталось прежним. Если у проекта используются дополнительные кеши, их поведение проверяют отдельно. Документация корня и файлового кеша Nginx.
При подготовке первого рабочего сайта полезно один раз сопоставить механизм с системой хранения. Ссылка и временное имя должны находиться в одном локальном каталоге; сетевые файловые системы и панели могут добавлять собственные ограничения и кеши. Не следует переносить обещание переименования на произвольную удалённую операцию файлового менеджера. Также заранее проверяют, что Nginx имеет доступ к конечным каталогам по обеим целям ссылки. Успешное чтение нового current пользователем издателя не доказывает чтение тем пользователем, от которого работает веб-сервер. Эти условия проверяются до выбора кандидата, чтобы переключение меняло только версию, а не одновременно права и размещение.
Проверка после выбора
После команды readlink знаем состояние файловой системы, но ещё не ответ публичного домена. Запрос health.json должен показать паспорт кандидата, а обязательные страницы — нужное содержимое. Несовпадение может объясняться другим виртуальным сайтом, дополнительным прокси или кешем. Поэтому не ограничиваемся записью «ссылка переключена»: она подтверждает только один шаг, а принятие выпуска связано с тем, что получают посетители.
В журнале сохраняют предыдущую цель, новую цель, идентификатор артефакта и время наблюдений. Не оставляют прежний путь только в переменной незавершённой сессии. Если соединение оборвётся, другой оператор должен понять, какой результат был выбран до события. Для минимального учебного журнала достаточно отдельного файла вне публичного дерева; для рабочей инфраструктуры используют согласованное хранилище истории изменений.
Повторный запуск и конкуренция
Повторная доставка того же принятого артефакта должна приводить к известному состоянию, а не создавать новый смысл старого номера. Можно обнаружить, что current уже указывает на кандидата, прочитать его паспорт и завершить операцию без повторной замены. Однако нельзя считать каталог существующим только по имени: содержимое могло быть неполным после прерванной распаковки. Полноту подготовки фиксируют отдельно до допуска к переключению.
Пока действия выполняет один издатель. Два одновременно работающих конвейера могут перепутать временные ссылки или опубликовать устаревший кандидат. Одной атомарной заменой эту проблему не решить: она защищает отдельный выбор, но не порядок решений. В двадцать первом уроке добавим сериализацию и проверку свежести. Сейчас мы получили ясный механизм одиночного выпуска и сохранённую цель для возврата, который разберём далее.