В первой версии доставки моего DocOps-проекта задание развёртывания завершалось после изменения GitOps-репозитория. Это подтверждало запись желаемого состояния, но не появление новой документации на сайте.
Позже между подготовкой изменения и его публикацией для Flux появился Merge Request с обязательными проверками. Эту границу я разобрал в статье «Почему CI GitOps-репозитория не является deployment gate». Но успешное слияние тоже не отвечало на вопрос: какую версию сейчас получает читатель?
Здесь я рассматриваю публикацию документации целиком, а не только обновление Kubernetes-ресурса Deployment. Для такой задачи недостаточно передать изменение следующему участнику процесса. Нужно отдельно подтвердить результат.
У каждого статуса своя граница
Коммит в отслеживаемой ветке означает, что изменение стало доступно Flux. Успешное согласование состояния подтверждает применение конфигурации и прохождение настроенных проверок. Flux умеет ждать готовности ресурсов через healthChecks или wait, поэтому нельзя безусловно считать его успех лишь подтверждением отправки манифестов.1
Завершённый rollout означает, что Kubernetes обновил реплики и выполнил условия их доступности. Но статус Deployment сам по себе не проверяет, что запрос по адресу сайта проходит через нужный маршрут и возвращает ожидаемую документацию.2
Эти статусы полезны для диагностики. Ошибка возникает, когда один из них начинают использовать как доказательство всей публикации.
HTTP 200 может вернуть прежняя версия
Представим, что обновление не дошло до приложения, а предыдущая версия продолжает работать. Запрос к сайту получает HTTP 200. Проверка доступности проходит, хотя результат текущего выпуска ещё не появился.
Поэтому для подтверждения публикации мало проверить, что сайт отвечает. Нужно отличить ожидаемую версию от любой другой.
В DocOps появилась проверка, которая опрашивает сайт и ждёт ожидаемую ревизию. Успешный HTTP-ответ — только первое условие. После него проверка читает тело ответа и сверяет опубликованный идентификатор с ожидаемым полным revision.
Смысл проверки такой:
сайт отвечает успешно
+
опубликованная ревизия совпадает с ожидаемой
Прежняя версия перестаёт быть допустимым подтверждением нового выпуска, даже если сама по себе работает исправно.
Ожидаемую ревизию нужно знать до запроса
Для сопоставления используется полный идентификатор ревизии исходников, а не только человекочитаемый номер релиза или короткий SHA. Проверка должна знать, какую ревизию выпускает текущий пайплайн, и искать именно её.
Здесь важна независимость ожидаемого значения от наблюдаемого. Если сначала прочитать версию с сайта, а затем объявить её ожидаемой, проверка примет любое уже опубликованное состояние.
Есть и требование к происхождению метаданных: идентификатор должен относиться к той сборке, которую отдаёт приложение. Запись новой ревизии в отдельное хранилище ещё не доказывает, что сам сайт обновился.
В этом сценарии ревизия нужна не для красивой подписи в интерфейсе. Она связывает конкретный выпуск с наблюдаемым ответом приложения.
Ожидать условие, а не фиксированную задержку
Доставка через GitOps асинхронна. После передачи изменения в этот процесс нужная версия может появиться не сразу. Поэтому проверка публикации повторяет запросы, пока не увидит ожидаемую ревизию или не исчерпает отведённое время.
Фиксированная пауза решала бы другую задачу. Она подтверждала бы только то, что прошло заданное число секунд. При быстрой доставке это лишнее ожидание, при медленной — преждевременная проверка.
У ожидания должны быть две границы: таймаут отдельного запроса и общий срок подтверждения публикации. Зависшее соединение не должно занимать всё время, а повторение запросов не должно продолжаться бесконечно.
Истечение срока означает: «публикация не подтверждена за отведённое время». Это не доказательство, что она никогда не произойдёт. Наблюдатель мог перестать ждать, пока остальная система продолжает работу. Из одного таймаута также нельзя вывести решение об откате.
Что именно подтверждает такая проверка
Ответ с ожидаемой ревизией подтверждает, что она стала доступна по проверяемому адресу из точки, где выполняется запрос. Это проверка публикации, а не доказательство полной исправности системы.
Один запрос может попасть на уже обновлённую реплику, пока остальные ещё обновляются. Поэтому проверка ревизии не заменяет наблюдение за завершением rollout. Она также не проверяет все страницы, работу интерфейса в браузере и доступность из каждой пользовательской сети.
Для моего сценария важным дополнением стало именно подтверждение нужной ревизии по HTTP. Раньше процесс сообщал, что изменение передано дальше. Теперь у него есть отдельное наблюдаемое условие: сайт отдаёт результат ожидаемого выпуска.
На этом можно завершить проверку публикации. За дальнейшую доступность отвечает уже постоянный мониторинг, а не бесконечно работающий пайплайн.
