Разборы

Запасное хранилище S3 для картинок сайта: авария провайдера и случайное удаление

Если картинки сайта лежат в облачном хранилище S3, у них два разных риска: хранилище недоступно (авария у провайдера) и файлы удалены (ошибка, скрипт, взлом). Простая копия у другого провайдера защищает только от первого. Удаление она послушно повторит. Для защиты от удаления нужны версии файлов на основном хранилище и копирование без удалений. Самое дешевое средство из всех - отдельный ключ доступа для сайта без права удалять.

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

Шаг 1. Проверить, что у вас есть сейчас

Три запроса к API хранилища. Пример для aws cli, адрес хранилища и имя бакета подставьте свои:

aws s3api get-bucket-versioning     --bucket my-bucket --endpoint-url https://storage.yandexcloud.net
aws s3api get-object-lock-configuration --bucket my-bucket --endpoint-url https://storage.yandexcloud.net
aws s3api get-bucket-lifecycle-configuration --bucket my-bucket --endpoint-url https://storage.yandexcloud.net

В моем случае ответы были такие:

МеханизмОтветЧто значит
Версионированиепустая конфигурацияне включено
Блокировка от удаления (Object Lock)ObjectLockConfigurationNotFoundErrorне настроена
Правила жизненного циклаNoSuchLifecycleConfigurationнет

То есть ни версий, ни блокировки, ни корзины. Готовой услуги резервного копирования бакетов российские провайдеры не продают: ни Яндекс, ни Selectel, ни Beget. У Яндекса в документации прямо написано, что копировать в другое хранилище надо своими силами. Yandex Cloud Backup покрывает виртуальные машины, бакеты в него не входят, похоже только название.

Что делает Битрикс при удалении файла

Модуль clouds подписан на событие удаления файла и стирает объект из облака сразу, в том же запросе. Корзины и отложенного удаления на стороне Битрикса нет.

Отдельный риск: штатный инструмент "Оценить объем и количество дубликатов" удаляет дубликаты из облака пачками.

Шаг 2. Ключ без права удаления

Создайте для сайта отдельный ключ доступа с политикой бакета, в которой нет действия s3:DeleteObject. Это ничего не стоит и полностью закрывает сценарий "админка или скрипт стерли файлы". Минус один: удаление картинок с сайта перестанет чистить облако, и место будет расти. На большинстве сайтов это копейки.

Шаг 3. Версионирование на основном бакете

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

Проверьте у своего провайдера оба механизма до того, как на них рассчитывать:

МеханизмYandexSelectelBeget
Версионированиеесть, выключить потом нельзя, только приостановитьестьне заявлено
Object Lockестьестьнет
Автоудаление старых версийестьнетнет

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

Шаг 4. Свой домен для картинок

Пока картинки отдаются по адресу провайдера вида bucket.storage.yandexcloud.net, любая смена хранилища меняет все адреса картинок на сайте и сбрасывает их индексацию. Со своим поддоменом вида img.example.ru переключение на резервное хранилище делается правкой одной DNS-записи.

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

У Selectel имя бакета с доменом совпадать не обязано, но сертификат надо обновлять вручную.

Шаг 5. Копия у другого провайдера, в другом регионе

Копия обновляется по расписанию через rclone. Ключевое здесь: не повторять удаления вслепую.

rclone sync main:my-bucket reserve:my-bucket-backup \
  --backup-dir reserve:my-bucket-trash/$(date +%F) \
  --max-delete 200 --max-delete-size 1G \
  --fast-list --transfers 16 --checkers 32 \
  --log-file /var/log/rclone/s3-backup.log --log-level INFO
  • --backup-dir в отдельный бакет: то, что исчезло из основного хранилища, не удаляется из копии, а переезжает в папку с датой. Корзина не должна лежать внутри бакета-приемника, rclone этого не допускает;
  • --max-delete: если в основном хранилище разом пропало много файлов, rclone остановится с ошибкой, а не разнесет удаление по копии. Эта ошибка должна приходить вам уведомлением, а не тонуть в логе;
  • первый запуск всегда с --dry-run;
  • rclone copy вместо sync удаления не повторяет вообще, но копия тогда только растет. Для десятков гигабайт это приемлемо.

Корзину чистить по возрасту: rclone delete reserve:my-bucket-trash --min-age 30d.

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

Резервное хранилище в самом Битриксе

В модуле clouds есть штатный механизм резервного хранилища, но в админке он скрыт. Блок настроек появляется, только если включить опцию:

COption::SetOptionString('clouds', 'failover_enabled', 'Y');

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

  • агент, который разбирает очередь копирования, модуль сам не регистрирует. Без него очередь растет, а резерв остается пустым. Ставится так: CAgent::AddAgent("CCloudFailover::queueAgent();", "clouds", "N", 60);
  • копирование идет через ваш сервер: скачал, залил. Для больших объемов есть многопоточный разбор: php bitrix/modules/clouds/tools/sync.php <число потоков>;
  • копирование берет файл по публичной ссылке, для приватного бакета этот путь не сработает;
  • автоматического переключения при аварии нет, флаг ставится руками или своим мониторингом;
  • после переключения нужно сбросить кеш файлов, кеш страниц и кеш веб-сервера. Иначе сайт продолжит отдавать ссылки на упавшее хранилище.

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

Как выбрать резервного провайдера

**Модуль Битрикса ставит права на каждый файл заголовком x-amz-acl: public-read.** Провайдер должен его понимать или хотя бы спокойно игнорировать. У MWS (МТС) ACL не поддерживаются принципиально. У Selectel в таблице совместимости ACL помечены как неподдерживаемые, хотя у Битрикса есть официальная инструкция по подключению Selectel. До обещаний проверьте одной командой:

aws s3api put-object --bucket test --key t.txt --body t.txt --acl public-read --endpoint-url https://адрес-хранилища

Ответ 400 или 501 означает, что понадобится правка модуля.

Еще несколько правил:

  • не храните копию у того же провайдера, где стоит сайт. Вы соберете все риски в одном месте;
  • смотрите на регион, а не на количество дата-центров. Мультизонный пул Selectel ru-6 это три площадки в пределах Москвы, в 10-15 км друг от друга. Он переживет пожар на одной площадке, но не событие масштаба региона. Резерв к московскому хранилищу логичнее держать в Петербурге;
  • проверяйте, чьи это мощности. Timeweb в Петербурге арендует стойки в дата-центре Selectel, Cloud4U и Cloud4Y работают на одной платформе. Посмотрите адрес хранилища в документации, пробейте его IP по whois, сравните подсети. Это занимает десять минут;
  • хранилище за границей не подходит, если в файлах есть персональные данные.

Сколько это стоит

Хранение десятков гигабайт у российских провайдеров стоит порядка 100 руб. в месяц (цены октября 2026 года, около 1,1-2,6 руб. за гигабайт). Отдельно считайте исходящий трафик: у Яндекса первые 100 ГБ бесплатны, у Selectel бесплатного объема нет. Трафик тратится, когда вы восстанавливаете файлы из копии.

Список для проверки

  1. Включено ли версионирование на основном бакете?
  2. Может ли ключ сайта удалять файлы?
  3. Картинки отдаются со своего домена или с адреса провайдера?
  4. Есть ли копия у другого провайдера и в другом регионе?
  5. Повторяет ли копирование удаления и есть ли порог на массовое удаление?
  6. Приходит ли уведомление, если копирование упало?
  7. Пробовали ли вы хоть раз восстановить файл из копии или переключить сайт на резерв?

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

Похожая проблема на вашем сайте? Напишите в Telegram или оставьте заявку.