Kvmzen Блог
← Назад к разделу «Технологии на практике»

OpenShip: проверка отката перед продакшеном

DevOps и CI/CD ·~11 мин чтения

OpenShip: проверка отката перед продакшеном

Платформа показывает успешный откат, но старый код не может прочитать уже изменённую базу данных.

Самое быстрое решение — считать OpenShip откат успешным только после проверки старого артефакта, конфигурации, совместимости схемы, реального трафика и фоновых задач. Если миграция была разрушающей, откатывайте приложение и восстанавливайте данные как две отдельные процедуры.

Эта инструкция нужна командам, которые готовят OpenShip к производственному развертыванию AI SaaS, Agent API или фоновых сервисов. Она также пригодится инженерам выпуска, которым нужно собрать доказательства для согласования релиза, а не просто нажать кнопку в панели.

Почему статус «успешно» ещё ничего не доказывает

Официальное описание OpenShip говорит о неизменяемом снимке развертывания, сохранении предыдущей версии, проверках доступности и возврате к прошлому выпуску. Это подтверждает наличие платформенного механизма, но не доказывает, что любое приложение восстановит бизнес-функции после отката. (openship.io)

Для производственной проверки разделяйте три уровня:

  • уровень платформы — панель приняла команду и создала операцию возврата;
  • уровень процесса — старый контейнер запустился, прошёл проверку готовности и получил нужную конфигурацию;
  • уровень бизнеса — пользователь снова может выполнить ключевую операцию, данные не повреждены, а фоновые действия не повторяются.

Проблема обычно возникает между вторым и третьим уровнями. Контейнер может слушать порт, отвечать на технический путь проверки и при этом не уметь:

  • читать новую колонку или тип данных в базе;
  • обращаться к внешнему API по старому адресу;
  • продолжать потоковую выдачу ответа;
  • распознавать уже обработанное задание;
  • использовать секрет с прежними правами;
  • корректно завершать запросы, начатые во время переключения.

Поэтому цель проверки — не доказать, что кнопка работает, а подтвердить возврат к известному рабочему состоянию.

Как заранее определить критерий восстановления

До запуска упражнения запишите четыре решения в карточке выпуска.

Первое — целевая версия. Укажите идентификатор образа, коммита или иного неизменяемого артефакта. Формулировка «вернуть предыдущую версию» слишком расплывчата: предыдущей может оказаться не та сборка, которую вы проверяли на тестовом окружении.

Второе — ключевые операции. Для AI SaaS это обычно вход пользователя, создание задания, отправка запроса к модели, потоковая выдача результата и просмотр истории. Для Agent API добавьте вызов инструмента и повторную отправку одного запроса с тем же идентификатором.

Третье — граница допустимой деградации. Например, временно можно отключить несущественную статистику, но нельзя потерять созданное задание или вернуть пользователю два платных результата. Такая граница должна быть записана до инцидента, иначе команда начнёт спорить о приемлемом ущербе во время отката.

Четвёртое — владельцы решений. Отдельно назначьте ответственного за выпуск, владельца инфраструктуры и человека, который может разрешить восстановление базы. Не объединяйте последнюю роль с тем, кто нажимает кнопку отката: приложение и данные могут требовать разных действий.

Важно. Не подставляйте в регламент обещание «нулевого простоя» из рекламного описания платформы. Для вашего сервиса это можно подтвердить только журналом запросов, результатами потоковых соединений и наблюдением за ошибками во время конкретной смены версии. (openship.io)

Первый этап: подтвердите старый артефакт и запуск с нуля

OpenShip описывает каждое развертывание как неизменяемый снимок, а предыдущую версию — как доступную для возврата. Ваша задача — проверить, что старый снимок действительно самодостаточен, а не держится на случайно оставшемся контейнере или файлах на сервере. (openship.io)

Проверьте следующие объекты:

  • идентификатор нужного образа;
  • дату и инициатора исходного выпуска;
  • журнал сборки и развертывания;
  • список зависимостей;
  • команду запуска;
  • подключаемые тома;
  • сетевые имена внешних сервисов;
  • наличие миграционного скрипта и его фактическое состояние.

Затем выполните проверку в таком порядке:

  1. Сохраните идентификатор текущей рабочей версии и версии, к которой планируется возврат.
  2. Запустите старый артефакт как новый экземпляр, не используя существующий контейнер.
  3. Убедитесь, что процесс стартует без локальных файлов, созданных новой версией.
  4. Проверьте техническую готовность и один настоящий бизнес-запрос.
  5. Остановите тестовый экземпляр и повторите запуск ещё раз из того же артефакта.
  6. Сопоставьте журналы запуска с журналом первоначального выпуска.

Проходит: старый образ доступен, запускается как новый экземпляр, использует ожидаемую команду запуска и не требует ручного копирования файлов.

Не проходит: старый контейнер работает, но новый экземпляр не стартует; часть зависимостей недоступна; идентификатор выпуска нельзя однозначно сопоставить с журналом.

Действие при отказе: не переводите откат в производственный регламент. Сначала восстановите хранение артефактов, зафиксируйте команду запуска и повторите тест на чистом экземпляре.

Конфигурация и секреты: что именно нужно сравнивать

Наиболее опасный сценарий — код вернулся назад, а окружение осталось от новой версии. Тогда визуально приложение может быть «старым», но фактически оно обращается к другой базе, новому адресу API или секрету с несовместимыми правами.

Проверяйте не только список переменных в панели, а фактически загруженную конфигурацию внутри старого процесса. Сравнение должно включать:

  • имя окружения;
  • адрес базы данных;
  • адрес очереди;
  • адреса внешних API;
  • режим работы и флаги функций;
  • имена бакетов или хранилищ;
  • идентификаторы версий секретов;
  • права сервисной учётной записи;
  • настройки тайм-аутов и повторных попыток.

Сами значения ключей и токенов в отчёт не записывайте. Используйте имя секрета, версию, хэш или время последнего обновления. Это особенно важно, если журнал выпуска доступен нескольким участникам команды.

Проходит: старая версия получает ожидаемые адреса и флаги, секреты доступны с нужными правами, а конфигурация не направляет старый код в новый несовместимый сервис.

Не проходит: переменная присутствует в панели, но отсутствует внутри процесса; секрет имеет новую версию; код читает другой адрес; после запуска появляется ошибка авторизации.

Действие при отказе: остановите переключение трафика, восстановите совместимую конфигурацию или подготовьте отдельный адаптер. Не исправляйте проблему ручным редактированием контейнера: такое изменение нельзя воспроизвести при следующем запуске.

Если команда использует защищённые окружения и автоматическое согласование выпуска, полезно хранить конфигурацию выпуска отдельно от секрета. Подход с окружениями, правилами защиты и ограничением доступа к секретам описан в официальной документации по управлению производственными развертываниями. (docs.github.com)

Второй этап: проверка базы данных и миграций

OpenShip откат не равен восстановлению базы данных. Возврат контейнера меняет код и связанные с ним параметры выпуска, но не должен автоматически описываться как возврат состояния PostgreSQL, Redis, MongoDB или другого постоянного хранилища.

Перед производственным выпуском разделите миграции на три группы.

Обратно совместимые. Новая версия добавляет таблицу, индекс или необязательное поле, а старый код продолжает работать с прежней схемой.

Переходные. Сначала добавляется новая структура, затем обе версии некоторое время читают и записывают совместимый формат. Удаление старого поля выполняется отдельным выпуском после завершения перехода.

Разрушающие. Поле удаляется, тип меняется без совместимого преобразования, данные перезаписываются необратимо или старая версия не может выполнить запрос к новой схеме.

Для каждой миграции подготовьте четыре доказательства:

  • схема до выпуска;
  • схема после миграции;
  • результат запуска старого кода на новой схеме;
  • независимый план восстановления данных.

Проверка должна быть не теоретической. Возьмите обезличенную копию данных, примените миграцию, запустите старый артефакт и выполните ключевые чтения и записи. Если старый код не умеет читать новую схему, у вас уже есть ответ: возврат приложения возможен только вместе с отдельным восстановлением базы или после ручного обратного преобразования.

Проходит: старый процесс запускается, выполняет критичные запросы, корректно записывает данные и не меняет их повторно из-за несовместимого формата.

Ограниченно проходит: пользовательские запросы работают, но отдельная функция отключена. В этом случае выпуск допускается только с явно записанным ограничением и владельцем компенсационного плана.

Не проходит: старая версия падает на чтении схемы, теряет данные, создаёт неверные записи или требует резервной копии, которую команда не может реально восстановить.

Для работ с базой заведите отдельную инструкцию, а в статье выпуска храните только ссылку на её идентификатор, время проверки и ответственного. В руководстве Kvmzen по стратегиям резервного копирования для AI SaaS можно использовать отдельный материал как основу для подготовки такой процедуры; саму готовность восстановления всё равно подтверждайте на своей копии данных.

Третий этап: здоровье сервиса, трафик и длинные соединения

Проверка готовности отвечает на вопрос: «Процесс может принимать работу?». Она не отвечает на вопрос: «Пользователь получил корректный результат?».

Официальное описание OpenShip заявляет поддержку проверок здоровья, маршрутизации и WebSocket-соединений. Внутренняя документация по маршрутам также указывает, что конфигурация маршрутизации должна быть привязана к конкретному развертыванию, чтобы при возврате восстанавливались соответствующие правила. Это нужно проверить в вашем варианте размещения, особенно если фронтенд, API и Worker разворачиваются раздельно. (openship.io)

Порядок проверки:

  1. Убедитесь, что старый экземпляр проходит технический путь готовности.
  2. Выполните обычный запрос к главному API.
  3. Выполните запрос с ошибочными параметрами и проверьте ожидаемый ответ.
  4. Запустите потоковую выдачу и дождитесь корректного завершения.
  5. Откройте WebSocket-соединение и проверьте обмен сообщениями.
  6. Сравните журналы до переключения, во время переключения и после него.
  7. Проверьте, не осталось ли запросов на новой версии после принятия решения о возврате.

Для Agent API добавьте тест с долгим вызовом инструмента. Он выявляет проблему, которую не видно в коротком HTTP-запросе: старый экземпляр уже принимает новые соединения, но не умеет завершить работу, начатую новой версией.

Проходит: новый экземпляр становится готовым до получения трафика; ключевые HTTP-запросы возвращают ожидаемый результат; поток и WebSocket не обрываются без предусмотренной обработки; ошибки во время переключения объяснимы и не приводят к потере операции.

Не проходит: панель показывает рабочий статус, но пользователь получает тайм-аут; WebSocket закрывается без повторного подключения; поток обрывается после смены маршрута; часть запросов продолжает попадать в несовместимую версию.

Действие при отказе: временно остановите переключение, уменьшите долю трафика на проблемный экземпляр или полностью изолируйте его. Для критичной операции сохраните идентификатор запроса и определите, можно ли безопасно повторить действие.

Фоновые задачи и очереди: где появляется скрытый дубль

При откате часто работают сразу несколько поколений приложения. Новая версия могла уже получить задание, а старая после запуска снова увидеть его в очереди. Аналогичная ситуация возникает с планировщиком, повторными доставками сообщений и вызовами внешних инструментов.

Проверяйте не только количество сообщений, а связь между тремя объектами:

  • устойчивый идентификатор задания;
  • запись состояния в базе или очереди;
  • внешний побочный эффект.

Например, отправка письма, создание документа, списание лимита или вызов инструмента должны иметь проверку «уже выполнено» до повторной операции. Если идентификатор создаётся заново при каждом чтении сообщения, откат почти наверняка создаст дубли.

Перед выпуском выполните упражнение:

  1. Создайте тестовое задание и запишите его ID.
  2. Запустите обработку новой версией.
  3. Остановите или ограничьте потребителей.
  4. Выполните возврат старого артефакта.
  5. Возобновите обработку.
  6. Сравните число попыток, статус задания и внешний результат.
  7. Намеренно повторите доставку того же сообщения.

Проходит: повторная доставка не создаёт второй побочный эффект; статус задания остаётся согласованным; команда знает, как безопасно возобновить очередь.

Не проходит: одна задача выполняется дважды; старая версия перезаписывает новый статус; планировщик запускает две копии; неизвестно, какие задания уже завершились.

Действие при отказе: остановите Worker и планировщик, заморозьте очередь или переведите её в режим ручной обработки. Затем составьте список затронутых ID, отделите подтверждённые побочные эффекты от неизвестных и только после этого запускайте компенсационные операции.

Итоговая проверка перед согласованием выпуска

Используйте этот список как обязательный документ, а не как заметку в чате.

  • [ ] Зафиксирован идентификатор старого и нового артефакта.
  • [ ] Старый артефакт запущен как новый экземпляр.
  • [ ] Повторный запуск не зависит от остаточных файлов или контейнеров.
  • [ ] Сохранены журналы сборки, запуска и переключения.
  • [ ] Проверена фактически загруженная конфигурация.
  • [ ] Версии и права секретов сопоставлены с целевым выпуском.
  • [ ] Адреса базы, очереди и внешних API не указывают на несовместимую новую систему.
  • [ ] Миграция классифицирована как совместимая, переходная или разрушающая.
  • [ ] Старый код проверен на текущей схеме базы.
  • [ ] Для восстановления данных существует отдельная подтверждённая процедура.
  • [ ] Техническая проверка готовности проходит до получения трафика.
  • [ ] Проверены обычный HTTP, потоковая выдача и WebSocket.
  • [ ] Журналы ошибок сохранены до, во время и после возврата.
  • [ ] Для Worker проверены ID заданий, статусы и побочные эффекты.
  • [ ] Повторная доставка сообщения не создаёт дубль.
  • [ ] Назначены ответственные за выпуск, инфраструктуру и данные.
  • [ ] Записаны условия остановки и перехода к восстановлению базы.

После заполнения списка выберите один из трёх выводов.

Разрешить выпуск — все критичные проверки прошли, а доказательства доступны ответственным.

Разрешить с ограничением — сервис восстанавливается, но часть второстепенных функций отключена и есть конкретный срок устранения ограничения.

Запретить выпуск — отсутствует артефакт, не подтверждена схема, теряются запросы, повторяются задания или нет владельца восстановления данных.

Такой вывод лучше связывать с номером выпуска и журналом согласования. В самой процедуре отката должны быть указаны триггер, человек, который принимает решение, и отдельный владелец данных. Подход с защитой производственного окружения, наблюдаемостью и ручным согласованием также используется в современных системах управления выпусками. (docs.github.com)

Что делать, если откат частично не удался

Не пытайтесь исправить все слои одновременно. Разделите инцидент на последовательные действия.

Если не запускается старый образ, остановите переключение и верните последнюю версию, которая подтверждённо стартует. Затем восстановите доступность артефакта и повторите запуск на чистом экземпляре.

Если не совпадает конфигурация, не меняйте секреты вручную внутри контейнера. Зафиксируйте активную версию конфигурации, откройте доступ только к совместимым значениям и повторно создайте экземпляр.

Если несовместима база, не запускайте массовые обратные запросы. Сначала остановите запись, определите границу повреждения и переходите к отдельной процедуре восстановления из резервной копии.

Если ломается трафик, сохраните запросы с идентификаторами, проверьте маршруты и временно оставьте рабочую версию активной. Для WebSocket и потоковой выдачи отдельно определите, какие соединения можно безопасно завершить.

Если дублируются задачи, остановите Worker и планировщик. Повторная обработка без списка затронутых ID может увеличить ущерб.

В инструкции Kvmzen по восстановлению Agent-сервисов можно дополнительно посмотреть, как подготовить отдельную среду для проверки процедуры. Для самой тренировки не используйте единственный рабочий сервер: вам нужно иметь возможность остановить потребителей, изменить конфигурацию и восстановить данные без давления со стороны реальных пользователей.

Подходит ли OpenShip для вашего производственного отката

OpenShip разумно рассматривать, если вам нужны неизменяемые артефакты, сохранение прошлых выпусков, единый маршрут от сборки до запуска и возможность проверять возврат через CLI или панель. Эти возможности полезны, но они не заменяют совместимость приложения с базой, идемпотентность Worker и проверку внешних зависимостей. (openship.io)

Текущий подход без полноценного упражнения обычно дешевле только на бумаге: команда полагается на статус панели, хранит конфигурацию в нескольких местах и узнаёт о несовместимой миграции уже после переключения. Локальная машина дополнительно ограничивает доступность сборки, повторяемость окружения и время, в течение которого можно проводить проверку.

Если вам нужен временный изолированный компьютер для сборки, smoke-тестов или повторной проверки старого артефакта, можно рассмотреть аренду Mac mini для удалённого рабочего окружения. Это не решает проблему базы данных автоматически, но помогает не проводить критичное упражнение на единственном ноутбуке разработчика. Перед выпуском всё равно убедитесь, что ваша команда один раз полностью выполнила откат, проверила трафик и отдельно подтвердила восстановление данных.

Ограниченное предложение

Больше чем Mac — ваша база разработки в облаке

Выделенные вычисления · Глобальные узлы · Ежемесячная подписка · Без покупки железа

На главную
Ограниченное предложение Посмотреть тарифы