ADG Оставить заявку
Блог Данные и аналитика 5 мин чтения

Hadoop 1.0.0 на старом железе: MapReduce на логах ритейл-сайта

Разворачиваем тестовый Hadoop-кластер из трёх нод на старых серверах и считаем агрегаты по логам веб-сайта: MapReduce вместо ночных SQL-запросов.

Контекст момента

Apache Hadoop 1.0.0 вышел в декабре 2011, получив статус стабильного релиза и первый формальный номер версии для enterprise-развёртываний

Клиент из ритейла позвонил в начале ноября с вопросом, который звучал примерно так: «Мы слышали про Big Data, вы умеете?». Откуда услышал - не уточнил, но скорее всего из какого-то отраслевого мероприятия, где это словосочетание звучало из каждого второго доклада. Конкретная боль была понятна без всяких Big Data: логи веб-сайта накопились за два года, лежат в файлах по 5-20 Гб каждый, и когда маркетинг хочет понять конверсию по источникам трафика за год - аналитик запускает скрипт, который молотит несколько дней, а потом всё равно падает по памяти.

Hadoop 1.0.0 как раз вышел - первый релиз с нормальным номером версии вместо дат и снапшотов. Решили попробовать.

Железо и кластер

Специально покупать ничего не стали. В серверной у клиента нашлись три машины на Intel Xeon, которые раньше были тестовыми серверами: по 16 Гб RAM, SATA-диски на 600-800 Гб, гигабитная сеть. Всё это простаивало. Поставили CentOS 6, Java 6, Hadoop 1.0.0.

Топология минимальная - один NameNode + JobTracker, два DataNode + TaskTracker. NameNode хранит метаданные HDFS и координирует задачи; DataNode - рабочие лошади, где живут данные и выполняются Map/Reduce-таски. Примерно так:

[NameNode / JobTracker]
        |
  [DataNode 1]  [DataNode 2]

Конфигурация hdfs-site.xml и mapred-site.xml - минималистичная. Репликация данных на два (default три - но нод-то только две рабочих). Размер блока HDFS оставили 64 Мб - стандарт для Hadoop 1.x.

Самое неочевидное при первом знакомстве: Hadoop категорически не любит, когда NameNode и DataNode запущены от разных пользователей, когда SSH-ключи между нодами не настроены, и когда часы на серверах расходятся. На последнем потеряли час - DataNode отваливался от кластера с невнятной ошибкой, пока не синхронизировали NTP.

Данные: два года логов nginx

Логи лежали в разных местах: часть на сервере nginx, часть в архивах на NAS, часть вообще на локальных дисках у системного администратора клиента. Собрали в одно место, распаковали, загрузили в HDFS:

hadoop fs -mkdir /logs/nginx
hadoop fs -put /tmp/nginx-logs/*.log /logs/nginx/

Формат лога - стандартный combined: IP, дата, метод, URL, статус, размер, referer, user-agent. Строки вида:

1.2.3.4 - - [01/Jan/2011:00:00:01 +0300] "GET /catalog/shoes HTTP/1.1" 200 12345 "http://yandex.ru/..." "Mozilla/5.0..."

Задача: посчитать уникальные IP по дням, количество запросов по разделам каталога, топ источников трафика (по referer).

Первый MapReduce: подсчёт запросов по разделам

MapReduce - это не просто «параллельный SQL». Нужно думать в терминах Map (каждая строка -> ключ-значение) и Reduce (агрегируем по ключу). Для подсчёта запросов по разделам каталога:

  • Map читает строку лога, парсит URL, выдаёт пару (раздел_каталога, 1).
  • Reduce суммирует единицы по каждому разделу.

Писали на Java - другого варианта в Hadoop 1.0 нет (Hive и Pig существуют, но мы их не трогали в этот раз). Код немаленький: классы Mapper, Reducer, драйвер, сборка в JAR, запуск через hadoop jar. Для людей, привыкших к SQL, это культурный шок - чтобы сделать GROUP BY COUNT(*), нужно написать страниц пять кода.

Запустили первую задачу. Hadoop поднял прогресс-бар в веб-интерфейсе (порт 50070 для HDFS, 50030 для JobTracker). Через 40 минут - результат в HDFS в виде нескольких файлов part-r-00000, part-r-00001. Забрали, посмотрели - данные сходятся с тем, что аналитик считал вручную по одному из месяцев.

Аналитик клиента, который несколько часов смотрел на прогресс-бар, сказал: «Это долго». Объяснили разницу: его скрипт на Python молотил данные последовательно на одной машине и падал. Hadoop раскидал задачу по нодам, обработал все два года данных и выдал результат. Если добавить ещё нод - будет быстрее.

Что неожиданно сработало и что нет

Сработало хорошо - HDFS как хранилище. Загрузить 200+ Гб логов, убедиться, что данные реплицированы на обе DataNode, и спокойно работать с ними - это работает надёжно. Веб-интерфейс NameNode показывает состояние кластера, репликацию, живые ноды.

Не сработало - рассчитывали, что Java-код будет несложно писать. Это не так. Для каждой новой агрегации нужен отдельный Mapper, отдельный Reducer, новый JAR. Итерации медленные. Если бы задача была разовая - проще было бы написать хороший Python-скрипт с партиционированием.

Неожиданный нюанс - маленькие файлы убивают производительность. У клиента среди логов оказались файлы по 1-5 Мб (ротация каждые полчаса на тихих сайтах). Для HDFS каждый файл - отдельный блок в памяти NameNode. Пришлось предварительно склеивать мелкие файлы командой hadoop fs -cat с перенаправлением в один большой файл.

Что дальше

Кластер из трёх нод сейчас в тестовом режиме. До production далеко: нет нормального мониторинга, нет процедуры загрузки новых логов, нет HA для NameNode (в Hadoop 1.x NameNode - single point of failure). Клиент хочет считать агрегаты раз в неделю - для этого текущей установки достаточно.

На вопрос «когда переходим на Big Data в production?» честный ответ пока такой: посмотрим на практике, сколько реально задач требуют такого подхода. SQL Server справляется с большинством аналитики, когда данные хорошо структурированы. Hadoop - это инструмент для случаев, где данные неструктурированные, объём такой, что не влезает в реляционную БД, или задача натурально параллельная.

Если интересно, что мы делаем с хранилищами данных и аналитикой - там можно посмотреть подробнее. Hadoop - это только один из инструментов, и не всегда нужный.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.