Отдача и ресайзинг фотографий
Задача - построить масштабируемую систему отдачи фотографий и их уменьшеных версий (замбнейлов, thumbnails, отресайзеных фоток…). Естественно все достаточно просто, когда нагрузки на эту подсистему низки, и решение впринципе не играет роли. Но когда у Вас по 20 картинок на страницу и несколько миллионов просмотров страниц в день?
Есть несколько вариантов решения данной задачи. И заключаются они, как в использовании технологий, так и построении архитектуры. Есть даже платные сервисные решения (удаленный хостинг и отдача изображений), но их мы рассматривать в рамках данной статьи не будем.
И так, подробнее
Проблемы
Для начала давайте разберемся, в чем же тонкости и специфика задачи по отдачи и ресайзингу фотографий:
- Ресайзинг - ресурсоемкий процесс, съедает процессорные ресурсы
- Хранение - хранение большого количества мелких файлов весьма безобидная на первый взгляд задача. Повышая нагрузки мы упирается в скорость записи на винчестер и его размер.
- Отдача - проблему отдачи файлов до определенного момента эффективно решает дисковый кеш. Но большая головная боль наступает, когда дисковый кеш перестает справляться с нагрузкой, и дисковая подсистема начинает работать крайне не эффективно (мелкие файлы - рэндомные чтения)
Декомпозиция и проблемы типичного решения
Функциональные узлы, которые на нужны для решения этой задачи:
- Хранилище
- Система ресайза картинок
- Web сервер для отдачи картинок и установки HTTP заголовков
Как Вы заметили ресайзер фотографий (рабочий сервер) у нас стоит промежуточным звеном между фронт-сервером и хранилищем. Это позволяет гибко управлять изменениями в размерах фотографий (удобно, когда Вам необходимо изменить размеры замбнейлов), кроме этого это позволяет эффективно использовать ресурсы хранилища и сервера ресайзинга, т.к. фотки, которые никогда не запрашиваются, ресайзиться не будут.
Если посмотрим на возможные узкие места этой архитектуры, то в каждом узле увидим SPOF проблему (единая точка отказа). Ниже они обозначены по порядку важности
Архитектурное решение
Для решения задачи масштабирования ресайзинга фоток, нам необходимо добавить узел балансирования ресайзеров. Таким узлом может выступить, например, проксирующий Web сервер или просто аппаратный балансировщик.
Обеспечив масштабирование рабочих серверов по ресайзингу фоток, мы уприраемся в ограничения диска хранилища. Тут есть два варианта:
- Использовать распределенные файловые системы (glusterfs, mogilefs и т.п.)
- Использовать “шардинг” картинок по серверам хранилища (в этом случае придется для каждой картинки хранить название сервера, на котором она находится)
Решив вопрос масштабирования рабочих серверов и хранилищ, мы встретимся с третьей проблемой - скорость отдачи. Суть ее заключается в чтении с диска большого количества мелких файлов (крайне не продуктивно). Это решается введением дополнительных узлов - кеширующих серверов. Отдача на них происходит из оперативной памяти - т.о. эффективность отдачи повышается на порядки. Кроме этого современные решения для кеширования позволяют балансировать запросы на несколько рабочих серверов.
Технологическое решение
Несколько замечаний о том, какие технологии можно использовать на определенных узлах, и что еще стоит сделать для обеспечения эффективной работы системы:
- В качестве фронт-сервера для отдачи картинок лучше использовать легкие сервера: nginx или lighttpd. Оба сервера эффективно работают с keep-alive соединениями, которые потребуются Вам при большом количестве фотографий/картинок на одну страницу. К тому же оба эти сервера умеют проксировать запросы (понадобиться для некоторых кеш серверов) и для них не соствит труда написать правила балансирования между кеш-серверами.
- В качестве кеширующего сервера стоит использовать varnish. Это узкопрофильный сервер, причем умеет балансировать бекенды. Кроме этого, если объем памяти позволяет Вам держать один сервер кеша, то можете смело избавляться от фронт-серверов и выводить varnish наружу
- При ограниченном количестве физических компьютеров можно настроить гибридную архитектуру. Например, кеширующий сервер будет нормально работать совместно с рабочим сервером. Кроме этого, Вы можете задействовать узлы такой системы и под другие задачи. Например, роль сервера отдачи может играть фронт-сервер, отдающий весь контент, рабочим сервером может выступать бекенд, генерирующий динамический контент, хранилище может быть общим хранилищем системы (фото, медиа, текстовые файлы и т.д.)
- Обязательно устанавливайте HTTP заголовки cache-control и expires в дальнее будущее, что-бы клиенты не запрашивали у серверов одни и те же картинки постоянно
- Не забудьте отключить gzip сжатие на web-серверах (если оно включено для всего остального контента) для картинок - про это часто забывают
- В случае огромной нагрузки на фронт-сервер, можно воспользоваться дополнительным балансировщиком и DNS round-robin + несколько одинаковых серверов на отдачу.
- Сохраняйте обработанные фотографии (версии разных размеров) на диск - это позволит избежать повторного ресайза при протухании файлов в кеше. Подумайте также об удобной организации структуры папок в хранилище, что-бы удобно было удалить все фотки определенного размера (нужно, когда размер замбнейлов меняется)


Здравствуйте, спасибо за такой качественный и хороший сайт по highload. Касательно темы, я не могу понять почему вы здесь используете кеширующий сервер varnish,почему сразу не использовать nginx, ведь он умеет кешировать. и тем самым отказаться от ненужного здесь дополнительного звена.
И еще один вопрос:
если одного кеширующего сервера не хватает и нужно ставить второй, как определить что хранится в кеше одного сервера а что у дургого? Или нужно делать разделение на основе данных, например картинки к новостям находятся на кешируемом сервер №1, все остальное на кешируемом сервере №2?
@Yuriy
Юрий, спасибо за комментарий!
1. Действительно, вместо варниша можно использовать nginx, т.к. он умеет работать, как обратый прокси сервер. Выбор пал в сторону варниша, т.к. предоставляет большую гибкость в настройке самой логики кеширования, т.к. предназначен для этой задачи. В nginx можно управлять только базовыми настройками. Так что, если глубокая настройка не нужна, можно смело пользовать nginx
2. Вы верно подметили, данные необходимо делить по серверам на уровне приложения (либо более низком). Впринципе, логика не обязательно должна быть привязана к сущностям высокого уровня (в этом случае будут трудности при попытке сбалансировать нагрузку на разные сервера). Удобнее будет выбирать сервер для новой картинки на основе, например, определенного хеша по ее имени, либо просто случайным образом и записывать этот номер (маркировка) в БД.
@Den Golotyuk
Понял, спасибо!
Очень классный блог по хайлоад! Кстати, здесь проскакивала мысль создания форума посвященного высоким нагрузкам, по моему, очень неплохая идея.