Модель выпуска: что именно мы развёртываем
После первой публикации статического сайта кажется, что развёртывание состоит из одного действия: скопировать файлы в папку хостинга. При следующем изменении выясняется, что этого описания недостаточно. Нужно знать, какие файлы относятся к новой версии, что уже работает у посетителей и какое состояние можно вернуть, если публикация окажется неудачной. В этом уроке введём модель выпуска, которой будем пользоваться во всей серии.
Предположим, что результат предыдущего курса уже получен: Markdown преобразован в HTML, ссылки сохранены, статический сайт можно разместить на Beget. Новый учебный проект называется release-lab. Его домены example.com и staging.example.com, а все показанные ответы и журналы являются условными. Мы не переносим эти настройки на действующий ProfessorWeb. Из его опыта берём задачу: менять устройство сайта, сохраняя содержимое, адреса и возможность восстановить прежнее состояние.
Три разных состояния одного проекта
Исходники содержат то, что редактирует автор: Markdown, шаблоны, изображения и код генератора. Готовый артефакт содержит то, что понадобится веб-серверу: HTML, стили, изображения, поисковый индекс и служебные файлы. Окружение добавляет настройки, благодаря которым этот результат становится доступен: домен, сертификат, корень сайта и права чтения. Если смешать эти состояния, получится папка, в которой трудно отличить публикацию от рабочего места разработчика.
Рассмотрим изменение заголовка статьи. Автор исправил Markdown, но посетитель ещё видит старый HTML. Это нормальное промежуточное состояние: изменены исходники, а действующий выпуск остался прежним. Затем генератор создаёт новый HTML. Даже после успешной генерации посетитель не обязан увидеть обновление: результат пока существует отдельно от публичного корня. Только публикация связывает подготовленный артефакт с выбранным окружением. Поэтому фраза «изменение готово» должна сопровождаться уточнением, о каком из трёх состояний идёт речь.
В нашем стенде прежний выпуск называется r1, кандидат — r2. Эти короткие имена нужны для объяснения; позднее CI присвоит версии уникальный номер. Важна неизменность содержания под этим номером. Если вчера r2 обозначал один набор файлов, а сегодня тот же каталог был перезаписан другим, журнал перестаёт объяснять происходящее. Лучше выпустить следующий кандидат, чем незаметно исправлять уже названный артефакт.
Что считается законченным выпуском
Пусть в r1 существуют главная, каталог, новая статья и старый адрес /my/legacy/first.php. Последний файл содержит готовый HTML. Расширение имени не превращает его в выполняемую программу. В r2 добавляется статья, но старый адрес должен остаться доступным. Если перенести только новый HTML, можно случайно забыть изображение или удалить файл, который кажется устаревшим. Целостный выпуск описывает весь публичный результат, включая ресурсы прежних страниц.
Исходники → подготовленное public/ → артефакт r2
↓
закрытый staging
↓
действующее окружение
Стрелка между staging и действующим сайтом означает перенос того же артефакта. Повторная генерация «ещё раз перед публикацией» создаёт другой результат, если изменились зависимости, время, данные или исходники. Тогда проверяли один набор файлов, а показываем другой. Настройки доступа у окружений могут различаться, но содержание выпуска должно оставаться узнаваемым. В следующем уроке добавим паспорт, который позволит установить это по самому результату, а не по памяти автора.
Полнота не означает, что в артефакт нужно включить весь компьютер разработчика. Виртуальное окружение Python, ключ SSH, история Git и конфигурация почты не нужны серверу статических файлов. Напротив, их присутствие усложняет публикацию и может открыть лишние данные. Практический критерий простой: файл либо участвует в публичном ответе, либо служит непубличным паспортом выпуска; остальные материалы остаются в исходниках или в настройках окружения.
Решение о публикации и наблюдаемый результат
До переключения полезно записать краткую карточку изменения. Для нашего примера она сообщает: добавлена статья; изменён общий стиль; старые пути сохранены; подготовлен кандидат r2; действующим остаётся r1. В карточке также указывают проверенные маршруты и причину возврата. Например, если после переключения старый URL получает 404 или каталог отдаёт страницу прежнего выпуска, выпуск считают неприемлемым и возвращают r1. Такая запись соединяет действия с ожидаемым поведением.
Кандидат: r2
Действующий выпуск до изменения: r1
Обязательный старый адрес: /my/legacy/first.php
Ожидаемый ответ: 200, HTML статьи
Условие возврата: адрес отсутствует либо выдаёт иной материал
Это учебная карточка, а не результаты HTTP-проверки. Когда читатель выполнит публикацию на своём стенде, рядом должны появиться дата, способ проверки и фактический ответ. Строка «проверено» без указания версии мало полезна: следующий запуск мог проверить уже другой каталог. Важны также тело ответа и вложенные ресурсы. Код 200 у пустой страницы не доказывает сохранение материала, а красивый снимок главной не подтверждает работоспособность всех старых адресов.
В GitLab окружение и развёртывание представлены отдельно от сборки: окружение описывает место, а развёртывание связывает его с изменением. Это помогает объяснить, почему успешное задание упаковки ещё не является публикацией. Конкретные возможности зависят от конфигурации проекта; мы воспользуемся только необходимыми сущностями. Документация GitLab об окружениях.
Почему модель помогает при ошибке
Представим, что стиль новой статьи не загрузился. Если r2 редактировали непосредственно в публичной папке, трудно восстановить ход события: часть HTML новая, часть ресурсов старая, предыдущие файлы затёрты. При отдельных неизменяемых выпусках вопрос точнее: отсутствует ли стиль внутри r2, выбрал ли сервер каталог r2, не хранит ли клиент прежнюю копию? Каждый вариант требует другого исправления. Мы сможем разбирать их последовательно, не угадывая содержимое публичной папки.
При этом номер выпуска не делает публикацию автоматически безопасной. Браузер получает HTML и ресурсы несколькими запросами, а внешний кеш может продолжать отдавать прежние данные. У приложения появляется ещё и состояние базы. В первых четырнадцати уроках работаем со статикой, поэтому основным объектом возврата будет публичное дерево и его конфигурация. Во второй части добавим процессы и данные и покажем, почему возврат файлов не всегда возвращает весь сайт.
Наконец, выпуск и резервная копия решают разные задачи. Выпуск позволяет воспроизвести известный результат из подготовленных файлов. Копия сохраняет состояние, которое может исчезнуть вместе с сервером: настройки, дополнительные данные и прежнее размещение. Даже если все статьи находятся в Git, в нём может не оказаться привязки домена или загруженных посетителями файлов. Поэтому в этой серии отдельно научимся переключать версию, откатывать изменение и восстанавливать копию.
К концу урока мы получили словарь для дальнейшей работы: исходники, артефакт, окружение, кандидат и действующий выпуск. Следующий шаг — описать состав артефакта так, чтобы его можно было передавать между окружениями и узнавать после публикации.