Как мы развернули S3-совместимое хранилище на MinIO для 50 ТБ медиафайлов

Исходная ситуация

Медиа-агентство «МедиаСток» — продакшен-компания, которая производит рекламные ролики, фотосессии и 3D-визуализации для крупных брендов. За 5 лет работы накопилось 50 ТБ медиафайлов: RAW-фотографии, ProRes-видео, проекты After Effects, финальные рендеры.

Всё это хранилось на NAS Synology с четырьмя 16 ТБ HDD в RAID 5. Проблемы нарастали:

  • Скорость — 10 дизайнеров одновременно работали с файлами по SMB, и скорость чтения падала до 30 MB/s.
  • Доступ — удалённые сотрудники и клиенты не могли получить файлы без VPN. Для каждой отправки файл заливали на Яндекс.Диск вручную.
  • Бэкапы — единственная копия на том же NAS. Пожар, кража или отказ двух дисков — и всё потеряно.
  • Интеграция — собственное веб-приложение для управления проектами не умело работать с SMB-шарами.

Нам в itfresh.ru поставили задачу: S3-совместимое хранилище, доступное через API, с веб-интерфейсом для клиентов и бэкапом в облако.

Почему MinIO, а не Ceph или облачный S3

Мы рассматривали три варианта:

КритерийMinIOCeph RGWYandex Object Storage
Сложность развёртыванияПростоеВысокаяНулевая (SaaS)
Стоимость 50 ТБ/мес~25 000 ₽ (серверы)~25 000 ₽ (серверы)~110 000 ₽
ПроизводительностьВысокаяВысокаяОграничена сетью
S3 API совместимость99%95%100%
Минимальный кластер1 нода3 нодыN/A
Операционные расходыНизкиеВысокие (нужен эксперт)Нулевые

MinIO победил по соотношению цена/сложность. Для 50 ТБ Ceph избыточен — он раскрывается на петабайтах. Облачный S3 при 50 ТБ стоит вчетверо дороже, а загрузка тяжёлых видеофайлов через интернет — слишком медленно для ежедневной работы.

Архитектура: single-node vs distributed

MinIO поддерживает два режима. Мы начали с single-node multi-drive, потому что для 50 ТБ одного сервера достаточно:

# /etc/default/minio — конфигурация single-node
MINIO_VOLUMES="/mnt/disk{1...8}/minio"
MINIO_OPTS="--console-address :9001"
MINIO_ROOT_USER=mediastock-admin
MINIO_ROOT_PASSWORD=

# Адрес API
MINIO_SERVER_URL=https://s3.mediastock.local
MINIO_BROWSER_REDIRECT_URL=https://console.mediastock.local

Серверная конфигурация:

  • CPU: AMD EPYC 7313P (16 ядер)
  • RAM: 128 GB ECC
  • Диски: 8x Seagate Exos 16 TB HDD (JBOD, без RAID — MinIO управляет данными сам)
  • Системный: 2x 480 GB SSD NVMe (RAID 1)
  • Сеть: 2x 10 Gbps (bond, LACP)

Общая raw-ёмкость: 128 ТБ. С erasure coding (4+4) полезная ёмкость: 64 ТБ. Этого хватает на текущие 50 ТБ с запасом на рост.

# Запуск MinIO как systemd-сервис
sudo systemctl start minio
sudo systemctl enable minio

# Проверка статуса
mc admin info myminio

# Вывод:
#   Drives: 8/8 OK
#   Pool: 1, Erasure sets: 1, Drives per erasure set: 8
#   Storage: 50.2 TiB used, 64.0 TiB total

Erasure Coding: защита данных без RAID

MinIO использует erasure coding вместо RAID. В нашей конфигурации 4+4 (4 data + 4 parity) каждый объект разбивается на 8 частей и распределяется по 8 дискам. Система выдерживает одновременный отказ 4 из 8 дисков без потери данных.

# Проверка состояния erasure coding
mc admin heal myminio --dry-run

# Вывод:
# ┌──────────┬───────────┬──────────┐
# │ Pool     │ Set       │ Status   │
# ├──────────┼───────────┼──────────┤
# │ 1        │ 1         │ Healthy  │
# └──────────┴───────────┴──────────┘
# Objects: 847,293 (OK: 847,293, Heal needed: 0)

# Если диск вышел из строя — автоматическое восстановление
mc admin heal myminio --recursive

# Мониторинг дисков
mc admin disk info myminio

# Проверка целостности данных (bitrot detection)
mc admin scanner myminio

Преимущества перед RAID 5/6:

  • Скорость восстановления — RAID 5 rebuild на 16 ТБ занимает 12-24 часа, во время которых массив уязвим. MinIO восстанавливает только затронутые объекты.
  • Параллельное чтение — данные читаются с нескольких дисков одновременно, агрегируя пропускную способность.
  • Обнаружение bitrot — каждый блок данных хэшируется, испорченные блоки автоматически восстанавливаются из parity.

Bucket Policies и Lifecycle Rules

Мы создали три бакета с разными политиками доступа:

# Создание бакетов через mc CLI
mc mb myminio/projects        # Активные проекты
mc mb myminio/archive          # Завершённые проекты
mc mb myminio/client-delivery  # Файлы для клиентов (публичное чтение)

# Политика для client-delivery — только чтение для анонимных
mc anonymous set download myminio/client-delivery

# Версионирование для projects — защита от случайного удаления
mc version enable myminio/projects

IAM-политика для дизайнера (доступ только к своим проектам):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
      "Resource": ["arn:aws:s3:::projects/designer-ivanov/*"]
    },
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": ["arn:aws:s3:::projects"],
      "Condition": {
        "StringLike": {"s3:prefix": ["designer-ivanov/*"]}
      }
    }
  ]
}

Lifecycle rules для автоматической архивации:

# lifecycle.json — перенос завершённых проектов в archive через 90 дней
{
  "Rules": [
    {
      "ID": "archive-old-projects",
      "Status": "Enabled",
      "Filter": {"Prefix": "projects/"},
      "Transition": {
        "Days": 90,
        "StorageClass": "GLACIER"
      }
    },
    {
      "ID": "cleanup-temp",
      "Status": "Enabled",
      "Filter": {"Prefix": "projects/tmp/"},
      "Expiration": {"Days": 7}
    },
    {
      "ID": "delete-old-versions",
      "Status": "Enabled",
      "Filter": {"Prefix": ""},
      "NoncurrentVersionExpiration": {"NoncurrentDays": 30}
    }
  ]
}

mc ilm import myminio/projects < lifecycle.json

Интеграция с приложением: presigned URLs

Веб-приложение «МедиаСток» для управления проектами нуждалось в возможности загружать и скачивать файлы напрямую из браузера, минуя бэкенд. Для этого мы использовали presigned URLs:

# Python — генерация presigned URLs для загрузки и скачивания
from minio import Minio
from datetime import timedelta

client = Minio(
    "s3.mediastock.local:9000",
    access_key="app-service-account",
    secret_key="app-secret-key",
    secure=True
)

def get_upload_url(project_id: str, filename: str) -> str:
    """Генерирует presigned URL для загрузки файла (PUT)"""
    return client.presigned_put_object(
        bucket_name="projects",
        object_name=f"{project_id}/{filename}",
        expires=timedelta(hours=1)
    )

def get_download_url(project_id: str, filename: str) -> str:
    """Генерирует presigned URL для скачивания (GET)"""
    return client.presigned_get_object(
        bucket_name="projects",
        object_name=f"{project_id}/{filename}",
        expires=timedelta(hours=24),
        response_headers={
            "response-content-disposition": f'attachment; filename="{filename}"'
        }
    )

def list_project_files(project_id: str) -> list:
    """Список файлов проекта"""
    objects = client.list_objects(
        "projects",
        prefix=f"{project_id}/",
        recursive=True
    )
    return [
        {
            "name": obj.object_name.split("/")[-1],
            "size": obj.size,
            "modified": obj.last_modified.isoformat()
        }
        for obj in objects
    ]

На фронтенде загрузка идёт напрямую в MinIO через presigned URL, без нагрузки на бэкенд. Файл в 2 GB загружается за 30 секунд по локальной сети 10 Gbps.

Бэкап в облако и мониторинг

Локальное хранилище — это не бэкап. Мы настроили репликацию критичных данных в Yandex Object Storage (S3-совместимый):

# Настройка репликации в облако через mc mirror
# Добавляем облачный endpoint
mc alias set yandex https://storage.yandexcloud.net ACCESS_KEY SECRET_KEY

# Создаём бакет в облаке
mc mb yandex/mediastock-backup

# Однократная синхронизация
mc mirror --overwrite myminio/projects yandex/mediastock-backup/projects

# Автоматическая репликация через cron (каждые 6 часов)
# /etc/cron.d/minio-backup
0 */6 * * * root /usr/local/bin/mc mirror --newer-than 7h \
  myminio/projects yandex/mediastock-backup/projects \
  >> /var/log/minio-backup.log 2>&1

Мониторинг через Prometheus:

# Включаем метрики MinIO
mc admin prometheus generate myminio

# prometheus.yml — добавляем scrape target
scrape_configs:
  - job_name: minio
    metrics_path: /minio/v2/metrics/cluster
    scheme: https
    static_configs:
      - targets: ['s3.mediastock.local:9000']
    bearer_token: <generated-token>

Ключевые метрики в Grafana:

  • minio_node_disk_free_bytes — свободное место на дисках (алерт при <15%).
  • minio_s3_requests_total — количество запросов по типам (GET/PUT/DELETE).
  • minio_node_disk_io_seconds — задержки дисковых операций (алерт при >100 мс).
  • minio_cluster_health — здоровье erasure sets.

Результаты и выводы

Через 2 недели после развёртывания MinIO «МедиаСток» полностью перешёл с NAS на объектное хранилище:

МетрикаNAS (Synology)MinIO
Скорость чтения (10 пользователей)30 MB/s450 MB/s
Скорость загрузки80 MB/s600 MB/s
Удалённый доступТолько VPNHTTPS + presigned URLs
ОтказоустойчивостьRAID 5 (1 диск)Erasure 4+4 (4 диска)
БэкапНетОблако каждые 6 часов
API для приложенийНет (только SMB)S3 API

Главные выводы из проекта:

  • MinIO — идеальный выбор для объёмов от 1 ТБ до 1 ПБ. Для больших объёмов стоит смотреть на Ceph.
  • Erasure coding надёжнее RAID и не требует контроллера.
  • Presigned URLs решают проблему раздачи файлов клиентам без сложной инфраструктуры.
  • Обязательно настраивайте бэкап в облако — локальное хранилище не защищает от физических катастроф.

Если вам нужно S3-совместимое хранилище без облачных расходов, мы в itfresh.ru поможем с развёртыванием и настройкой MinIO под вашу задачу.

Часто задаваемые вопросы

MinIO предоставляет S3 API для интеграции с приложениями, erasure coding для отказоустойчивости без RAID-контроллера, presigned URLs для безопасной раздачи файлов и горизонтальное масштабирование. NAS ограничен протоколами SMB/NFS и масштабируется только вертикально.
Минимум — 4 диска для erasure coding. Оптимально — 8 или 16 дисков на ноду. Чем больше дисков, тем выше производительность и отказоустойчивость. Для single-node конфигурации мы рекомендуем 8 дисков с erasure coding 4+4.
Да, MinIO совместим с S3 API на 99%. Все S3-клиенты (AWS SDK, boto3, mc) работают с MinIO без изменений. Единственное ограничение — некоторые продвинутые фичи AWS (S3 Select, S3 Inventory) не поддерживаются. Для большинства задач MinIO полностью заменяет облачный S3.
MinIO поддерживает добавление серверных пулов (server pools) к существующему кластеру без простоя. Вы добавляете новый набор серверов с дисками, и MinIO автоматически начинает использовать дополнительную ёмкость. Старые данные не перемещаются — балансировка происходит только для новых объектов.
Зависит от нагрузки. Для медиафайлов (большие объекты, последовательное чтение/запись) HDD достаточно. Для мелких объектов (миллионы файлов по 1-100 КБ) нужен NVMe SSD. В нашем кейсе медиафайлы от 5 МБ до 50 ГБ — HDD Seagate Exos обеспечили 600 MB/s на запись при 8 дисках.

Нужна помощь с проектом?

Специалисты АйТи Фреш помогут с архитектурой, DevOps, безопасностью и разработкой — 15+ лет опыта

📞 Связаться с нами
#minio#s3#объектное хранилище#erasure coding#bucket policies#presigned urls#ceph#медиафайлы
Комментарии 0

Оставить комментарий

загрузка...