Аналитика на GitHub

21 апреля 2014, 08:55

Небольшой экскурс в недавнюю историю создания публичной аналитики трафика GitHub, ставшей доступной для трудящихся масс разработчиков в начале этого года. Автор рассказывает о команде проекта, его философии, а также ряде приемов, использовавшихся при создании этих графиков.

В прошлом году я активно занимался работой с данными и аналитикой на GitHub. Команда, в которой я работал, постепенно выросла от 1 до 4 человек на фултайме. Кроме того, мы наладили тесное техническое взаимодействие с несколькими людьми из других команд.

Это был очень интересный год, в который мне довелось многому научиться. Поэтому напишу большой пост-резюме о том, что мы программировали и зачем.

Про философию Unix, контроль версий, как использовался журнал и, конечно, сами графики

Начало

Все началось с отдельно взятого приложения на Rails для сборки и обработки данных о трафике, а также для подготовки отчетов о них. Конечной целью являлась разработка графиков о трафике репозиториев для массового использования.

Урок, который я усвоил при разработке Gauges, таков: если вам требуются инкрементные отчеты для быстрого считывания, то для такой работы сырые данные – не самая ценная вещь. Учитывая это, я начал пользоваться Cassandra для хранения всех просмотров страниц в необработанном виде. Кроме того, при помощи распределенных счетчиков Cassandra для ускоренного считывания я стал накапливать ряд приращиваемых «индексов».

Обратите внимание: до сих пор в этой статье я говорю о себе в единственном числе. Работать в команде всегда лучше, чем одному. Поэтому через пару месяцев упорного труда я решил, что пришло время найти соратников, которые не меньше меня увлекаются данными и аналитикой.

На одной январской конференции в 2013 году я познакомился с Дереком Гринтри, к которому и обратился в первую очередь. Дерек занимался обеспечением запрашиваемости данных о деятельности (activity data), поэтому мы решили объединить усилия. Учитывая его знания в области периодической отчетности (batch reporting) и hadoop, казалось целесообразным сосредоточиться именно на периодической отчетности с заданным интервалом (ежедневная, ежечасная), а обновлениями в реальном времени заняться позже. Главное правильно начать, а потом оперативно двигаться вперед. Мы отказались от использования счетчиков Cassandra и с тех пор сохраняли только сырые данные.

Применение философии Unix

Примерно на том этапе мы осознали, что, реализуя весь функционал в одном приложении, мы рано или поздно нарвемся на неприятности. В этом случае нас определенно ожидал путь бесповоротных раскаяний, поэтому мы решили, пока не поздно, задействовать нашу излюбленную философию Unix. Разбили единое приложение на несколько компонентов. Они отвечали, соответственно, за сбор данных, их обработку и подготовку отчетов.

Сборщик представлял собой приложение на Rails, решавшее эту и только эту задачу. Программа получала события и выстраивала их в очередь при помощи Kestrelиспользовал эту систему распределенных очередей в Gauges, никаких проблем у меня с ней не возникло, поэтому я решил задействовать Kestrel и в этом проекте).

Обработчик вытягивал информацию из очереди и сохранял сырые данные в Cassandra. Другой обрабатывающий компонент (Hadoop) затем перебирал сырые данные по интервалам и агрегировал их в виде списков («индексов»), которые потом было удобно использовать для быстрого считывания (например: просмотры страниц для конкретного репозитория за заданный час). 

Генератор отчетов также был написан как приложение на Rails, решавшее только эту задачу. Приложение получало запросы API с github.com, а потом считывало данные, необходимые для выполнения запроса, поступившего от Cassandra.

Все эти приложения получились небольшими и узкоспециальными, контакт между ними осуществлялся при помощи очереди или хранилища данных. Связующим звеном между сборщиком и обработчиком выступал механизм Kestrel. Связь между обработчиком и генератором отчетов выполнялась при помощи Cassandra. Каждый компонент предоставлял свою информацию в качестве ввода следующему компоненту, который на ее основе генерировал вывод. Возможно, мы не в полной мере следовали философии Unix, но все варианты, на наш взгляд, были выбраны прагматично.  

Применение схемы и контроля версий

Примерно на этом этапе к нам присоединился Дитрих Фезерстоун. У нас уже была готова небольшая часть схемы (приложение, тип события, метка времени, полезная нагрузка), но он убеждал нас, что необходимо развивать эту схему. Мы разбили полезную нагрузку на величины (двумерный словарь, состоящий из строковых ключей и значений) и параметры (двумерный словарь строковых ключей и целочисленных значений). Это было первое и единственное изменение схемы, которое мы сделали. Думаю, это красноречиво свидетельствует о гибкости всей схемы. Типичное событие записывается в нотации JSON примерно так:

{
  "app": "github",
  "event_type": "page_view",
  "timestamp": ...,
  "data": {
    "dimensions": {
      "repository_id": 1234,
      ...
    },
    "measures": {
      "performance_timing_navigation_start": 23,
      ...
    },
    "version": 2
  }
}

Как видите, мы усвоили урок с изменениями схемы и добавили версию схемы к данным/полезной нагрузке. Некоторые читатели могли бы возразить, что мы не сделали нужных выводов, так как по-прежнему используем JSON через интерфейс trift или protobuf, однако действительно оставлю это на суд читателя.

Применение журнала только для записи

Вскоре мы обнаружили, что неограниченно долгое хранение всех сырых данных в Cassandra станет непозволительно затратным. Когда работаешь над аналитикой, быстро осознаешь, что значительный объем данных в системе быстро становится неактуальным, в зависимости от свежести информации (то есть: пользователя гораздо больше интересуют данные за текущую неделю или текущий месяц, чем за все время). Необходимо учитывать, что данные бывают «горячими», «еще тепленькими» и «холодными».

На данном этапе в команду пришел Энди Линдеманн. Почти сразу же он взялся за работу над нижестоящим потребителем данных из нашей очереди, который назывался Gulch. Для тех, кто не в курсе – Kestrel поддерживает механизм доставки сообщений, отправляемых всем подключенным очередям (fanout queues).

Gulch полностью соответствует философии Unix и является крошечным демоном на языке Go. Gulch получает события из источника (kestrel) и по конвейеру сливает их в файловую систему. Единственная задача Gulch – прикреплять события к файлам настолько быстро, насколько это возможно.

Получение каждого события из kestrel осуществляется при помощи протокола Reliable Read. Кроме считываний по данному протоколу gulch также выполняет синхронизацию fsync с определенным интервалом (в настоящее время – раз в секунду). Механизм похож на те, что применяются во всех распространенных базах данных и призван минимизировать потерю информации при отказе системы. Наряду со считыванием по протоколу Reliable Read и с синхронизацией fsync, мы также задействуем резервный экземпляр gulch, который выполняет все те же задачи, что и основной gulch, но кроме того еще и отправляет двумерные файлы в S3.   

В случае отказа основного gulch, мы можем вручную переключиться на использование резервного. Мы предпочли ручное переключение автоматическому по той причине, что к данному этапу работы отказов у нас еще не случалось, и мы хотели опробовать, как получится вручную справиться с несколькими отказами, а уже потом пытаться что-то автоматизировать.
Файлы находятся в структуре каталогов :app/:event_type/%Y-%m-%d и именуются по шаблону «час + случайный UUID». Файлы записываются в каталог до тех пор, пока он не достигнет определенного размера, либо пока не закончится час. При наступлении одного из этих двух условий файлы архивируются и отправляются в S3 (для этого применяется старый добрый cron или подобная блочная структура).

Как только мы освоились с применением gulch и S3 в качестве постоянного хранилища данных, мы воспользовались Hadoop и осуществили миграцию данных с Cassandra на S3 в том же формате, что и при работе с gulch. На данном этапе мы также прекратили запись сырых данных в Cassandra. 

Gulch работает в штатном режиме уже полгода и записал на диск более 2 Тб событий практически без сбоев. Раз в пару месяцев нам приходится немного доработать либо сам gulch, либо его настройки, так как объем входящих данных растет. Но в остальном gulch и go на практике не преподнесли никаких сюрпризов – в хорошем смысле, конечно.

Графики трафика

К ноябрю мы уже истосковались по разработке новых фич для всего сообщества GitHub. Поэтому решили сосредоточиться на окончательной доработке графиков трафика. Мы начерно реализовали доставку запросов с GitHub.com на аналитический API, причем полученная скорость оказалась гораздо выше, чем мы рассчитывали. За несколько недель мы окончательно доработали систему откликов и объявили, что фича готова к релизу. 

В первую рабочую неделю в январе (после новогодних праздников) графики трафика были запущены, и они заработали как по маслу, никак не снизив производительности GitHub.com. Более того, многие люди ими уже заинтересовались, что совершенно превосходно.

Графики трафика – первая фича, реализованная на нашем конвейере данных. События попадают в сборщик, выстраиваются в очередь и направляются в Kestrel. Gulch обрабатывает события и отправляет их на диск. С диска события отправляются в S3, это делает cron. Hadoop извлекает данные из S3 и переваривает их, затем дампирует вывод отчета в Cassandra и, наконец, подает результат на github.com через API Rails, считывающий информацию из Cassandra.   

Для тех, кто предпочитает наглядную информацию, вот описание потока задач в системе, кодировка ascii:

+-----------------------------------------+
         http in           http out       |
     +-------------+   +-------------+    |
     |  collector  |   |  reporter   |    |  API
     +------+------+   +-------------+    |  (for github.com
            |                 ^           |   and other apps)
            |                 | +---------+
            |                 | +---------+
            v                 |           |
     +-------------+   +------+------+    |
     |   kestrel   |   |  cassandra  |    |
     +------+------+   +-------------+    |
            |                 ^           |
            |                 |           |
            |                 |           |
            v                 |           |
     +-------------+   +------+------+    |  Internal
     |    gulch    |   | hadoop/hive |    |  (parts are swappable
     +------+------+   +-------------+    |   by analytics team)
            |                 ^           |
            |                 |           |
            v                 |           |
     +-------------+          |           |
     |   AWS S3    +----------+           |
     +-------------+                      |
                                          |
+-----------------------------------------+
API for github.com… API для github.com и других приложений
Internal… Внутренняя часть. Команда аналитиков может заменять отдельные элементы
Collector Сборщик
Reporter Механизм отчетов

 

 
 
  

Возможно, вам уже кажется, что мы смастерили молоток, хотя на деле хватило бы и отвертки. Читайте далее – я подробно объясню, что именно мы разработали, и почему, на мой взгляд, графики трафика лишь подтверждают работоспособность фич, реализованных на их основе.

Возвращаемся к журналу

Программа, задуманная как решение для построения графиков трафика, сама собой эволюционировала в конвейер для сборки и обработки данных. Такие метаморфозы произошли не из-за того, что «процесс важнее результата», а потому, что мы осознали, насколько рационально было бы переправлять просмотры страниц в S3, а потом произвольно запрашивать данные при помощи Hive/Hadoop. Убедившись в удобстве этого механизма, мы решили реализовать его для обработки всех событийных данных.  

В конце января мы устроили маленький аналитический саммит – вся наша команда собралась в головном офисе GitHub в Сан-Франциско. Некоторые из нас до этого были знакомы лишь виртуально, так что встреча удалась.

На обратном пути в самолете я прочитал пост Джея Крепса "The Log" и понял, что он описывает именно такую систему, над которой мы работаем (как минимум, именно такой мы ее видели и надеялись реализовать). Хотя, наша система уступала по мощности той, что работает в основе LinkedIn, Джей сформулировал в своей статье многие ценные мысли, которые роились у меня в голове весь год.

Цель: обеспечить доступность данных

Я считаю, что с виду прозаическая проблема «обеспечения доступности данных» – один из важнейших фронтов работ, которому должна уделять внимание каждая организация.

Это действительно очень важно. Выстраивая систему, целью которой является получение конечного продукта (графиков трафика), но делая небольшое отступление, чтобы обеспечить удобство переиспользования нашего решения, мы значительно повысили доступность данных на GitHub.

Этап 1: Упрощение снятия данных

По моему опыту, многие организации оставляют огромные пробелы в основании этой пирамиды – не полностью выстраивают поток данных – зато спешат перейти сразу к продвинутым этапам моделирования данных. Все надо делать с точностью до наоборот.

Это так. Прежде, чем вам удастся готовить информативную отчетность и делать надежные прогнозы, нужно как следует наладить сбор информации. Мы первым делом настраиваем наш сборщик и очередь, через которые организуем прокачку данных. 

Как только решены эти основные задачи, связанные с унификацией снятия данных, мы приступаем к необходимой работе над инфраструктурой, которая требуется для разнообразной обработки полученных данных – речь о MapReduce, системах запросов в реальном времени и т.д.

Можно сказать, что у нас на GitHub уже есть версия такой системы. Мы организовали простой способ получения данных (HTTP-запросы, каждый с единым пакетом событий). Но мы этим, конечно же, не удовлетворимся, так как хотим еще и упростить разнообразную обработку этой информации.

Этап 2: Улучшаем визуализацию, отчетность и прогнозирование

Наконец, можно сосредоточиться на более филигранных аспектах обработки: речь о качественной визуализации, организации отчетности, алгоритмической обработке и прогнозировании.

Все это происходит в GitHub у меня на глазах. Со времени нашего январского слета я работаю над повышением наглядности всех этих данных в самой системе GitHub. Нам удается обеспечивать вас (пользователей) более качественной информацией и прогонять больше событий через конвейер. Таким образом, реализованные нами графики трафика оказались очень успешными.

Заключение

В любом бизнесе исключительно важен процесс сбора данных, измерения производительности, внесения изменений и проверки того, насколько успешными они оказались.

Начните со сборки. Надежная сборка данных – важнейшая предпосылка для обеспечения правильных измерений. Сырые данные ценны, причем собирать их не так сложно, а хранение таких данных (как правило) не является чрезмерно дорогим удовольствием. Как только накоплен солидный корпус информации, можно приступать к организации отчетности и визуализации полученных данных.

Кроме того, должен еще раз подчеркнуть, что вся вышеописанная работа была проделана командой, а не лично мною (правда, я это уже подчеркивал). Можете считать автора графоманом, но не забывайте, что он вполне неплохо для графомана разбирается в программировании, базах данных и построении систем.

Если у вас есть к автору конкретные вопросы и комментарии по этой статье, можете связываться с ним через твиттер:
~ @jnunemaker.

Джон Ньюнмейкер

Источник

Фото: devianart.com

подписка на главные новости 
недели != спам
# ит-новости
# анонсы событий
# вакансии
Обсуждение