ActiveScaleинженерная практика

S3-совместимость ActiveScale

Основы s3iamapicompatibilitysigv4

Коротко. ActiveScale предоставляет S3-совместимый REST API (S3 Standard) и холодный класс, совместимый с S3 Glacier. Поддерживаются бакеты, объекты, multipart, версионирование, Object Lock, lifecycle, квоты, репликация, IAM. Но «совместимость» не значит «любой AWS-клиент заработает без проверки»: подпись, кодирование ключей, query-параметры и адресацию надо тестировать.


1. Что есть

Возможность Поддержка
Бакеты/объекты, PUT/GET/HEAD/DELETE да
Multipart upload да (макс. объект до 50 TB на Z200)
Листинг, копирование да
Presigned URL да (с оговорками по времени)
Версионирование да
Object Lock (WORM) да
Lifecycle (переход в Cold, expiration) да
Квоты аккаунтов да
Bucket replication да
IAM (пользователи, группы, политики) да
Checksums MD5; с 7.3 — CRC32/CRC32C/SHA-1/SHA-256
NFS v3 да, отдельный сценарий

Про checksums: до 7.3 единственной суммой был MD5 (Content-MD5). С 7.3 появились x-amz-checksum-crc32, -crc32c, -sha1, -sha256 — на PutObject, GetObject, CopyObject и multipart. Современные SDK включают CRC32 по умолчанию, поэтому при клиенте «новом», а хранилище старом это первое, что стоит проверить.


2. Endpoint и стиль адресации

  • S3-фронт — на порту 443 ноды (vhost-стиль).
  • Нужен S3-домен (FQDN), например s3.<system>.<domain>; для IAM — отдельный IAM-домен.
  • Path-style (https://host/<bucket>/<key>) работает всегда.
  • Virtual-hosted (https://<bucket>.s3.<domain>/<key>) требует wildcard-DNS (*.s3.<domain>) и сертификат с SAN на wildcard.

Типовые ошибки:

  • :11000 — это внутренний DSS-порт, не S3.
  • s3.activescale.com — UI-обёртка, не endpoint.
  • Самоподписанный сертификат CN=scalerd без SAN — клиент валится на проверке имени хоста.

За портом 443 стоит прокси, а S3-движок слушает только loopback. Поэтому Server: Quantum-ActiveScale ставит именно движок: по нему видно, что ответил S3, а не веб-сервер; по x-amz-id-2 понятно, какая нода обслужила запрос. Незнакомый Host даёт редирект на дефолтный домен — для клиента «307 непонятно куда», а причина в том, что имя хоста не описано в модели.

Подробнее: S3-домен, сертификат и DNS.


3. Подпись (SigV4) и не-ASCII

  • Актуальная подпись — AWS Signature Version 4; окно времени ±15 минут.
  • Не-ASCII ключи и значения метаданных должны кодироваться по спеке (процентное кодирование, верхний регистр hex).
  • Некоторые клиенты (например, Ceph RGW cloud-transition) исторически спотыкаются на не-ASCII; проверяйте форму запроса и версию клиента.

Механика ошибки на не-ASCII обманчива. Клиент подписывает канонический запрос, где значение метаданных — сырые байты UTF-8; шлюз при проверке пересобирает его и заменяет не-ASCII на ?. Хэши не сходятся → 403 SignatureDoesNotMatch, хотя ключи верные и время в норме. Со стороны клиента это выглядит как «неверный секрет», и дальше идёт охота за несуществующей проблемой.

Признак: подпись ломается только на объектах с не-ASCII в ключе или в x-amz-meta-*, ASCII в том же бакете работает. Лечится на стороне клиента — процентным кодированием до подписи. Прокси, который «нормализует» запрос, тоже работает, но это обход: лишняя точка отказа и своя эксплуатация.

Второй класс: 501 Not Implemented на неизвестный query-параметр. Шлюз отвечает 501 на любой параметр вне своего списка — даже на безобидный ?foo=bar, и клиент трактует это как «операция не поддерживается». Штатный путь — добавить параметр в список разрешённых через поддержку; в 7.0 так поступили с x-id, который шлют некоторые AWS SDK.

Подробнее: S3 501 на неизвестный query-параметр.


4. IAM и resource policy

  • Ключи без resource policy → AccessDenied даже на листинг.
  • Resource policy назначается на бакет (PutResourcePolicy).
  • IAM-домен обязателен для IAM-вызовов; по IP IAM не работает.

Типовой сценарий первого подключения: ключи созданы, aws s3 ls отвечает AccessDenied, инженер идёт проверять ключи. Проверять надо политику на бакете: валидная пара ключей тут сама по себе не даёт доступа ни к чему.

С 7.1 в IAM есть условия и переменные политик: ограничение по IP, времени, тиру Glacier, длительности Object Lock; переменные-подстановки (например, префикс = имя пользователя). С 7.2 — MFA (TOTP) для администраторов в UI, CLI и REST.

Деталь из практики: IAM-пути с [ и \ до 7.2 ломали вызовы, а на scaled-out нодах IAM работал некорректно. Разное поведение на разных нодах — повод сверить версию.

Подробнее: IAM и resource policy.


5. Таблица операций: что проверять при подключении клиента

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

Операция Что проверять Частая причина отказа
PutObject 200 и корректный ETag; заголовок x-amz-storage-class если нужен Cold несовпадение контрольной суммы (x-amz-checksum-* на старой сборке), 403 при не-ASCII метаданных
GetObject байты совпадают с исходником, Content-Length, диапазонный запрос Range объект в GLACIER без restore; неверный ключ после процентного кодирования
HeadObject 200, размер, ETag, StorageClass лишний query-параметр → 501; объект в Cold, но HEAD должен работать всегда
ListObjectsV2 число объектов и NextContinuationToken на большом бакете клиент ждёт ListObjects v1; лимит max-keys; отсутствие прав на листинг
Multipart upload CreateMultipartUpload → UploadPart → Complete; объект >100 МБ таймаут на большом part; x-amz-storage-class в составном запросе (ломалось до 7.2); part нулевого размера
CopyObject копия в тот же и другой бакет; метаданные перенеслись копирование объекта из Cold требует restore; права на чтение источника и запись цели
DeleteObjects (batch) удаление пачки, разбор ответа с ошибками по каждому ключу клиент считает операцию атомарной; при версионировании остаются delete-маркеры
Presigned URL ссылка работает в пределах срока, не «в будущем» время в будущем блокировалось до 7.0; слишком длинный TTL; расхождение часов

Важно не «работает/не работает», а поведение на границах: PutObject на 1 КБ пройдёт почти всегда, а ломается на объекте в несколько гигабайт, на не-ASCII ключе или составной загрузке. Поэтому в проверку берут самый крупный и самый «неудобный» объект из реального набора.

HeadObject — самая полезная операция для диагностики: не тянет тело и не требует restore. Если HEAD даёт 200 с верным размером и классом, а GET — нет, проблема в состоянии объекта (Cold) или правах, но не в связности и не в подписи.


6. Типовые клиенты и на что они спотыкаются

Классы клиентов ведут себя по-разному, и знать типовые точки отказа полезнее, чем названия конкретных продуктов.

Бэкап-системы. Пишут много, крупными объектами, часто multipart, проверяют контрольные суммы. Три точки: (1) checksum-заголовки, которых нет на старой сборке; (2) ожидание атомарного DeleteObjects; (3) «индексный» объект, который перезаписывается поверх — при версионировании каждая перезапись оставляет версию, и бакет растёт быстрее, чем клиент считает. Последнее — самая частая причина «занято втрое больше, чем данных».

Аналитические движки. Читают выборочно и много: ListObjectsV2 с префиксами, Range, параллельные GET. Спотыкаются на (1) пагинации больших бакетов и max-keys; (2) range к объектам в Cold (нужен ranged restore, с 7.1); (3) HEAD «на всякий случай» → 501 из-за своего параметра.

SDK и прикладной код. Самая пёстрая группа: (1) region — значение условное, но без него клиент падает на конфигурации; (2) aws-chunked и потоковая подпись принимаются сборками не одинаково; (3) автовключение CRC32 в новых SDK; (4) retry: клиент ретраит 403 и 501, хотя это не временные ошибки, и создаёт шторм запросов.

Системы lifecycle и тиринга. Добавляют свои query-параметры и заголовки — именно они чаще ловят 501 на неизвестный параметр и 403 на не-ASCII, перенося имя исходного объекта в x-amz-meta-*.

Общее правило: выясните, какие операции клиент реально делает, а не какие умеет — debug-логи показывают это за пять минут.


7. Как воспроизвести проблему минимально

Задача — свести «ваш S3 не работает» к одному запросу, воспроизводимому с любой машины: сначала убедиться, что endpoint отвечает как S3, потом что подпись проходит на простом запросе, и только потом добавлять «подозрительные» элементы по одному.

# 1. Кто отвечает на порту: должен быть S3-движок, а не веб-сервер
curl -skI https://s3.<system>.<domain>/ | head -5
# ожидаем Server: Quantum-ActiveScale и x-amz-* заголовки

# 2. Сертификат: имя хоста, SAN, срок
openssl s_client -connect s3.<system>.<domain>:443 -servername s3.<system>.<domain> </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -dates -ext subjectAltName

Дальше — минимальный подписанный запрос. Проще всего через CLI, потому что он сам считает подпись:

aws --endpoint-url https://s3.<system>.<domain> --region us-east-1 \
    s3api put-object --bucket <BUCKET> --key probe.txt --body /etc/hostname
aws --endpoint-url https://s3.<system>.<domain> --region us-east-1 \
    s3api head-object --bucket <BUCKET> --key probe.txt

Если это работает, а «настоящий» клиент — нет, ищут разницу: --no-verify-ssl (не как решение, а как проверку гипотезы про сертификат), затем --debug и список подписываемых заголовков. Расхождение почти всегда там.

Без CLI подпись собирают вручную — тогда проверяют три вещи: SignedHeaders совпадает с фактически отправленными заголовками; x-amz-content-sha256 соответствует телу (или UNSIGNED-PAYLOAD); x-amz-date не уехал от ноды больше чем на 15 минут.

Что смотреть в ответе, по коду:

Код Что это значит Куда смотреть
403 SignatureDoesNotMatch подпись не сошлась не-ASCII в подписанном заголовке, расхождение SignedHeaders, часы
403 AccessDenied подпись верна, прав нет resource policy бакета, права ключа, x-amz-* не в подписи
404 NoSuchBucket / NoSuchKey не тот endpoint или не тот ключ path-style vs virtual-hosted, процентное кодирование
400 InvalidArgument параметр или значение не приняты x-amz-storage-class, checksum-заголовок, form-параметры
501 Not Implemented шлюз не знает query-параметр список разрешённых параметров, версия клиента
507 Storage is full раскладка не влезает политика w+s ≤ N, число блоксторов (не свободное место)
307 на другой хост незнакомый Host S3-домен в модели, DNS, стиль адресации

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


8. Границы

  • S3-совместимость не гарантирует 100% поведения AWS: проверяйте конкретные операции.
  • Часть query-параметров может быть неизвестна шлюзу (501); штатный путь — whitelist через поддержку/обновление.
  • Поведение presigned URL и Object Lock зависит от версии — сверяйтесь с release notes.
  • NFS v3 — отдельный сценарий; на scaled-out нодах это не эквивалент S3.

9. Источники


Нужен инженер? Разберу вашу ситуацию и скажу, что делать — по шагам, с проверками.

Обсудить задачу