Масштабирование хранилища изображений: от одного сервера до распределённого кластера | AdminWiki

Масштабирование хранилища изображений: от одного сервера до распределённого кластера

11 сентября 2026 12 мин. чтения
Содержание статьи

Когда пора масштабировать хранилище изображений

Хранилище изображений на одном сервере TrueNAS с четырьмя HDD в RAIDZ1 и 32 ГБ RAM перестаёт держать нормальные задержки, когда сервис выходит на 1500-2500 запросов в минуту и 400-500 одновременных пользователей. Диски уходят в 90-95% утилизации, среднее время ответа на чтение растёт с 10 до 100 мс.

Прямой ответ: пока %util дисков не превышает 60%, чтение укладывается в 10-15 мс, а пул ZFS заполнен меньше чем на 80%, одного сервера достаточно. Помогают SSD-кэш, recordsize под крупные файлы, сжатие lz4 и настройка Nginx. Когда диски стабильно держат 80% и выше, а сеть подходит к пределу 1 GbE, вертикальный рост упирается в цену: каждый следующий терабайт обходится дороже предыдущего, а единая точка отказа остаётся на месте.

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

Ключевые метрики и пороговые значения

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

МетрикаНормаПора расширятьсяЧем смотреть
Утилизация дисков, %utilдо 60%стабильно больше 80%iostat -x 1
Задержка чтения и записи5-15 мсбольше 20 мс на 95-м перцентилеiostat -x 1, fio
Заполнение пула ZFSдо 70%больше 80%, падает скорость записиzpool list, zfs list
Сетевой throughputдо 50% каналабольше 70% от 1 GbE или 10 GbEsar -n DEV 1, iftop
iowait процессорадо 20%больше 40% продолжительное времяmpstat 1, top
Время ответа API отдачи изображениядо 50 мсбольше 100 мсnginx stub_status, Prometheus
Открытые файловые дескрипторыдо 50% лимитабольше 80% лимитаlsof, node_exporter

Для ZFS добавьте zpool iostat -v 1: колонка busy показывает загрузку каждого диска, bw даёт реальную пропускную способность пула. Разбивку нагрузки по ядрам снимает mpstat 1, состояние контейнеров с хранилищем - docker stats. Постоянный мониторинг удобнее держать в Prometheus с Grafana или в Zabbix: разовые замеры пропускают пики, а именно пики ломают сервис.

Отдельно считайте объём метаданных. Миллион объектов в одном бакете MinIO или в одном каталоге NFS замедляет листинг и обход дерева даже при свободном месте на дисках. Если число файлов растёт быстрее объёма, узкое место окажется в метаданных, а не в дисковом I/O.

Пример из практики: когда одного сервера перестало хватать

Сервис изображений на TrueNAS SCALE: пул ZFS из четырёх HDD по 8 ТБ в RAIDZ1, 32 ГБ RAM, доступ по NFS и раздача через Nginx с локальным кэшем. На старте 200 пользователей и 300 запросов в минуту, задержка чтения 10 мс, %util дисков 35%.

Через год нагрузка выросла до 500 одновременных пользователей и 2000 запросов в минуту. Задержка поднялась до 100 мс, %util держался на 95%, iowait 45%. Первый шаг: два NVMe под SLOG и L2ARC, память до 64 ГБ, recordsize=1M и compression=lz4. Задержка вернулась к 25-30 мс. Эффекта хватило на три месяца.

Дальше упёрлись в физику: четыре диска не выдают больше 450 IOPS случайного чтения, а 1 GbE ограничивает отдачу 110 МБ/с. Вертикальный путь дал отсрочку, но не убрал единую точку отказа. Итогом стал кластер MinIO из четырёх узлов с erasure coding 8+4.

Вертикальное масштабирование: наращивание ресурсов одного сервера

Вертикальный рост - самый быстрый способ выиграть время. Вы добавляете диски в пул, память, NVMe под журналы и кэш, настраиваете параметры ядра и Nginx. Простой минимален, приложение переписывать не нужно.

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

Аппаратные ограничения и экономическая целесообразность

Считайте потолок до покупки. Сервер с восемью слотами под HDD: пул RAIDZ2 из шести дисков по 16 ТБ даёт 96 ТБ сырого объёма и около 64 ТБ полезного. Два оставшихся слота разумно держать под горячий резерв или SSD. Дальше объём растёт только заменой дисков на более ёмкие, а это перезапись всего пула.

Экономика ломается на памяти и процессоре. Переход с 64 на 128 ГБ ECC с заменой всех модулей стоит как половина нового сервера. Линии PCIe тоже конечны: NVMe-кэш, сетевые карты 25 GbE и HBA делят один и тот же пул линий.

Вертикальное масштабирование оправдано, когда ожидаемый рост нагрузки не превышает двух-трёх раз, бюджет ограничен, а требования к доступности допускают плановые окна обслуживания на 1-2 часа. Если сервис обещает 99,9% доступности или объём изображений растёт на терабайты в месяц, пора смотреть на кластер.

Настройка TrueNAS и ZFS перед масштабированием

Порядок действий для пула с изображениями размером 200 КБ - 5 МБ:

  • recordsize под размер файла: zfs set recordsize=1M tank/images. Значение по умолчанию 128 КБ даёт лишние операции чтения при отдаче крупных JPEG.
  • Сжатие: zfs set compression=lz4 tank/images. JPEG и PNG уже сжаты, но lz4 почти не тратит CPU и экономит место на миниатюрах и служебных данных.
  • Отключение лишних записей: zfs set atime=off tank/images.
  • SLOG под синхронную запись: zpool add tank log mirror /dev/nvme0n1 /dev/nvme1n1. Нужен, если приложение пишет через NFS с sync=always или ведёт журнал метаданных.
  • L2ARC под чтение: zpool add tank cache /dev/nvme2n1. Индекс кэша живёт в RAM и занимает 1-2% его объёма, поэтому L2ARC имеет смысл начиная с 64 ГБ памяти.
  • Ядро: vm.dirty_background_ratio=5 и vm.dirty_ratio=20 через sysctl, чтобы грязные страницы не копились и не вызывали залповые сбросы на диск.
  • Nginx перед хранилищем: sendfile on, tcp_nopush on, open_file_cache max=10000 inactive=60s. Это снимает с дисков часть обращений к одним и тем же файлам.

Ошибки, которые ломают всё: SLOG из одного SSD без зеркала (отказ устройства при потере питания ведёт к потере подтверждённых записей) и L2ARC на сервере с 32 ГБ RAM, где кэш вытесняет ARC. Эффект от каждой настройки подтверждайте замерами до и после.

Горизонтальное масштабирование: переход к распределённому кластеру

Кластер даёт то, чего не даст ни один одиночный сервер: продолжение работы при отказе узла и рост производительности вместе с объёмом. Добавляя узел, вы получаете диски, сеть и процессор для erasure coding одновременно.

Практических путей два. MinIO - объектное хранилище с S3 API, разворачивается за вечер. Ceph - универсальная платформа: объектное (RGW), блочное (RBD) и файловое (CephFS) хранилище в одном кластере, но с заметно более высоким порогом входа.

Ceph: архитектура и компоненты

Кластер Ceph собирается из демонов:

  • MON - мониторы, хранят карту кластера и держат кворум. Нужно три штуки, пять для крупных инсталляций.
  • MGR - менеджер, метрики и модули управления.
  • OSD - по одному демону на диск, хранят данные и отвечают за репликацию.
  • MDS - метаданные CephFS, нужен только для файлового доступа.
  • RGW - шлюз S3 и Swift для объектного доступа.

Данные не привязаны к узлам. CRUSH-карта описывает иерархию (диск, хост, стойка) и вычисляет, на какие OSD положить каждую копию. Объект попадает в placement groups (PG), и каждая PG живёт на нескольких OSD. Пулы задают уровень избыточности: size 3 и min_size 2 означают три копии и продолжение записи при двух доступных OSD.

Развёртывание через cephadm: cephadm bootstrap --mon-ip 10.0.0.11, затем ceph orch apply osd --all-available-devices. Пул под изображения: ceph osd pool create images 128 128, ceph osd pool set images size 3, ceph osd pool set images min_size 2. Состояние всегда проверяйте командой ceph -s: HEALTH_OK и отсутствие PG в состоянии inactive. Минимум для продакшена: три MON, шесть OSD на разных хостах, сеть 10 GbE и отдельные NVMe под журналы OSD. Разбор компонентов и критериев выбора собран в статье про Ceph как распределённую систему хранения.

MinIO: простота и S3-совместимость

MinIO закрывает задачу хранения изображений с доступом по HTTP(S): S3 API, presigned URL, версионирование, lifecycle-политики. Одиночный узел поднимается одной командой: docker run -d -p 9000:9000 -p 9001:9001 -v /mnt/data:/data minio/minio server /data --console-address ":9001".

Распределённый режим запускается на всех узлах одинаково: minio server http://node{1...4}/data{1...8}. Данные раскладываются по дискам через erasure coding, типовые схемы избыточности 8+4 и 4+2. Отдельного RAID не требуется, диски подключаются как JBOD. Управление и диагностика идут через mc: mc admin info, mc admin heal, mc replicate. Выбор между MinIO, Ceph RGW и TrueNAS S3 по производительности и стоимости владения описан в статье про S3-совместимые хранилища.

Шардирование и репликация данных

Шардирование распределяет объекты между узлами, репликация создаёт копии. В Ceph изображение 1 МБ на входе RGW превращается в объект RADOS, который через CRUSH привязывается к набору PG, а каждая PG размещается на трёх OSD из разных хостов и стоек. Сеть получает тройной объём записи, зато отказ диска или целого узла не прерывает чтение: клиент берёт копию с живого OSD, а кластер восстанавливает потерянную копию в фоне.

В MinIO работает erasure coding: объект режется на блоки данных и блоки чётности, например 8+4. Потеря до четырёх дисков из двенадцати в наборе не приводит к потере данных, восстановление запускается командой mc admin heal. При репликации между площадками (mc replicate) копии синхронизируются асинхронно, поэтому между сайтами возможна согласованность в конечном счёте. Внутри одного кластера S3 API отдаёт сильную согласованность: запись становится видна последующим чтениям сразу.

Практическое следствие: не смешивайте роли. Отдельный пул или бак под миниатюры с высокой частотой запросов и отдельный под оригиналы с редким доступом экономят и диски, и CPU на erasure coding.

Узкие места при масштабировании хранилища изображений

Кластер меняет профиль нагрузки: то, что раньше упиралось в шпиндели, теперь упирается в сеть, процессор erasure coding и метаданные.

Сетевые задержки и пропускная способность

При репликации 3x каждый байт записи уходит в сеть трижды, при erasure coding 8+4 накладные расходы составляют около 50% плюс служебный трафик восстановления. Минимум для Ceph - 10 GbE, для высоких нагрузок 25 GbE. Пропускную способность проверяйте до развёртывания: iperf3 -c ip-узла -P 4, задержку - ping с размером пакета 9000 байт. Jumbo frames (MTU 9000) на всех коммутаторах и интерфейсах снижают нагрузку на CPU, но включение MTU только на части узлов приводит к фрагментации и падению скорости.

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

Дисковый I/O и выбор оборудования

Для ёмкости берите HDD, для журналов и кэша NVMe. Типовая конфигурация на три узла: в каждом восемь HDD по 8 ТБ и два NVMe по 1 ТБ под DB и WAL для OSD. Erasure coding дополнительно нагружает процессор, поэтому 8+4 на слабых CPU даёт меньшую скорость, чем 4+2. В MinIO диски подключаются без RAID: избыточность обеспечивает сам код, а RAID-контроллер только добавляет точку отказа и скрывает SMART-данные.

Работа с метаданными

В Ceph метаданные CephFS обслуживает MDS, и его пул разумно выносить на отдельные SSD: операции создания и переименования файлов чувствительны к задержке. В MinIO метаданные распределены по тем же дискам, что и данные, и проблема проявляется иначе: 10 млн изображений в одном бакете замедляют листинг до секунд.

Решение простое: раскладывайте объекты по префиксам. Ключ вида images/2026/09/11/ab12cd.jpg даёт равномерное распределение по внутренним наборам и быстрый list с префиксом. Для повторяющихся обращений полезен кэширующий слой: Nginx с proxy_cache или CDN перед бакетом снимает до 80% чтений с кластера.

Практические шаги по миграции на распределённое хранилище

Миграция разбивается на пять этапов, каждый проверяется отдельно.

Подготовка и развёртывание кластера

  1. Выберите технологию: MinIO для S3-доступа к изображениям, Ceph при потребности в блочном и файловом доступе рядом.
  2. Подготовьте серверы: одинаковое железо, отдельные NVMe под журналы, сеть 10 GbE и выше, синхронизация времени через NTP или chrony.
  3. Разверните ПО. Для MinIO достаточно бинарника и systemd-юнита с параметром ExecStart, для Ceph - cephadm bootstrap с последующим добавлением хостов.
  4. Соберите тестовый стенд: четыре виртуальные машины с тем же набором дисков дадут корректную оценку поведения erasure coding и репликации до закупки железа. Быстрее и дешевле поднять эти узлы в облаке: Timeweb Cloud позволяет заказать серверы и VDS с почасовой оплатой и удалить стенд сразу после проверки.

Перенос данных и переключение трафика

Синхронизация с TrueNAS в MinIO через rclone: rclone sync /mnt/tank/images s3:images --transfers 16 --checkers 32 --progress. Первый проход занимает часы, последующие инкрементальные - минуты. Перед переключением обязательно сверьте контрольные суммы: rclone check /mnt/tank/images s3:images --size-only, для критичных данных без флага size-only.

Трафик переводится на кластер через Nginx: в блоке upstream перечислите адреса всех узлов MinIO с параметром max_fails и fail_timeout, а старый сервер TrueNAS оставьте как резервный upstream с флагом backup. Переключение делайте в два шага: сначала на 1% пользователей, затем на всех. DNS с малым TTL или изменение upstream в конфигурации балансировщика дают откат за минуты.

Проверка и настройка после миграции

  • Целостность: сравните выборку из 1000 файлов по хешу между старым сервером и кластером.
  • Производительность: fio на запись и чтение случайными блоками, s3bench для задержек API. Сравните с метриками исходного сервера, а не с ожиданиями.
  • Отказоустойчивость: погасите один узел и убедитесь, что чтение продолжается, а кластер возвращается в HEALTH_OK после включения.
  • Мониторинг: Prometheus плюс Grafana с алертами на заполнение пулов выше 75%, PG в состоянии inactive, рост задержки S3 выше 100 мс и число незавершённых восстановлений.

Сравнение решений: TrueNAS, Ceph и MinIO

КритерийTrueNASCeph RGWMinIO
Сложность настройкинизкая, веб-интерфейсвысокая, отдельный кластер и командасредняя, конфигурация из командной строки
S3 APIограниченныйполныйполный, основная точка доступа
Горизонтальное масштабированиев пределах одного узладо сотен узловот 4 узлов и выше
ОтказоустойчивостьRAIDZ, один узелрепликация или EC, до 2 отказов без потери данныхerasure coding 4+2 или 8+4
Минимум для старта1 сервер3 узла, 6 OSD, 10 GbE4 узла или 4 диска
Сильная сторонабыстрый запуск, NFS и SMB из коробкиуниверсальность: объекты, блоки, файлыпростота и высокая скорость мелких объектов
Слабые местаединая точка отказа, потолок одного узлапорог входа, требования к сети и метаданнымтолько объектный доступ
Когда выбиратьнебольшой проект, до 500 одновременных пользователейKubernetes, OpenStack, смешанные задачихранилище изображений с раздачей по HTTP(S)

Разницу между объектным, блочным и файловым доступом и типичные ошибки выбора разбирает материал про типы хранилищ. Для типового сервиса изображений MinIO даёт лучший баланс: S3 API, erasure coding и предсказуемая скорость отдачи мелких файлов.

Заключение

Порядок действий для растущего хранилища изображений выглядит так. Замерьте %util дисков, задержку, заполнение пула и пропускную способность сети, сравните с порогами из таблицы. Если запас есть, выжмите текущий сервер: recordsize, lz4, SLOG и L2ARC на NVMe, кэш Nginx. Если диски держат 80% и выше, а сеть подошла к пределу канала, планируйте кластер и начинайте с тестового стенда на четырёх узлах.

Миграцию ведите через rclone с проверкой контрольных сумм и переключением трафика в Nginx двумя шагами. После перевода трафика оставьте старый сервер в резерве минимум на месяц и настройте алерты на заполнение пулов, задержку S3 и незавершённые восстановления. Общие подходы к работе с большими объёмами и переносу данных без потерь собраны в руководстве про хранение больших данных и миграцию.

Поделиться:
Сохранить гайд? В закладки браузера