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 CE | 11.0.13, Docker (seafileltd/seafile-mc) |
| JuiceFS | 1.3.1, FUSE mount, LZ4 compression, block-size 4MB |
| S3 Storage | reg.ru S3 (s3.regru.cloud), bucket: seafile-blocks |
| Redis | 7-alpine, JuiceFS metadata store, AOF persistence |
| MariaDB | 10.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)

| Size | T1 | T2 | T3 | T4-T6 avg | Mean |
| 1 MB | 2.2 | 0.9 | 2.5 | 2.1 | 2.0 |
| 10 MB | 4.8 | 14.1 | 18.9 | 6.0 | 9.3 |
| 50 MB | 9.5 | 17.5 | 20.7 | 8.2 | 12.1 |
| 100 MB | 6.7 | 10.1 | 8.7 | 8.8 | 8.7 |
| 500 MB | 6.1 | 7.7 | 7.0 | 6.5 | 6.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)
| Op | Size | Mean (MB/s) | Overhead vs Seafile |
| Upload | 100 MB | 68.5 | 7.9x slower |
| Upload | 500 MB | 36.8 | 5.7x slower |
| Download | 100 MB | 65.1 | 2.1x slower |
| Download | 500 MB | 43.4 | 2.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. Подводные камни и решения
- JuiceFS mount внутри Docker нестабилен. FUSE mount теряется при перезапуске контейнера. Решение: монтировать на хосте, пробрасывать через bind mount.
- Redis теряет данные при заполнении диска. Когда диск 100%, AOF write fail → потеря JuiceFS metadata → нужен повторный format. Решение: мониторинг диска, автоочистка блоков.
- network_mode: host ломает Docker DNS. Сервисные имена (db, memcached) не резолвятся. Нужно sed -i по всем конфигам после каждого recreate.
- Seafile deduplication мешает тестированию. Один и тот же файл не загружается повторно. Для нагрузочных тестов нужен /dev/urandom для каждого файла.
- seaf-server стартует до JuiceFS mount. Блоки пишутся на локальный диск (250+ MB/s — явный признак). Решение: custom entrypoint или host-mount.
- Upload link содержит внешний IP. При загрузке с localhost нужен sed replace: http://EXTERNAL_IP → http://localhost.
5. Прогноз производительности

| Метод | Скорость | 5 ТБ |
| Seafile API (1 thread) | 6.5 MB/s | 9 дней |
| Seafile API (3 threads) | 17.3 MB/s | 3.5 дня |
| Direct S3 (aws cli) | 36.8 MB/s | 1.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