{ "index": 124, "slug": "editorial-2024-07-field-release-engineering", "title": "Когда версия не доказывает релиз: проверка artifact, migration и rollback", "excerpt": "Один номер релиза может скрывать разные commit, artifact и migration. Разбираем, где остановиться, какие связи проверить и почему возврат образа не отменяет изменения данных.", "contentHtml": "
В заявке на выпуск стоит 2024.07.0. Такой же номер виден у commit, container image, migration и rollout. После выкладки сервис отвечает кодом старой схемы: новый код ждёт поле, которого в базе нет. Команда повторяет deploy, потому что все карточки выглядят согласованными. Ошибка становится дороже с каждой попыткой: растёт окно недоступности, меняется состояние данных, а точку возврата уже трудно назвать.
Проблема не в самом номере версии. Проблема в том, что номер заменил связи между объектами. Он не доказывает, что artifact собран из нужного commit, что migration рассчитана на этот contract и что rollout ссылается на тот же digest. Выпуск готов только тогда, когда эти связи можно проверить по точным значениям, а отрицательный результат останавливает действие.
\nCommit описывает исходный revision. Artifact содержит собранное содержимое и immutable digest. Migration меняет схему или данные и должна назвать целевую версию и совместимость. Rollout intent говорит, какой digest команда собирается отправить. Return point хранит предыдущую версию и digest. Эти записи связаны, но не заменяют друг друга.
\nУ каждой связи есть проверяемое утверждение. Artifact должен ссылаться на exact commit id. Migration должна называть release version и совместимость с текущей схемой. Rollout должен содержать digest из artifact, а не только tag. Return point должен быть известен до approval. Если одно утверждение ложно или неизвестно, действие заканчивается на gate. Retry не исправляет неправильную запись.
\nНиже — ограниченный учебный пример. Значения вымышлены. Код не обращается к Git, registry, CI, Kubernetes API или базе данных. Он только сравнивает заранее заданные записи и возвращает решение для проверки человеком.
\nconst release={version:'2024.07.0'},commit={id:'commit-7f4a0c1'},artifact={digest:'sha256:release-070-a1',sourceCommitId:'commit-7f4a0c1'},migration={targetReleaseVersion:'2024.07.0',compatibleWith:'2024.06.3'},rollout={requestedArtifactDigest:'sha256:release-070-a1',migrationVersion:'2024.07.0'}; const checks={source:artifact.sourceCommitId===commit.id,migration:migration.targetReleaseVersion===release.version,artifact:rollout.requestedArtifactDigest===artifact.digest,rollout:rollout.migrationVersion===migration.targetReleaseVersion}; const ready=Object.values(checks).every(Boolean); if(!ready) throw new Error('stop: reconcile release records');\nПри ready === true пример говорит только о согласованности пяти записей. Он не говорит, что образ существует, подпись действительна, migration выполнена или сервис здоров. Если заменить sourceCommitId на другой id, результат должен стать отрицательным. То же относится к digest и target version. Это и есть полезный отрицательный путь: система не угадывает, какую запись считать правильной.
| Симптом | Причина | Проверка | Действие |
|---|---|---|---|
| Номер версии совпадает, но artifact указывает на другой commit. | Tag используют вместо точной связи с исходным revision. | Сравнить artifact.sourceCommitId и commit id. | Остановить выпуск. Исправить запись или пересобрать artifact после решения владельца. |
| Код можно вернуть, но схема базы уже изменилась. | Rollback binary ошибочно считают rollback данных. | Проверить migration target, compatibility и обратную процедуру. | Вернуть только явно разрешённый artifact; вопрос данных передать отдельному владельцу. |
| Rollout прошёл с тем же tag, но другим digest. | Intent ссылается на mutable label, а не на immutable content. | Сравнить requested digest с digest artifact. | Не запускать rollout. Пересоздать intent после сверки. |
| После stop команда предлагает повторить deploy. | Retry используют как замену объяснению расхождения. | Найти первую ложную связь и назвать её источник. | Сначала reconcile records, затем повторить только проверку. |
Таблица разделяет четыре разных вопроса. Mismatch commit относится к происхождению artifact. Mismatch migration относится к совместимости contract. Mismatch digest относится к содержимому, выбранному для rollout. Повторная попытка без такой классификации стирает причину и оставляет команду без доказуемого решения.
\nRollback Deployment обычно возвращает предыдущую ревизию Pod template. Это полезно для кода и настроек, которые входят в template. Оно не отменяет произвольный SQL, удалённую запись, заполненное поле или изменение внешнего contract. Если migration уже прошла, старый image может не уметь читать новую схему.
\nReturn point должен содержать две границы. Первая — какую версию artifact можно запустить. Вторая — что разрешено делать с данными. Возможны обратная migration, совместимый промежуточный код, восстановление из резервной копии или запрет автоматического возврата. Пока путь не проверен, честная формулировка звучит так: «возврат artifact определён; откат данных не доказан».
\nТо же различие действует для provenance и attestations. Официальная документация SLSA описывает проверку provenance через сравнение с ожиданиями пакета. GitHub описывает artifact attestations как подписанные claims о происхождении и даёт команды для проверки. Ни один из этих механизмов сам по себе не утверждает, что migration совместима, rollout одобрен или production здоров.
\nОписанный подход ловит расхождения между названными записями. Он не доказывает, что значения правдивы. Он не проверяет историю Git, содержимое image, подпись, policy CI, права на кластер, runtime configuration, состояние базы, трафик, метрики или факт доставки. Для этих вопросов нужны разрешённые источники и отдельные проверки.
\nЕсли commit неизвестен, digest отсутствует, migration не имеет compatibility statement или return point не назван, результат должен быть отрицательным. Не подставляйте «последний main», не ищите image по tag и не объявляйте data rollback по факту отката Pod template. Остановитесь на первой неизвестной границе. Такое поведение медленнее одной зелёной кнопки, но дешевле расследования после повреждения данных.
\nМатериал готов к передаче на human review, если второй инженер без устных пояснений может показать: exact commit id, artifact digest, связь artifact с commit, migration target и compatibility, rollout digest, migration version и return point. Для каждой строки есть источник, владелец и действие при mismatch. Проверка должна дать либо все утверждения true, либо конкретный stop с названием ложной связи. В первом случае разрешение на deploy всё ещё принимает авторизованный процесс. Во втором случае deploy не начинается.
Учебные значения в примере не являются production-результатами. Их задача — показать форму проверки и сохранить отрицательный путь. Реальную оценку готовности нужно выполнять на доступных и разрешённых записях конкретного выпуска.
\n