1. Постановка задачи

Требовалось развернуть корпоративное файловое хранилище с объёмом ~5 ТБ (с запасом до 100 ТБ), поддержкой версионирования, WebDAV-доступа и десктопной синхронизацией. Только open-source.

Проблема: Seafile Community Edition не поддерживает S3-backend напрямую (это функция Pro-версии). Блоки хранятся только на локальной файловой системе. Нужен способ прозрачно подменить локальное хранилище на S3-совместимое объектное хранилище.

2. Архитектура решения

Рис. 1. Архитектура: Seafile CE → JuiceFS (FUSE) → S3 Object Storage

2.1 Стек технологий

КомпонентВерсия / Детали
Seafile CE11.0.13, Docker (seafileltd/seafile-mc)
JuiceFS1.3.1, FUSE mount, LZ4 compression, block-size 4MB
S3 Storagereg.ru S3 (s3.regru.cloud), bucket: seafile-blocks
Redis7-alpine, JuiceFS metadata store, AOF persistence
MariaDB10.6, Seafile metadata
ОСUbuntu 22.04/24.04, Yandex Cloud + reg.ru VPS

2.2 Ключевая проблема: EXDEV

Seafile CE в block-backend-fs.c создаёт временные файлы в /tmp, затем вызывает rename() для перемещения в директорию блоков. Когда блоки находятся на JuiceFS (FUSE mount), rename() возвращает EXDEV (Invalid cross-device link) — перемещение между разными файловыми системами невозможно.

2.3 Решение: LD_PRELOAD rename interceptor

Мы написали shared library, которая перехватывает syscall rename() через LD_PRELOAD. При получении EXDEV выполняется fallback: copy (через sendfile()) + delete. Это стандартный паттерн, используемый в coreutils mv.

#define _GNU_SOURCE
#include <dlfcn.h>
#include <sys/sendfile.h>

int rename(const char *old, const char *new) {
    orig_rename_func orig = dlsym(RTLD_NEXT, "rename");
    int ret = orig(old, new);
    if (ret == -1 && errno == EXDEV) {
        // fallback: sendfile() + unlink()
        int src = open(old, O_RDONLY);
        int dst = open(new, O_WRONLY|O_CREAT|O_TRUNC);
        sendfile(dst, src, &off, size);
        close(dst); close(src); unlink(old);
        return 0;
    }
    return ret;
}

Компиляция: gcc -shared -fPIC -o rename_fix.so rename_fix.c -ldl

2.4 Docker-конфигурация

Критически важно: JuiceFS монтируется на хосте, а не внутри контейнера. FUSE mount внутри Docker нестабилен и теряется при перезапуске. Bind mount с хоста в контейнер — надёжное решение.

seafile:
  image: seafileltd/seafile-mc:11.0-latest
  privileged: true
  network_mode: host
  volumes:
    - ./seafile-data:/shared
    - ./rename_fix.so:/usr/local/lib/rename_fix.so:ro
    - /mnt/juicefs-blocks:/shared/seafile/.../blocks:rw
  environment:
    LD_PRELOAD: /usr/local/lib/rename_fix.so

network_mode: host — необходим для доступа к Redis на localhost. Побочный эффект: Docker DNS не работает, все hostname (db, memcached) нужно заменить на 127.0.0.1 в конфигах Seafile.

3. Результаты бенчмарков

Проведено 6 полных циклов тестирования. Ниже — усреднённые результаты. Тестовый стенд: Yandex Cloud VM (2 vCPU, 2GB RAM, HDD) + reg.ru S3.

3.1 Upload Speed (Seafile API)

SizeT1T2T3T4-T6 avgMean
1 MB2.20.92.52.12.0
10 MB4.814.118.96.09.3
50 MB9.517.520.78.212.1
100 MB6.710.18.78.88.7
500 MB6.17.77.06.56.5

3.2 Download Speed

Средние значения: 1MB=3.7, 10MB=21.4, 50MB=57.4, 100MB=31.2, 500MB=18.7 MB/s. Пик на 50MB объясняется JuiceFS read-ahead cache (10GB, настроен через –cache-size 10240). На 500MB кэш не вмещает весь файл, скорость падает до уровня S3.

3.3 Direct S3 (baseline)

OpSizeMean (MB/s)Overhead vs Seafile
Upload100 MB68.57.9x slower
Upload500 MB36.85.7x slower
Download100 MB65.12.1x slower
Download500 MB43.42.3x slower

Overhead Seafile на upload: 5-8x — обусловлен цепочкой nginx → seahub → seaf-server → JuiceFS FUSE → S3. Download overhead значительно меньше (2x) благодаря кэшированию.

3.4 Concurrency Scaling

Оптимум — 3 потока (17.3 MB/s, ×2.5). При 10 потоках деградация до 13.5 MB/s — вероятно, contention на seaf-server (однопоточная индексация блоков) или rate limiting S3 endpoint.

3.5 Small Files IOPS

Стабильно ~3.1 ops/s. Каждая операция = 3 HTTP запроса (auth + upload_link + upload + commit). Для batch-загрузки мелких файлов рекомендуется архивирование в .tar/.zip перед загрузкой.

3.6 Latency & Integrity

TTFB: Cold start 160-530ms, warmed up 2ms. Среднее 25ms. Низкая латентность обусловлена JuiceFS metadata cache в Redis.

Data Integrity: 120/120 файлов (1-30MB, random data) — sha256 match после upload → download цикла. Zero data corruption.

4. Подводные камни и решения

  1. JuiceFS mount внутри Docker нестабилен. FUSE mount теряется при перезапуске контейнера. Решение: монтировать на хосте, пробрасывать через bind mount.
  2. Redis теряет данные при заполнении диска. Когда диск 100%, AOF write fail → потеря JuiceFS metadata → нужен повторный format. Решение: мониторинг диска, автоочистка блоков.
  3. network_mode: host ломает Docker DNS. Сервисные имена (db, memcached) не резолвятся. Нужно sed -i по всем конфигам после каждого recreate.
  4. Seafile deduplication мешает тестированию. Один и тот же файл не загружается повторно. Для нагрузочных тестов нужен /dev/urandom для каждого файла.
  5. seaf-server стартует до JuiceFS mount. Блоки пишутся на локальный диск (250+ MB/s — явный признак). Решение: custom entrypoint или host-mount.
  6. Upload link содержит внешний IP. При загрузке с localhost нужен sed replace: http://EXTERNAL_IP → http://localhost.

5. Прогноз производительности

МетодСкорость5 ТБ
Seafile API (1 thread)6.5 MB/s9 дней
Seafile API (3 threads)17.3 MB/s3.5 дня
Direct S3 (aws cli)36.8 MB/s1.6 дня

6. Выводы

Seafile CE + JuiceFS + S3 — рабочая, но не тривиальная связка. Upload overhead 5-8x по сравнению с прямым S3 — приемлемая цена за полноценный файловый менеджер с версионированием, WebDAV и десктопной синхронизацией.

Система стабильна при правильной конфигурации: JuiceFS на хосте (не в контейнере), LD_PRELOAD rename fix, мониторинг диска. Целостность данных подтверждена на 120 файлах — zero corruption.

Рекомендация: для production deployment использовать Seafile Pro (нативный S3-backend, без JuiceFS overhead) или рассмотреть Nextcloud + S3 как альтернативу. Текущая связка подходит для проектов с ограниченным бюджетом и готовностью к постоянной DevOps-поддержке.

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post

Recent posts

Quote of the week

"People ask me what I do in the winter when there's no baseball. I'll tell you what I do. I stare out the window and wait for spring."

~ Rogers Hornsby

Designed with WordPress