Задать вопрос
@alenov
Программист

Как масштабировать сервис хранения картинок?

Есть микросервис для хранения файлов картинок. Допустим, его url - s1.site.ru
Устроен просто:
- загрузка файлов по API с помощью POST-запроса s1.site.ru/upload
- удаление файлов по API с помощью POST-запроса s1.site.ru/delete или GET-запроса s1.site.ru/delete/some_filename.jpg
- получение файлов s1.site.ru/storage/some_filename.jpg

Теперь этот сервис нужно горизонтально масштабировать + повесить на него ssl сертификат.
Допустим, я поднимаю ещё несколько таких сервисов, например s2.site.ru, s3.site.ru, ... - и их нужно как-то объединить.

Очевидное решение, которое приходит в голову: поднять балансировщик с адресом типа https://files.site.ru, прикрутить к нему ssl-сертификат, и проксировать запросы (уже без ssl) по сервисам.

API запрос https://files.site.ru/upload распределяется по сервисам, используя вес сервиса. Он должен вернуть клиенту абсолютный url загруженного файла, например "https://files.site.ru/storage/some_filename.jpg".
Запрос https://files.site.ru/storage/some_filename.jpg должен сразу проксироваться на тот сервис, на котором этот файл лежит. Как это сделать? Может, some_filename.jpg должно ещё что-то в себе содержать, какой-то ключ, чтобы балансировщик знал, куда отправить запрос? Или есть более умное решение?
То же самое относится и к https://files.site.ru/delete/some_filename.jpg

Буду очень признателен, если кто-то скажет, каким балансировщиком и как это реализовать, и нарисует пример конфига, хотя бы схематично, а то я себе его неясно представляю)

ГОТОВЫЕ РЕШЕНИЯ ТИПА S3 ИЛИ ПОДОБНЫЕ НЕ РАССМАТРИВАЮТСЯ!
  • Вопрос задан
  • 599 просмотров
Подписаться 10 Сложный 17 комментариев
Решения вопроса 1
@alenov Автор вопроса
Программист
Спасибо всем, кто дал полезные советы по существу вопроса. Ещё раз кратко. Имеется микросервис хранения картинок, который нужно масштабировать.
Решение получилось следующим. Микросервис поднимается на нескольких серверах:
s1.site.ru
s2.site.ru
s3.site.ru

Также поднимается nginx-балансировщик s.site.ru:

# Список серверов для балансировки запросов на запись (upload).
upstream storage_backend {
    server s1.site.ru:80;
    server s2.site.ru:80;
    server s3.site.ru:80;
}

# Определение локации сервера для запросов на запись и удаление файла. Опция default нужна
# только для того, чтобы в случае неопределённого запроса получить 404, а не 500.
map $uri $storage_location {
    "~/(storage|delete)/s1-"  "s1.site.ru:80";
    "~/(storage|delete)/s2-"  "s2.site.ru:80";
    "~/(storage|delete)/s3-"  "s3.site.ru:80";
    default                           "s1.site.ru:80";
}

server {
    listen 80;
    server_name s.site.ru;

    location / {
        proxy_pass http://$storage_location;
    }

    location /upload/ {
        proxy_pass http://storage_backend;
    }

}


В блоке map часть паттерна "s1...s2...s3" - это префикс имени файла, который формируется и возвращается сервером, выполнившим запрос на запись. Т.е. сервер, записывающий файл, цепляет к имени префикс, по которому затем в этом блоке можно будет однозначно определить локацию. Это добавляет лишних хлопот при добавлении нового сервиса в пул: нужно "ручками" назначить ему этот префикс так, чтобы он был уникальным. Есть в этом какая-то неровность, но я не знаю в данный момент способа это обойти.

В итоге при поступлении запросов на запись, типа POST s.site.ru/upload балансировщик выбирает очередной сервер из storage_backend, а при запросах на получение по uri определяет локацию по $storage_location.

Ну и ssl прикручивается только к балансировщику, стандартно.

Сделал небольшой тестовый пул локально, всё работает. Буду пробовать на реальном железе.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 4
zoonman
@zoonman
⋆⋆⋆⋆⋆
Итак, учим матчасть:


Вам не нужно иметь миллион разных доменов и десяток сертфикатов.
Решение - нормальный балансировщик.

А в остальном, лучше всего сделать что-то в стиле:

GET https://f.s.com/path/to/file.ext скачать
POST  https://f.s.com/path/to/file.ext закачать
PUT  https://f.s.com/path/to/file.ext заменить
DELETE  https://f.s.com/path/to/file.ext удалить


На s.com можно лендинг/приложеньку повесить.

А в целом, прочитайте про NIH и начните использовать Ceph.
Ответ написан
uvelichitel
@uvelichitel
habrahabr.ru/users/uvelichitel
Может, some_filename.jpg должно ещё что-то в себе содержать, какой-то ключ
Можно использовать some_filename как ключ. Выберите хеш-функцию которая будет равномерно проецировать строку символов some_filename на адреса сервисов.
Ответ написан
@BorisKorobkov
Web developer
В простом варианте:

Загружать файл на files.site.ru, а он уже по некоторму алгоритму (рандомно, остаток от деления свертки на кол-во серверов, с учетом веса/нагрузки/работоспособности серверов или еще как-то) загружает файл на выбранный сервер и возвращает прямую ссылку на него. Если пропускная способность files.site.ru ограничена, тогда у него только спрашивать про сервер, а загружать напрямую на указанный.

Чтение происходит напрямую с сервера, куда загружен файл. Без files.site.ru, чтобы он не стал узким местом
Ответ написан
Комментировать
efrolov54rus
@efrolov54rus
Младший Web-разработчик (принимаю заказы)
Обычно подобные субдомены (s1, s2, и т.п) создаются для того чтобы балансировать нагрузку на файловый сервер, используя для этого промежуточные кэширующие сервера.
Предположим что у нас есть несколько кэширующих серверов в разных локациях. Каждый из них постоянно передаёт статистику о своёй нагруженности на основной сервер.
При отдаче статики пользователю, скрипт на основном (бэкэнд) сервере обращается в кэш (например, Redis) и получает из него статистику загруженности серверов на текущий момент времени и например их расположение. После этого на основе IP выбирается самый близкий к пользователю сервер а затем выбирается наименее загруженный сервер из списка (пусть он называется s3). Таким образом клиент получает в ответе от скрипта подобный url: https://s3.example.org/cache/images/sample.jpg.
После этого происходит запрос на этот сервер и получение файла из кэша или напрямую из файлового сервера (при отсутствии файла на кэш сервере).
Удалять файлы физически КРАЙНЕ не рекомендуется (особенно если это происходит часто), вместо этого стоит использовать флаг скрытия на программном уровне
Для надёжности желательно использование RAID-5 на файловом сервере. В случае полного хайлоада можно создать несколько репликационных файловых серверов и объединить их в один кластер.
Более подробно, можно прочитать здесь:
https://winitpro.ru/index.php/2013/09/25/ustanovka...

PS Поправьте меня если я ошибаюсь
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы