О версионировании в S3 в деталях: разбор от команды VK Object Storage

от автора

S3-совместимые хранилища предоставляют бизнесу и ИТ-специалистам широкий набор инструментов для работы с данными. Это и практически бесконечная масштабируемость, позволяющая хранить петабайты информации без сложной настройки, и высокая надежность за счет автоматического резервирования, и гибкое управление доступом для разных команд и сервисов. Наряду с ними важной и полезной функцией является версионирование бакетов, которое позволяет хранить полную историю изменений каждого объекта и защищает от потери данных.

Но чтобы раскрыть весь потенциал этой функции и грамотно встроить ее в рабочие процессы, важно понимать, как она устроена. 

В этой статье разберем, что такое версионирование бакетов в S3, как оно меняет логику работы хранилища и как использовать его для решения реальных задач.

Начнем с азов: что такое версия и версионирование в S3

S3-совместимые хранилища поддерживают возможность работы с разными версиями объектов. Это позволяет не просто хранить файлы, а управлять их историей. В основе этой технологии лежит понятие версии объекта.

Версия — это идентификатор конкретного состояния объекта. По сути, это уникальный «снимок» файла в определенный момент времени. В модели хранения этот идентификатор (versionId) представляет собой строку, безопасную для использования в URL.

Именно на работе с такими идентификаторами строится версионирование — режим бакета, который позволяет хранить всю историю изменений. Это не просто техническая опция, а способ сделать работу с данными надежнее. Так, версионирование решает прикладную задачу: оно дает возможность восстановить данные, если объект был перезаписан или удален по ошибке. Для хранилища это означает, что операции перестают быть разрушительными в привычном смысле: вместо одной записи появляется цепочка версий, а удаление часто превращается не в физическое уничтожение, а в создание специальной служебной версии — Delete-marker.

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

  • Версионирование выключено. Это режим по умолчанию. В нем все работает привычным образом: операция PUT перезаписывает объект, а DELETE удаляет его навсегда. История версий не ведется.

  • Версионирование включено (Enabled). В этом режиме каждая новая загрузка объекта (PUT) создает новую версию с уникальным versionId. Старые версии остаются в хранилище, а удаление объекта не стирает его, а добавляет в историю специальный маркер — Delete-marker.

  • Версионирование приостановлено (Suspended). Этот режим можно включить только после того, как версионирование уже заработало. В этом состоянии новые объекты будут загружаться с Null-версией (как если бы версионирования не было), но при этом история всех старых версий, созданных ранее, сохранится.

Примечание: Если версионирование приостановлено, новая загрузка с именем file.txt перезапишет существующую Null-версию (если она есть) без сохранения истории.

Здесь есть важный архитектурный нюанс: вернуться из состояния «включено» обратно в «выключено» нельзя. Можно только приостановить версионирование. Это принципиальный момент, который влияет на логику хранения и работу других функций, например Object Lock. Но об этом мы подробно поговорим чуть позже.

Операции с объектами: загрузка, чтение и удаление

Теперь подробнее остановимся на том, как версионирование меняет привычные операции с файлами. Для пользователя API остается прежним, но под капотом происходят важные изменения.

Специфика загрузки объекта (PUT)

Если бакет не версионирован или версионирование приостановлено, объект получает так называемую Null-версию. В этом случае операция PUT работает по-старому: она просто перезаписывает файл.

Если же версионирование включено, система ведет себя иначе. Каждая новая загрузка создает новую версию с уникальным идентификатором (versionId). Старая версия никуда не исчезает — она просто становится неактуальной. Для клиента API ничего не меняется, но в ответе на успешную загрузку появляется новый заголовок x-amz-version-id. Он содержит идентификатор только что созданной версии.

Примечание: Новая версия — это полностью независимая запись. Она не наследует права доступа (ACL), теги и другие метаданные от предыдущей автоматически. Это нужно учитывать при проектировании прав.

Специфика получения объекта (GET и HEAD)

Доступ к данным тоже зависит от режима работы.

  • Если нужен текущий (актуальный) объект, система сама выберет версию с самым поздним временем создания. Это происходит автоматически.

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

  • Объекты с Null-версией доступны по тому же пути, что и обычные. Это позволяет единообразно работать и со старыми файлами, и с данными из приостановленного режима.

Специфика удаления (DELETE)

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

  • Версионирование выключено: объект удаляется физически и навсегда.

  • Версионирование включено: обычное удаление не стирает данные. Вместо этого создается специальный служебный объект — маркер удаления (Delete-marker). Он становится самой актуальной версией, и при следующем запросе без указания versionId система вернет ошибку 404 Not Found. При этом все старые версии файла остаются в хранилище и их можно восстановить.

  • Удаление конкретной версии: если при удалении указать versionId, то будет удалена именно эта версия (даже если это сам маркер удаления).

  • Версионирование приостановлено: удаление работает через Null-delete-marker, сохраняя логику неразрушающего удаления для новых объектов.

Примечание: При обращении к актуальному маркеру удаления система возвращает ошибку 404 Not Found («Не найдено»). Если же запросить неактуальный маркер удаления из истории версий, ответом будет ошибка 405 Method Not Allowed («Метод не разрешен»). Это важно для правильного поведения GET и HEAD поверх исторических версий.

Правила жизненного цикла данных

Как писали выше, при включенном версионировании данные не исчезают навсегда: операция DELETE лишь создает маркер удаления, а старые версии остаются в хранилище. Это надежно защищает от потерь, но неизбежно ведет к росту объема данных. И чтобы история версий не переполнила бакет, нужны инструменты для автоматизации очистки. Для этого используются правила жизненного цикла (Lifecycle), и их поведение напрямую зависит от режима версионирования.

Так, для бакетов с активным версионированием появляются инструменты, которые позволяют управлять именно неактуальными версиями объектов:

  • NoncurrentVersionExpiration. Позволяет автоматически удалять старые версии объектов по истечении заданного количества дней. Это основной инструмент для контроля роста объема данных.

  • NewerNoncurrentVersions. Ограничивает количество хранимых неактуальных версий (например, можно хранить только пять последних версий).

  • ExpiredObjectDeleteMarker. Позволяет удалить сам маркер удаления, если у объекта больше не осталось реальных версий. Правило срабатывает, когда среди всех версий остались только маркеры удаления, и удаляет самый старый из них.

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

Навигация и аудит: листинг объектов и версий

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

  • Обычный листинг (ListObjects). Это стандартный метод, который возвращает список объектов так, будто версионирование не включено. В ответе содержатся только актуальные версии файлов. Если для какого-то объекта был создан маркер удаления, он не попадает в этот список. Если же объект был перезаписан, в списке отображается только его последняя версия. Этот метод удобен для получения актуальной картины содержимого бакета.

  • Листинг версий (ListObjectVersions). Это специальный метод, предназначенный для работы с историей. Он показывает все содержимое бакета без исключений. В ответе содержится массив всех версий объектов (включая самые первые и уже неактуальные), а также массив всех маркеров удаления (Delete-markers). Это позволяет восстановить полную хронологию: когда файл был создан, изменен или удален. Данный метод используется для аудита, резервного копирования и восстановления данных.

Влияние версионирования на Object Lock

Версионирование защищает от случайных изменений и позволяет хранить историю объектов. Однако для защиты данных от преднамеренного удаления или изменения, в том числе со стороны злоумышленников или администраторов, это не помогает. Для этого существует Object Lock (блокировка объектов), которая реализует модель WORM (Write Once, Read Many — «одна запись, многократное чтение»). 

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

Но нюанс в том, что Object Lock не может работать без версионирования, поскольку блокировка в S3 применяется не к «файлу» в привычном смысле, а к конкретной версии объекта.

Логика работы становится следующей:

  • Можно заблокировать текущую версию документа. После этого ее нельзя будет ни удалить, ни изменить.

  • При этом можно загрузить новую версию. Она не будет заблокирована (пока это не будет настроено отдельно).

  • Старая, заблокированная версия останется нетронутой.

Таким образом, версионирование создает «фундамент» из версий, а Object Lock позволяет «цементировать» нужные из них, делая их неизменяемыми.

При этом применение Object Lock накладывает строгие и необратимые ограничения на управление бакетом. Это плата за максимальную безопасность.

  • Object Lock требует включенного версионирования. Включить блокировку на бакете без версионирования невозможно.

  • Состояние фиксируется навсегда. После включения Object Lock для бакета становится невозможно отключить версионирование или перевести бакет в режим приостановки (Suspended). Попытка сделать это приведет к ошибке. Бакет навсегда остается в режиме Enabled. Это сделано для того, чтобы никто не смог обойти политику хранения (Retention) и удалить заблокированные данные, просто отключив историю версий.

В итоге Object Lock превращает объектное хранилище в настоящий цифровой сейф.

Как это работает на примере VK Object Storage в VK Cloud

Теперь подробнее остановимся на том, как все описанное выше работает на практике. В рамках примера будем использовать S3-совместимое хранилище VK Object Storage в VK Cloud, которое в рамках недавних обновлений получило поддержку версионирования.

Итак, приступим.

Подготовка AWS CLI

Для работы с S3-совместимым хранилищем необходимо настроить параметры подключения.

  • Выполняем команду для настройки: aws configure.

  • Указываем идентификатор ключа доступа к VK Object Storage.

  • Вводим секретный ключ доступа Secret Key. Он должен соответствовать идентификатору ключа, указанному в консоли.

  • Определяем регион размещения сервиса VK Object Storage по умолчанию (ru-msk — регион Москва, kz-ast — регион Казахстан).

  • Указываем выходной формат по умолчанию, то есть в каком виде AWS CLI будет отображать результаты выполнения команды. На выбор доступно несколько вариантов: json, yaml, yaml-stream, текст, таблица.

После этого можно переходить к подготовке бакета. 

Подготовка бакета

Для наглядности рассмотрим два варианта: с поддержкой только с версионирования, а также с версионированием и Object Lock.

Сценарий 1. Создание нового бакета с поддержкой только с версионирования

Создаем бакет: 

aws s3api create-bucket  --bucket versioning-only-bucket  --region ru-msk   --endpoint-url https://hb.ru-msk.vkcloud-storage.ru

Включаем версионирование:

aws s3api put-bucket-versioning  --bucket versioning-only-bucket  --versioning-configuration Status=Enabled   --endpoint-url https://hb.ru-msk.vkcloud-storage.ru

Проверяем:

aws s3api get-bucket-versioning   --bucket versioning-only-bucket   --endpoint-url https://hb.ru-msk.vkcloud-storage.ru

Ожидаемый результат:

{  "Status": "Enabled"}

Здесь важно отметить несколько моментов. Так, после создания бакета с поддержкой версионирования:

  • все новые объекты будут получать уникальный versionId;

  • PUT больше не перезаписывает объект — создает новую версию;

  • DELETE больше не удаляет объект — создает Delete-marker;

  • старые объекты (если были) остаются с versionId = null.

При этом полностью «выключить» версионирование нельзя, но его можно приостановить с помощью команды:

aws s3api put-bucket-versioning   --bucket versioning-only-bucket   --versioning-configuration Status=Suspended   --endpoint-url https://hb.ru-msk.vkcloud-storage.ru

Сценарий 2. Создание нового бакета с поддержкой версионирования и Object Lock

Object Lock работает только при включенном версионировании. Причем после включения Object Lock состояние версионирования становится неизменяемым.

Но и в этом сценарии алгоритм создания бакета довольно простой.

Создаем бакет с поддержкой Object Lock:

aws s3api create-bucket   --bucket versioning-lock-bucket   --object-lock-enabled-for-bucket   --region ru-msk   --endpoint-url https://hb.ru-msk.vkcloud-storage.ru

Примечание: Параметр —object-lock-enabled-for-bucket задается только при создании бакета. Включить Object Lock позже нельзя.

Далее включаем версионирование:

aws s3api put-bucket-versioning  --bucket versioning-lock-bucket  --versioning-configuration Status=Enabled  --endpoint-url https://hb.ru-msk.vkcloud-storage.ru

Выполняем проверку:

aws s3api get-bucket-versioning  --bucket versioning-lock-bucket   --endpoint-url https://hb.ru-msk.vkcloud-storage.ruОжидаемый результат:{  "Status": "Enabled"}

Ожидаемый результат:

{  "Status": "Enabled"}

Тут отдельно стоит остановиться на изменениях по сравнению с обычным версионированием. Так, в случае подключения Object Lock:

  • каждая версия объекта может быть заблокирована (WORM);

  • удалить или перезаписать заблокированную версию нельзя;

  • можно создавать новые версии.

Вместе с тем нельзя сделать Status=Suspended — попытка приведет к ошибке уровня InvalidBucketState, потому что Object Lock требует постоянного состояния Versioning = Enabled.

Загрузка объекта

Перейдем к загрузке первой версии объекта.

Создадим файл echo «v1» > file.txt.

Загрузим:

aws s3api put-object   --bucket versioning-lock-bucket  --key file.txt   --body file.txt   --endpoint-url https://hb.ru-msk.vkcloud-storage.ru

Ответ будет содержать:

{  "VersionId": "..."}

Это первая версия объекта.

Теперь проверяем, что версия действительно существует. Для этого получаем список версий:

aws s3api list-object-versions   --bucket versioning-lock-bucket  --endpoint-url https://hb.ru-msk.vkcloud-storage.ru

Ответ будет содержать массив Versions, поле IsLatest=true, versionId.

Установка Object Lock при загрузке

Теперь добавим блокировку:

aws s3api put-object   --bucket versioning-lock-bucket  --key locked.txt   --body file.txt   --object-lock-mode GOVERNANCE   --object-lock-retain-until-date 2026-12-31T00:00:00  --endpoint-url https://hb.ru-msk.vkcloud-storage.ru

Примечание: Важно, чтобы дата добавления блокировки была позже момента загрузки объекта, иначе API вернет ошибку.

В результате создается новая версия, а на нее навешивается Retention Policy.

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

aws s3api get-object-retention   --bucket versioning-lock-bucket  --key locked.txt   --endpoint-url https://hb.ru-msk.vkcloud-storage.ru

Ожидаемый ответ:

{  "Retention": {    "Mode": "GOVERNANCE",    "RetainUntilDate": "..."  }}

Чтобы проверить, работает ли блокировка на удаление, воспользуемся командой:

aws s3api delete-object   --bucket versioning-lock-bucket  --key locked.txt   --endpoint-url https://hb.ru-msk.vkcloud-storage.ru

Несмотря на то что на объект будет установлен Deletion Marker и он пропадет из списка объектов, все загруженные версии останутся неудаленными и их можно будет восстановить. 

Что в итоге

Версионирование бакетов — это не просто техническая опция, а фундаментальный инструмент для обеспечения надежности данных. Оно превращает операции из потенциально разрушительных в безопасные, позволяя хранить полную историю каждого объекта и легко восстанавливать информацию после ошибок. В сочетании с Object Lock эта технология становится основой для построения систем, соответствующих самым строгим требованиям безопасности.

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

ссылка на оригинал статьи https://habr.com/ru/articles/1042626/