Архитектура Instagram

Instagram - всего лишь iOS, а теперь и Android, приложение для обмена фотографиями с друзьями. Последнее время находится на слуху благодаря новости о покупке проекта Facebook'ом за кругленькую сумму. Недавно один из основателей проекта, Mike Krieger, выступил на конференции с докладом о техническом аспекте проекта, который я и хотел бы вкратце пересказать.

Статистика

Начало:

  • 1 сервер слабее Macbook Pro
  • 25к регистраций в первый день
  • 2 разработчика

Сегодня:

  • 40+ миллионов пользователей
  • 100+ виртуальных серверов в EC2, в том числе:
  • Проект куплен Facebook за 1 млрд. долл
  • 1 миллион регистраций за 12 часов после запуска Android-версии
  • 5 разработчиков

Технологии

  • UbuntuLinux 11.04 - основная операционная система
  • Python - основной язык программирования серверной части
  • Django - фреймворк
  • Amazon:
    • EC2 - хостинг
    • ELB - балансировка входящих HTTP-запросов
    • Route53 - DNS
    • S3 - хранение фотографий
    • CloudFront - CDN
  • nginx - второй уровень балансировки входящихHTTP-запросов
  • gunicorn - WSGI-сервер
  • HAProxy - балансировка нагрузки внутри системы
  • PostgreSQL - основное хранилище данных
  • postgis - поддержка гео-запросов
  • pgfouine - отчеты на основе логов
  • pgbouncer - создание пула соединений
  • Redis - дополнительное хранилище данных
  • Memcached - кэширование
  • Gearman - очередь задач
  • Solr - гео-поиск
  • munin, statsd, pingdom - мониторинг
  • Fabric - управление кластером
  • xfs - файловая система

Философия

  1. Простота
  2. Минимизация операционных издержек
  3. Использование подходящих инструментов

История

  • Забыли сделать favicon.ico до запуска - в первый же день логи пестрили ошибками 404
  • Для хранения данных использовали просто Django ORM и PostgreSQL (из-за postgis)
  • Начали с одного слабого сервера, после успешного запуска решили переехать на EC2
  • Довольно быстро пришлось вынести СУБД на отдельный сервер (виртуальный, естественно)
  • Количество фотографий продолжало расти и расти, даже самый большой инстанс EC2 не справлялся
  • Решили вертикально разделить данные на несколько баз, с использованием механизма routers из ORM, параллельно избавившись от внешних ключей
  • Через несколько месяцев суммарный размер базы данных перевалил за 60Гб и перестало справляться и это решение
  • Следующим шагом стало горизонтальное разбиение данных (sharding):
  • Создали несколько тысяч логических баз данных.
  • Распределили их по существенно меньшему количеству физических серверов (читай: виртуальных машин).
  • Написали свой механизм определения где искать какую базу данных, с поддержкой миграции (вероятно тоже на основе routers).
  • По последним данным под PostgreSQL используется 12+12 виртуальных машин с максимальной оперативной памятью (68.4Гб), а также сетевые диски EBS, объединенные в программный RAID посредством mdadm. Это необходимо, чтобы весь массив данных помещался в памяти, EBS не в состоянии обеспечить достаточную производительность.
  • С некоторыми задачами лучше справляется Redis:
  • Для каждого пользователя в Redis есть список идентификаторов новых фотографий от других пользователей, на которых он подписан.
  • При отображении потока новых для пользователя фотографий делается выборка части такого списка, после чего посредством multiget достается подробная о них информация из memcached.
  • Пробовали возложить на него задачу хранения списков подписчиков, но в итоге вернулись к решению на PostgreSQL с небольшим кэшированием.
  • В Redis также хранится информация о сессиях.
  • Несколько фактов о Redis:
    • Так как все находится в памяти - очень быстрые операции записи и работы с множествами.
    • Является не заменой, а дополнением к основному хранилищу данных.
    • Redis хорош для структур данных, которые относительно ограничены.
    • Отлично подходит для кэширования комплексных структур данных, где нужно большее, чем просто получить значение по ключу (например - счетчики, подмножества, проверка вхождения в множества).
    • Механизм репликации (посредством slaveof) позволяет легко масштабировать операции чтения.
  • Пользователи синхронно загружают фотографии на медиа-сервер с (опциональными) заголовком и месте на карте, все остальное происходит асинхронно посредством очередей, например:
    • Сохраняются гео-метки, обновляется Solr (который впоследствии заменил postgis).
    • Идентификатор нового фото добавляется в обсуждавшиеся выше списки для всех подписчиков автора.
  • Поначалу использовали Apache + mod_wsgi для запуска Django, впоследствии перешли к gunicorn из-за меньшего потребления ресурсов и простоты настройки.
  • С недавних пор начали использовать Amazon ELB вместо DNS round-robin для первичной балансировки входяших HTTP-запросов, что позволило:
  • избежать необходимости дешифровки SSL посредством nginx;
  • ускорить исключение из балансировки проблемных серверов.
  • Благодаря использованию xfs есть возможность "замораживать" и "размораживать" дисковые массивы при резервном копировании.

Подводим итоги

  • Многие проблемы с масштабируемостью - результат банальных человеческих ошибок.
  • Масштабирование = замена всех деталей в машине на скорости 150 км/ч.
  • Заранее сложно узнать как в основном будут обращаться к данным, без реального использования.
  • В первую очередь попытайтесь адаптировать известные Вам технологии и инструменты для создания простого и понятного решения, прежде чем бросаться на поиски чего-то нетривиального.
  • Дополните свое основное хранилище более гибким компонентом, вроде Redis.
  • Постарайтесь не использовать два инструмента для решения одной и той же задачи.
  • Оставайтесь гибкими и ловкими = напоминайте себе о том, что на самом деле имеет значение.
  • Разрабатывайте решения, к которым не придется постоянно возвращаться из-за их сбоев.
  • Активное юнит- и функциональное тестирование стоят потраченного на них времени.
  • DRY: не делайте одну и ту же работу несколько раз.
  • Слабая связанность посредством уведомлений или сигналов позволяет легко менять структуру проекта.
  • Дисковый ввод-вывод часто оказывается узким местом, особенно на EC2.
  • Спускаться до C нужно только при необходимости, большую часть работы лучше делать в Python.
  • Короткий цикл разработки - залог быстрого развития.
  • Частые совместные рассмотрения кода нужны, чтобы все были в курсе происходящего.
  • Не изобретайте велосипед.
  • Окружите себя с толковыми консультантами.
  • Культура открытости вокруг разработки.
  • Делитесь с opensource сообществом.
  • Фокусируйтесь на том, что вы делаете лучше всего.
  • Вашим пользователям абсолютно без разницы, написали ли Вы собственную СУБД или нет.
  • Не переоптимизируйте и не предполагайте заранее как сайт будет расти.
  • Не рассчитывайте, что "кто-то еще присоединится к команде и разберется с этим".
  • Для социальных стартапов очень мало, или даже совсем нет, нерешимых вопросов, связанных с масштабируемостью.

Источник информации

Упоминавшаяся во вступлении неприлично длинная презентация из 185 слайдов:

На видео, к сожалению, это выступление не записывалось.

Часть информации взята из технического блога Instagram.