Эта статья послужит практическим руководством по сборке, начальной настройке и тестированию работоспособности Hadoop начинающим администраторам. Мы разберем, как собрать Hadoop из исходников, сконфигурировать, запустить и проверить, что все работает, как надо. В статье вы не найдете теоретической части. Если вы раньше не сталкивались с Hadoop, не знаете из каких частей он состоит и как они взаимодействуют, вот пара полезных ссылок на официальную документацию:
hadoop.apache.org/docs/r2.7.3/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html
hadoop.apache.org/docs/r2.7.3/hadoop-yarn/hadoop-yarn-site/YARN.html
Почему просто не использовать готовый дистрибутив?
— Обучение. Похожие статьи часто начинаются с рекомендаций скачать образ виртуальной машины с дистрибутивом Cloudera или HortonWorks. Как правило, дистрибутив – сложная экосистема с уймой компонент. Новичку будет непросто разобраться, где что, и как это все взаимодействует. Начиная from scratch мы немного уменьшаем порог вхождения, так как имеем возможность рассматривать компоненты по одной.
— Функциональные тесты и бенчмарки. Есть небольшой лаг между выходом новой версии продукта, и моментом, когда она появляется в дистрибутиве. Если вам необходимо протестировать новые функции только что появившейся версии, Вы не сможете использовать готовый дистрибутив. Также будет сложно сравнить производительность двух версий одного и того же софта, так как в готовых дистрибутивах как правило отсутствует возможность обновить версию какого-либо одного компонента, оставив все остальное как есть.
— Just for fun.
Почему собираем из исходников? Ведь бинарные сборки Hadoop также доступны.
Часть кода Hadoop написана на C/C++. Не знаю, на какой системе делает сборки команда разработчиков, но С-библиотеки, поставляемые вместе с бинарными сборками Hadoop, зависят от версии libc, которой нет ни в RHEL, ни в Debian/Ubuntu. Неработоспособность С-библиотек Hadoop в общем случае не критично, но некоторые фичи без них работать не будут.
Зачем заново описывать все то, что и так есть в официальной документации?
Статья призвана сэкономить время. Официальная документация не содержит quickstart-инструкций – делай так и оно заработает. Если вам по той или иной причине необходимо собрать “ванильный” Hadoop, но нет времени на то, чтоб делать это методом проб и ошибок, вы зашли по адресу.
Для сборки будем использовать CentOS 7. Если верить Сloudera, большинство кластеров работают именно на RHEL и производных (CentOS, Oracle Linux). 7-я версия подходит больше всего, так как в ее репозиториях уже есть библиотека protobuf нужной версии. Если Вы хотите использовать CentOS 6, будет необходимо собрать protobuf самостоятельно.
Сборку и прочие эксперименты будем проводить с привилегиями root (чтоб не усложнять статью).
Где-то 95% кода Hadoop написано на Java. Для сборки нам понадобятся Oracle JDK и Maven.
Загружаем последнюю версию JDK с сайта Oracle и разархивируем в /opt. Так же добавим переменную JAVA_HOME (используется Hadoop) и добавим /opt/java/bin в PATH для пользователя root (для удобства):
Устанавливаем Мaven. Он будет необходим только на этапе сборки. По-этому установим его в наш home (после окончания сборки все файлы, которые останутся в home, можно будет удалить).
Где-то 4-5% кода Hadoop написано на C/C++. Установим компилятор и прочие пакеты необходимые для сборки:
Также нам понадобятся некоторые сторонние библиотеки:
Система готова. Скачиваем, собираем и устанавливаем Hadoop в /opt:
Hadoop насчитывает около тысячи параметров. К счастью, чтобы запустить Hadoop и сделать некоторые первые шаги в освоении достаточно около 40, оставив остальное по умолчанию.
Приступим. Если помните, мы установили Hadoop в /opt/hadoop. Все конфигурационные файлы находятся в /opt/hadoop/etc/hadoop. В общей сложности потребуется отредактировать 6 конфигурационных файлов. Все конфиги ниже привожу в виде команд. Для того, чтобы те, кто пытается собрать свой Hadoop по этой статье, могли просто копипастить команды в консоль.
Во-первых, установим переменную окружения JAVA_HOME в файлах hadoop-env.sh и yarn-env.sh. Так мы дадим всем компонентам знать, где установлена java, которую они должны использовать.
Сконфигурируем URL для HDFS в файле core-site.xml. Он состоит из префикса hdfs://, имени хоста, на котором запущен NameNode и порта. Если этого не сделать, Hadoop не будет использовать распределенную файловую систему, а будет работать с локальной ФС на вашем компьютере (URL по умолчанию: file:///).
В файле hdfs-site.xml конфигурируем 4 параметра. Количество реплик устанавливаем в 1, так как наш “кластер” состоит всего из одной ноды. Также конфигурируем директории, где будут хранить данные NameNode, DataNode и SecondaryNameNode.
Мы закончили настройку HDFS. Можно было бы запустить NameNode и DataNode, и поработать с ФС. Но оставим это для следующего раздела. Перейдем к конфигурации YARN.
Параметров довольно много. Пройдемся по ним по порядку.
Параметр yarn.resourcemanager.hostname указывает, на каком хосте запущен сервис ResourceManager.
Параметры yarn.nodemanager.resource.memory-mb и yarn.nodemanager.resource.cpu-vcores являются, пожалуй, самыми важными. В них мы сообщаем кластеру, сколько памяти и ядер CPU может использовать каждая нода в общей сложности для запуска контейнеров.
Параметры yarn.scheduler.maximum-allocation-mb и yarn.scheduler.maximum-allocation-vcores указывают, сколько памяти и ядер максимально может выделяться под отдельный контейнер. Нетрудно видеть, что с данной конфигурацией в нашем “кластере”, состоящим из одной ноды, могут одновременно быть запущены 4 контейнера (с 1GB памяти каждый).
Параметр yarn.nodemanager.vmem-check-enabled установленный в false, отключает проверку количества используемой виртуальной памяти. Как видно из предыдущего абзаца, каждому контейнеру доступно не так много памяти, и при такой конфигурации любое приложение наверняка привысит лимит доступной виртуальной памяти.
Параметр yarn.nodemanager.local-dirs указывает, где будут храниться временные данные контейнеров (jar с байткодом приложения, файлы конфигурации, временные данные, сгенерированные во время исполнения, …)
Параметр yarn.nodemanager.log-dirs указывает, где локально будут храниться логи каждого таска.
Параметр yarn.log-aggregation-enable указывает хранить логи в HDFS. После окончания исполнения приложения, его логи из yarn.nodemanager.log-dirs каждой ноды будут перемещены в HDFS (по умолчанию – в директорию /tmp/logs).
Параметры yarn.nodemanager.aux-services и yarn.nodemanager.aux-services.mapreduce_shuffle.class указывают сторонний shuffle-сервис для фреймворка MapReduce.
Вот пожалуй и все для YARN. Приведу также конфигурацию для MapReduce (один из возможных фреймворков распределенных вычислений). Хоть он в последнее время и потерял популярность в связи с появлением Spark, однако еще много где используется.
Параметр mapreduce.framework.name указывает, что мы будем запускать задачи MapReduce в YARN (значение по умолчанию local используется только для отладки – все задачи запускаются в одной и той же jvm на одной и той же машине).
Параметры mapreduce.jobhistory.address и mapreduce.jobhistory.webapp.address указывают имя ноды, на которой будет запущен сервис JobHistory.
Параметр mapreduce.job.reduce.slowstart.completedmaps указывает запускать фазу reduce не ранее, чем выполнится 80% фазы map.
Остальные параметры задают максимально фозможные значения памяти и ядер CPU и jvm heap для мапперов, редьюсеров и application master-ов. Как видите, они не должны превышать соответствующим значениям для контейнеров YARN, которые мы определили в yarn-site.xml. Значения jvm heap как правило устанавливают в 75% от параметров *.memory.mb.
Создадим директорию /data, в которой будут храниться данные HDFS, а так же временные файлы контейнеров YARN.
Отформатируем HDFS
И, наконец, запустим все сервисы нашего «кластера»:
Если все прошло удачно (можно проверить сообщения об ошибках в логах в /opt/hadoop/logs), Hadoop развернут и готов к работе…
Посмотрим на структуру директорий hadoop:
Сам Hadoop (исполняемый java-байткод) находится в директории share и разбит на компоненты (hdfs, yarn, mapreduce, etc...). В директории lib находятся библиотеки, написанные на C.
Назначение остальных директорий интуитивно понятно: bin – утилиты командной строки для работы с Hadoop, sbin – скрипты запуска, etc – конфиги, logs – логи. Нас в первую очередь будут интересовать две утилиты из директории bin: hdfs и yarn.
Если вы помните, мы уже отформатировали HDFS и запустили все необходимые процессы. Посмотрим, что у нас в HDFS:
Хотя мы явно не создавали эту структуру директорий, ее создал сервис JobHistory (последний запущеный демон: mr-jobhistory-daemon.sh start historyserver).
Посмотрим, что в директории /data:
Как видите, в /data/dfs/nn NameNode создал файл fsimage и первый edit-файл. В /data/dfs/dn DataNode создал директорию для хранения блоков данных, но самих данных еще нет.
Скопируем какой-нибудь файл с локальной ФС в HDFS:
Снова посмотрим на содержимое /data
Ура!!! Появился первый блок и его контрольная сумма.
Запустим какое-нибудь приложение, чтобы убедится, что YARN работает как надо. Например, pi из пакета hadoop-mapreduce-examples.jar:
Если посмотреть на содержимое /data/yarn во время исполнения приложения, можно узнать много интересного о том, как исполняются приложения YARN:
В частности видим, что логи пишутся в /data/yarn/log (параметр yarn.nodemanager.log-dirs из yarn-site.xml).
По окончании работы приложения /data/yarn приходит в свой первозданный вид:
Если мы снова взглянем на содержимое HDFS, увидим, что log aggregation работает (логи только что выполненного приложения были перемещены из локальной ФС /data/yarn/log в HDFS /tmp/logs).
Также увидим, что сервис JobHistory сохранил информацию о нашем приложении в /tmp/hadoop-yarn/staging/history/done.
Возможно вы обратили внимание, что до сих пор я брал “кластер” в кавычки. Ведь у нас все работает на одной и той же машине. Исправим это досадное недоразумение. Протестируем наш Hadoop в настоящем распределенном кластере.
Прежде всего, подправим конфигурацию Hadoop. На данный момент имя хоста в конфигурации Hadoop указано как localhost. Если сейчас просто скопировать эту конфигурацию на другие ноды, каждая нода будет пытаться найти NameNode, ResourceManager и JobHistory сервисы на своем хосте. Поэтому определимся заранее с именем хоста с этими сервисами и внесем правки в конфиги.
В моем случае, все выше master-сервисы (NameNode, ResourceManager, JobHistory) будут выполняться на хосте master.local. Заменим localhost на master.local в конфигурации:
Теперь я просто клонирую виртуальную машину, на которой я проводил сборку два раза, чтоб получить две slave-ноды. На slave-нодах нужно установить уникальное имя хоста (в моем случае это slave1.local и slave2.local). Также на всех трех нодах нашего кластера сконфигурируем /etc/hosts, чтоб каждая машина кластера могла обращаться к другим по имени хоста. В моем случае это выглядит так (одно и то же содержимое на всех трех машинах):
Дополнительно на нодах slave1.local и slave2.local нужно очистить содержимое /data/dfs/dn
Все готово. На master.local запускаем все сервисы:
На slave1.local и slave2.local запускаем только DataNode и NodeManager:
Проверим, что наш кластер теперь состоит из трех нод.
Для HDFS посмотрим на вывод команды dfsadmin -report и убедимся, что все три машины включены в список Live datanodes:
Или зайдем на веб страничку NameNode:
master.local:50070/dfshealth.html#tab-datanode
Для YARN посмотрим на вывод команды node -list:
Или зайдем на веб страничку ResourceManager
master.local:8088/cluster/nodes
Все ноды должны быть в списке со статусом RUNNING.
Напоследок убедимся, что запускаемые приложения MapReduce используют ресурсы на всех трех нодах. Запустим уже знакомое нам приложение Pi из hadoop-mapreduce-examples.jar:
Во время выполнения приложения еще раз посмотрим вывод yarn node -list -all:
Number-of-Running-Containers — 4 на каждой ноде.
Также мы можем зайти на master.local:8088/cluster/nodes и посмотреть, сколько ядер и памяти используется всеми приложениями в общей сложности на каждой ноде.
Мы собрали Hadoop из исходного кода, установили, сконфигурировали и протестировали работоспособность на отдельной машине и в распределенном кластере. Если тема вам интересна, если есть желание подобным образом собрать другие сервисы из экосистемы Hadoop, оставлю ссылку на скрипт, который поддерживаю для собственных нужд:
github.com/hadoopfromscratch/hadoopfromscratch
С его помошью вы сможете установить zookeeper, spark, hive, hbase, cassandra, flume. Если найдете ошибки или неточности, пожалуйста напишите. Буду очень признателен.
hadoop.apache.org/docs/r2.7.3/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html
hadoop.apache.org/docs/r2.7.3/hadoop-yarn/hadoop-yarn-site/YARN.html
Почему просто не использовать готовый дистрибутив?
— Обучение. Похожие статьи часто начинаются с рекомендаций скачать образ виртуальной машины с дистрибутивом Cloudera или HortonWorks. Как правило, дистрибутив – сложная экосистема с уймой компонент. Новичку будет непросто разобраться, где что, и как это все взаимодействует. Начиная from scratch мы немного уменьшаем порог вхождения, так как имеем возможность рассматривать компоненты по одной.
— Функциональные тесты и бенчмарки. Есть небольшой лаг между выходом новой версии продукта, и моментом, когда она появляется в дистрибутиве. Если вам необходимо протестировать новые функции только что появившейся версии, Вы не сможете использовать готовый дистрибутив. Также будет сложно сравнить производительность двух версий одного и того же софта, так как в готовых дистрибутивах как правило отсутствует возможность обновить версию какого-либо одного компонента, оставив все остальное как есть.
— Just for fun.
Почему собираем из исходников? Ведь бинарные сборки Hadoop также доступны.
Часть кода Hadoop написана на C/C++. Не знаю, на какой системе делает сборки команда разработчиков, но С-библиотеки, поставляемые вместе с бинарными сборками Hadoop, зависят от версии libc, которой нет ни в RHEL, ни в Debian/Ubuntu. Неработоспособность С-библиотек Hadoop в общем случае не критично, но некоторые фичи без них работать не будут.
Зачем заново описывать все то, что и так есть в официальной документации?
Статья призвана сэкономить время. Официальная документация не содержит quickstart-инструкций – делай так и оно заработает. Если вам по той или иной причине необходимо собрать “ванильный” Hadoop, но нет времени на то, чтоб делать это методом проб и ошибок, вы зашли по адресу.
Сборка
Для сборки будем использовать CentOS 7. Если верить Сloudera, большинство кластеров работают именно на RHEL и производных (CentOS, Oracle Linux). 7-я версия подходит больше всего, так как в ее репозиториях уже есть библиотека protobuf нужной версии. Если Вы хотите использовать CentOS 6, будет необходимо собрать protobuf самостоятельно.
Сборку и прочие эксперименты будем проводить с привилегиями root (чтоб не усложнять статью).
Где-то 95% кода Hadoop написано на Java. Для сборки нам понадобятся Oracle JDK и Maven.
Загружаем последнюю версию JDK с сайта Oracle и разархивируем в /opt. Так же добавим переменную JAVA_HOME (используется Hadoop) и добавим /opt/java/bin в PATH для пользователя root (для удобства):
cd ~
wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u112-b15/jdk-8u112-linux-x64.tar.gz
tar xvf ~/jdk-8u112-linux-x64.tar.gz
mv ~/jdk1.8.0_112 /opt/java
echo "PATH=\"/opt/java/bin:\$PATH\"" >> ~/.bashrc
echo "export JAVA_HOME=\"/opt/java\"" >> ~/.bashrc
Устанавливаем Мaven. Он будет необходим только на этапе сборки. По-этому установим его в наш home (после окончания сборки все файлы, которые останутся в home, можно будет удалить).
cd ~
wget http://apache.rediris.es/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz
tar xvf ~/apache-maven-3.3.9-bin.tar.gz
mv ~/apache-maven-3.3.9 ~/maven
echo "PATH=\"/root/maven/bin:\$PATH\"" >> ~/.bashrc
source ~/.bashrc
Где-то 4-5% кода Hadoop написано на C/C++. Установим компилятор и прочие пакеты необходимые для сборки:
yum -y install gcc gcc-c++ autoconf automake libtool cmake
Также нам понадобятся некоторые сторонние библиотеки:
yum -y install zlib-devel openssl openssl-devel snappy snappy-devel bzip2 bzip2-devel protobuf protobuf-devel
Система готова. Скачиваем, собираем и устанавливаем Hadoop в /opt:
cd ~
wget http://apache.rediris.es/hadoop/common/hadoop-2.7.3/hadoop-2.7.3-src.tar.gz
tar -xvf ~/hadoop-2.7.3-src.tar.gz
mv ~/hadoop-2.7.3-src ~/hadoop-src
cd ~/hadoop-src
mvn package -Pdist,native -DskipTests -Dtar
tar -C/opt -xvf ~/hadoop-src/hadoop-dist/target/hadoop-2.7.3.tar.gz
mv /opt/hadoop-* /opt/hadoop
echo "PATH=\"/opt/hadoop/bin:\$PATH\"" >> ~/.bashrc
source ~/.bashrc
Первичная конфигурация
Hadoop насчитывает около тысячи параметров. К счастью, чтобы запустить Hadoop и сделать некоторые первые шаги в освоении достаточно около 40, оставив остальное по умолчанию.
Приступим. Если помните, мы установили Hadoop в /opt/hadoop. Все конфигурационные файлы находятся в /opt/hadoop/etc/hadoop. В общей сложности потребуется отредактировать 6 конфигурационных файлов. Все конфиги ниже привожу в виде команд. Для того, чтобы те, кто пытается собрать свой Hadoop по этой статье, могли просто копипастить команды в консоль.
Во-первых, установим переменную окружения JAVA_HOME в файлах hadoop-env.sh и yarn-env.sh. Так мы дадим всем компонентам знать, где установлена java, которую они должны использовать.
sed -i '1iJAVA_HOME=/opt/java' /opt/hadoop/etc/hadoop/hadoop-env.sh
sed -i '1iJAVA_HOME=/opt/java' /opt/hadoop/etc/hadoop/yarn-env.sh
Сконфигурируем URL для HDFS в файле core-site.xml. Он состоит из префикса hdfs://, имени хоста, на котором запущен NameNode и порта. Если этого не сделать, Hadoop не будет использовать распределенную файловую систему, а будет работать с локальной ФС на вашем компьютере (URL по умолчанию: file:///).
cat << EOF > /opt/hadoop/etc/hadoop/core-site.xml
<configuration>
<property><name>fs.defaultFS</name><value>hdfs://localhost:8020</value></property>
</configuration>
EOF
В файле hdfs-site.xml конфигурируем 4 параметра. Количество реплик устанавливаем в 1, так как наш “кластер” состоит всего из одной ноды. Также конфигурируем директории, где будут хранить данные NameNode, DataNode и SecondaryNameNode.
cat << EOF > /opt/hadoop/etc/hadoop/hdfs-site.xml
<configuration>
<property><name>dfs.replication</name><value>1</value></property>
<property><name>dfs.namenode.name.dir</name><value>/data/dfs/nn</value></property>
<property><name>dfs.datanode.data.dir</name><value>/data/dfs/dn</value></property>
<property><name>dfs.namenode.checkpoint.dir</name><value>/data/dfs/snn</value></property>
</configuration>
EOF
Мы закончили настройку HDFS. Можно было бы запустить NameNode и DataNode, и поработать с ФС. Но оставим это для следующего раздела. Перейдем к конфигурации YARN.
cat << EOF > /opt/hadoop/etc/hadoop/yarn-site.xml
<configuration>
<property><name>yarn.resourcemanager.hostname</name><value>localhost</value></property>
<property><name>yarn.nodemanager.resource.memory-mb</name><value>4096</value></property>
<property><name>yarn.nodemanager.resource.cpu-vcores</name><value>4</value></property>
<property><name>yarn.scheduler.maximum-allocation-mb</name><value>1024</value></property>
<property><name>yarn.scheduler.maximum-allocation-vcores</name><value>1</value></property>
<property><name>yarn.nodemanager.vmem-check-enabled</name><value>false</value></property>
<property><name>yarn.nodemanager.local-dirs</name><value>/data/yarn</value></property>
<property><name>yarn.nodemanager.log-dirs</name><value>/data/yarn/log</value></property>
<property><name>yarn.log-aggregation-enable</name><value>true</value></property>
<property><name>yarn.nodemanager.aux-services</name><value>mapreduce_shuffle</value></property>
<property><name>yarn.nodemanager.aux-services.mapreduce_shuffle.class</name><value>org.apache.hadoop.mapred.ShuffleHandler</value></property>
</configuration>
EOF
Параметров довольно много. Пройдемся по ним по порядку.
Параметр yarn.resourcemanager.hostname указывает, на каком хосте запущен сервис ResourceManager.
Параметры yarn.nodemanager.resource.memory-mb и yarn.nodemanager.resource.cpu-vcores являются, пожалуй, самыми важными. В них мы сообщаем кластеру, сколько памяти и ядер CPU может использовать каждая нода в общей сложности для запуска контейнеров.
Параметры yarn.scheduler.maximum-allocation-mb и yarn.scheduler.maximum-allocation-vcores указывают, сколько памяти и ядер максимально может выделяться под отдельный контейнер. Нетрудно видеть, что с данной конфигурацией в нашем “кластере”, состоящим из одной ноды, могут одновременно быть запущены 4 контейнера (с 1GB памяти каждый).
Параметр yarn.nodemanager.vmem-check-enabled установленный в false, отключает проверку количества используемой виртуальной памяти. Как видно из предыдущего абзаца, каждому контейнеру доступно не так много памяти, и при такой конфигурации любое приложение наверняка привысит лимит доступной виртуальной памяти.
Параметр yarn.nodemanager.local-dirs указывает, где будут храниться временные данные контейнеров (jar с байткодом приложения, файлы конфигурации, временные данные, сгенерированные во время исполнения, …)
Параметр yarn.nodemanager.log-dirs указывает, где локально будут храниться логи каждого таска.
Параметр yarn.log-aggregation-enable указывает хранить логи в HDFS. После окончания исполнения приложения, его логи из yarn.nodemanager.log-dirs каждой ноды будут перемещены в HDFS (по умолчанию – в директорию /tmp/logs).
Параметры yarn.nodemanager.aux-services и yarn.nodemanager.aux-services.mapreduce_shuffle.class указывают сторонний shuffle-сервис для фреймворка MapReduce.
Вот пожалуй и все для YARN. Приведу также конфигурацию для MapReduce (один из возможных фреймворков распределенных вычислений). Хоть он в последнее время и потерял популярность в связи с появлением Spark, однако еще много где используется.
cat << EOF > /opt/hadoop/etc/hadoop/mapred-site.xml
<configuration>
<property><name>mapreduce.framework.name</name><value>yarn</value></property>
<property><name>mapreduce.jobhistory.address</name><value>localhost:10020</value></property>
<property><name>mapreduce.jobhistory.webapp.address</name><value>localhost:19888</value></property>
<property><name>mapreduce.job.reduce.slowstart.completedmaps</name><value>0.8</value></property>
<property><name>yarn.app.mapreduce.am.resource.cpu-vcores</name><value>1</value></property>
<property><name>yarn.app.mapreduce.am.resource.mb</name><value>1024</value></property>
<property><name>yarn.app.mapreduce.am.command-opts</name><value>-Djava.net.preferIPv4Stack=true -Xmx768m</value></property>
<property><name>mapreduce.map.cpu.vcores</name><value>1</value></property>
<property><name>mapreduce.map.memory.mb</name><value>1024</value></property>
<property><name>mapreduce.map.java.opts</name><value>-Djava.net.preferIPv4Stack=true -Xmx768m</value></property>
<property><name>mapreduce.reduce.cpu.vcores</name><value>1</value></property>
<property><name>mapreduce.reduce.memory.mb</name><value>1024</value></property>
<property><name>mapreduce.reduce.java.opts</name><value>-Djava.net.preferIPv4Stack=true -Xmx768m</value></property>
</configuration>
EOF
Параметр mapreduce.framework.name указывает, что мы будем запускать задачи MapReduce в YARN (значение по умолчанию local используется только для отладки – все задачи запускаются в одной и той же jvm на одной и той же машине).
Параметры mapreduce.jobhistory.address и mapreduce.jobhistory.webapp.address указывают имя ноды, на которой будет запущен сервис JobHistory.
Параметр mapreduce.job.reduce.slowstart.completedmaps указывает запускать фазу reduce не ранее, чем выполнится 80% фазы map.
Остальные параметры задают максимально фозможные значения памяти и ядер CPU и jvm heap для мапперов, редьюсеров и application master-ов. Как видите, они не должны превышать соответствующим значениям для контейнеров YARN, которые мы определили в yarn-site.xml. Значения jvm heap как правило устанавливают в 75% от параметров *.memory.mb.
Пуск
Создадим директорию /data, в которой будут храниться данные HDFS, а так же временные файлы контейнеров YARN.
mkdir /data
Отформатируем HDFS
hadoop namenode -format
И, наконец, запустим все сервисы нашего «кластера»:
/opt/hadoop/sbin/hadoop-daemon.sh start namenode
/opt/hadoop/sbin/hadoop-daemon.sh start datanode
/opt/hadoop/sbin/yarn-daemon.sh start resourcemanager
/opt/hadoop/sbin/yarn-daemon.sh start nodemanager
/opt/hadoop/sbin/mr-jobhistory-daemon.sh start historyserver
Если все прошло удачно (можно проверить сообщения об ошибках в логах в /opt/hadoop/logs), Hadoop развернут и готов к работе…
Проверка работоспособности
Посмотрим на структуру директорий hadoop:
/opt/hadoop/
+-- bin
+-- etc
¦ L-- hadoop
+-- include
+-- lib
¦ L-- native
+-- libexec
+-- logs
+-- sbin
L-- share
+-- doc
¦ L-- hadoop
L-- hadoop
+-- common
+-- hdfs
+-- httpfs
+-- kms
+-- mapreduce
+-- tools
L-- yarn
Сам Hadoop (исполняемый java-байткод) находится в директории share и разбит на компоненты (hdfs, yarn, mapreduce, etc...). В директории lib находятся библиотеки, написанные на C.
Назначение остальных директорий интуитивно понятно: bin – утилиты командной строки для работы с Hadoop, sbin – скрипты запуска, etc – конфиги, logs – логи. Нас в первую очередь будут интересовать две утилиты из директории bin: hdfs и yarn.
Если вы помните, мы уже отформатировали HDFS и запустили все необходимые процессы. Посмотрим, что у нас в HDFS:
hdfs dfs -ls -R /
drwxrwx--- - root supergroup 0 2017-01-05 10:07 /tmp
drwxrwx--- - root supergroup 0 2017-01-05 10:07 /tmp/hadoop-yarn
drwxrwx--- - root supergroup 0 2017-01-05 10:07 /tmp/hadoop-yarn/staging
drwxrwx--- - root supergroup 0 2017-01-05 10:07 /tmp/hadoop-yarn/staging/history
drwxrwx--- - root supergroup 0 2017-01-05 10:07 /tmp/hadoop-yarn/staging/history/done
drwxrwxrwt - root supergroup 0 2017-01-05 10:07 /tmp/hadoop-yarn/staging/history/done_intermediate
Хотя мы явно не создавали эту структуру директорий, ее создал сервис JobHistory (последний запущеный демон: mr-jobhistory-daemon.sh start historyserver).
Посмотрим, что в директории /data:
/data/
+-- dfs
¦ +-- dn
¦ ¦ +-- current
¦ ¦ ¦ +-- BP-1600342399-192.168.122.70-1483626613224
¦ ¦ ¦ ¦ +-- current
¦ ¦ ¦ ¦ ¦ +-- finalized
¦ ¦ ¦ ¦ ¦ +-- rbw
¦ ¦ ¦ ¦ ¦ L-- VERSION
¦ ¦ ¦ ¦ +-- scanner.cursor
¦ ¦ ¦ ¦ L-- tmp
¦ ¦ ¦ L-- VERSION
¦ ¦ L-- in_use.lock
¦ L-- nn
¦ +-- current
¦ ¦ +-- edits_inprogress_0000000000000000001
¦ ¦ +-- fsimage_0000000000000000000
¦ ¦ +-- fsimage_0000000000000000000.md5
¦ ¦ +-- seen_txid
¦ ¦ L-- VERSION
¦ L-- in_use.lock
L-- yarn
+-- filecache
+-- log
+-- nmPrivate
L-- usercache
Как видите, в /data/dfs/nn NameNode создал файл fsimage и первый edit-файл. В /data/dfs/dn DataNode создал директорию для хранения блоков данных, но самих данных еще нет.
Скопируем какой-нибудь файл с локальной ФС в HDFS:
hdfs dfs -put /var/log/messages /tmp/
hdfs dfs -ls /tmp/messages
-rw-r--r-- 1 root supergroup 375974 2017-01-05 09:33 /tmp/messages
Снова посмотрим на содержимое /data
/data/dfs/dn
+-- current
¦ +-- BP-1600342399-192.168.122.70-1483626613224
¦ ¦ +-- current
¦ ¦ ¦ +-- finalized
¦ ¦ ¦ ¦ L-- subdir0
¦ ¦ ¦ ¦ L-- subdir0
¦ ¦ ¦ ¦ +-- blk_1073741825
¦ ¦ ¦ ¦ L-- blk_1073741825_1001.meta
¦ ¦ ¦ +-- rbw
¦ ¦ ¦ L-- VERSION
¦ ¦ +-- scanner.cursor
¦ ¦ L-- tmp
¦ L-- VERSION
L-- in_use.lock
Ура!!! Появился первый блок и его контрольная сумма.
Запустим какое-нибудь приложение, чтобы убедится, что YARN работает как надо. Например, pi из пакета hadoop-mapreduce-examples.jar:
yarn jar /opt/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.3.jar pi 3 100000
…
Job Finished in 37.837 seconds
Estimated value of Pi is 3.14168000000000000000
Если посмотреть на содержимое /data/yarn во время исполнения приложения, можно узнать много интересного о том, как исполняются приложения YARN:
/data/yarn/
+-- filecache
+-- log
¦ L-- application_1483628783579_0001
¦ +-- container_1483628783579_0001_01_000001
¦ ¦ +-- stderr
¦ ¦ +-- stdout
¦ ¦ L-- syslog
¦ +-- container_1483628783579_0001_01_000002
¦ ¦ +-- stderr
¦ ¦ +-- stdout
¦ ¦ L-- syslog
¦ +-- container_1483628783579_0001_01_000003
¦ ¦ +-- stderr
¦ ¦ +-- stdout
¦ ¦ L-- syslog
¦ L-- container_1483628783579_0001_01_000004
¦ +-- stderr
¦ +-- stdout
¦ L-- syslog
+-- nmPrivate
¦ L-- application_1483628783579_0001
¦ +-- container_1483628783579_0001_01_000001
¦ ¦ +-- container_1483628783579_0001_01_000001.pid
¦ ¦ +-- container_1483628783579_0001_01_000001.tokens
¦ ¦ L-- launch_container.sh
¦ +-- container_1483628783579_0001_01_000002
¦ ¦ +-- container_1483628783579_0001_01_000002.pid
¦ ¦ +-- container_1483628783579_0001_01_000002.tokens
¦ ¦ L-- launch_container.sh
¦ +-- container_1483628783579_0001_01_000003
¦ ¦ +-- container_1483628783579_0001_01_000003.pid
¦ ¦ +-- container_1483628783579_0001_01_000003.tokens
¦ ¦ L-- launch_container.sh
¦ L-- container_1483628783579_0001_01_000004
¦ +-- container_1483628783579_0001_01_000004.pid
¦ +-- container_1483628783579_0001_01_000004.tokens
¦ L-- launch_container.sh
L-- usercache
L-- root
+-- appcache
¦ L-- application_1483628783579_0001
¦ +-- container_1483628783579_0001_01_000001
¦ ¦ +-- container_tokens
¦ ¦ +-- default_container_executor_session.sh
¦ ¦ +-- default_container_executor.sh
¦ ¦ +-- job.jar -> /data/yarn/usercache/root/appcache/application_1483628783579_0001/filecache/11/job.jar
¦ ¦ +-- jobSubmitDir
¦ ¦ ¦ +-- job.split -> /data/yarn/usercache/root/appcache/application_1483628783579_0001/filecache/12/job.split
¦ ¦ ¦ L-- job.splitmetainfo -> /data/yarn/usercache/root/appcache/application_1483628783579_0001/filecache/10/job.splitmetainfo
¦ ¦ +-- job.xml -> /data/yarn/usercache/root/appcache/application_1483628783579_0001/filecache/13/job.xml
¦ ¦ +-- launch_container.sh
¦ ¦ L-- tmp
¦ ¦ L-- Jetty_0_0_0_0_37883_mapreduce____.rposvq
¦ ¦ L-- webapp
¦ ¦ L-- webapps
¦ ¦ L-- mapreduce
¦ +-- container_1483628783579_0001_01_000002
¦ ¦ +-- container_tokens
¦ ¦ +-- default_container_executor_session.sh
¦ ¦ +-- default_container_executor.sh
¦ ¦ +-- job.jar -> /data/yarn/usercache/root/appcache/application_1483628783579_0001/filecache/11/job.jar
¦ ¦ +-- job.xml
¦ ¦ +-- launch_container.sh
¦ ¦ L-- tmp
¦ +-- container_1483628783579_0001_01_000003
¦ ¦ +-- container_tokens
¦ ¦ +-- default_container_executor_session.sh
¦ ¦ +-- default_container_executor.sh
¦ ¦ +-- job.jar -> /data/yarn/usercache/root/appcache/application_1483628783579_0001/filecache/11/job.jar
¦ ¦ +-- job.xml
¦ ¦ +-- launch_container.sh
¦ ¦ L-- tmp
¦ +-- container_1483628783579_0001_01_000004
¦ ¦ +-- container_tokens
¦ ¦ +-- default_container_executor_session.sh
¦ ¦ +-- default_container_executor.sh
¦ ¦ +-- job.jar -> /data/yarn/usercache/root/appcache/application_1483628783579_0001/filecache/11/job.jar
¦ ¦ +-- job.xml
¦ ¦ +-- launch_container.sh
¦ ¦ L-- tmp
¦ +-- filecache
¦ ¦ +-- 10
¦ ¦ ¦ L-- job.splitmetainfo
¦ ¦ +-- 11
¦ ¦ ¦ L-- job.jar
¦ ¦ ¦ L-- job.jar
¦ ¦ +-- 12
¦ ¦ ¦ L-- job.split
¦ ¦ L-- 13
¦ ¦ L-- job.xml
¦ L-- work
L-- filecache
42 directories, 50 files
В частности видим, что логи пишутся в /data/yarn/log (параметр yarn.nodemanager.log-dirs из yarn-site.xml).
По окончании работы приложения /data/yarn приходит в свой первозданный вид:
/data/yarn/
+-- filecache
+-- log
+-- nmPrivate
L-- usercache
L-- root
+-- appcache
L-- filecache
Если мы снова взглянем на содержимое HDFS, увидим, что log aggregation работает (логи только что выполненного приложения были перемещены из локальной ФС /data/yarn/log в HDFS /tmp/logs).
Также увидим, что сервис JobHistory сохранил информацию о нашем приложении в /tmp/hadoop-yarn/staging/history/done.
hdfs dfs -ls -R /
drwxrwx--- - root supergroup 0 2017-01-05 10:12 /tmp
drwxrwx--- - root supergroup 0 2017-01-05 10:07 /tmp/hadoop-yarn
drwxrwx--- - root supergroup 0 2017-01-05 10:12 /tmp/hadoop-yarn/staging
drwxrwx--- - root supergroup 0 2017-01-05 10:07 /tmp/hadoop-yarn/staging/history
drwxrwx--- - root supergroup 0 2017-01-05 10:13 /tmp/hadoop-yarn/staging/history/done
drwxrwx--- - root supergroup 0 2017-01-05 10:13 /tmp/hadoop-yarn/staging/history/done/2017
drwxrwx--- - root supergroup 0 2017-01-05 10:13 /tmp/hadoop-yarn/staging/history/done/2017/01
drwxrwx--- - root supergroup 0 2017-01-05 10:13 /tmp/hadoop-yarn/staging/history/done/2017/01/05
drwxrwx--- - root supergroup 0 2017-01-05 10:13 /tmp/hadoop-yarn/staging/history/done/2017/01/05/000000
-rwxrwx--- 1 root supergroup 46338 2017-01-05 10:13 /tmp/hadoop-yarn/staging/history/done/2017/01/05/000000/job_1483628783579_0001-1483629144632-root-QuasiMonteCarlo-1483629179995-3-1-SUCCEEDED-default-1483629156270.jhist
-rwxrwx--- 1 root supergroup 117543 2017-01-05 10:13 /tmp/hadoop-yarn/staging/history/done/2017/01/05/000000/job_1483628783579_0001_conf.xml
drwxrwxrwt - root supergroup 0 2017-01-05 10:12 /tmp/hadoop-yarn/staging/history/done_intermediate
drwxrwx--- - root supergroup 0 2017-01-05 10:13 /tmp/hadoop-yarn/staging/history/done_intermediate/root
drwx------ - root supergroup 0 2017-01-05 10:12 /tmp/hadoop-yarn/staging/root
drwx------ - root supergroup 0 2017-01-05 10:13 /tmp/hadoop-yarn/staging/root/.staging
drwxrwxrwt - root supergroup 0 2017-01-05 10:12 /tmp/logs
drwxrwx--- - root supergroup 0 2017-01-05 10:12 /tmp/logs/root
drwxrwx--- - root supergroup 0 2017-01-05 10:12 /tmp/logs/root/logs
drwxrwx--- - root supergroup 0 2017-01-05 10:13 /tmp/logs/root/logs/application_1483628783579_0001
-rw-r----- 1 root supergroup 65829 2017-01-05 10:13 /tmp/logs/root/logs/application_1483628783579_0001/master.local_37940
drwxr-xr-x - root supergroup 0 2017-01-05 10:12 /user
drwxr-xr-x - root supergroup 0 2017-01-05 10:13 /user/root
Тестирование в распределенном кластере
Возможно вы обратили внимание, что до сих пор я брал “кластер” в кавычки. Ведь у нас все работает на одной и той же машине. Исправим это досадное недоразумение. Протестируем наш Hadoop в настоящем распределенном кластере.
Прежде всего, подправим конфигурацию Hadoop. На данный момент имя хоста в конфигурации Hadoop указано как localhost. Если сейчас просто скопировать эту конфигурацию на другие ноды, каждая нода будет пытаться найти NameNode, ResourceManager и JobHistory сервисы на своем хосте. Поэтому определимся заранее с именем хоста с этими сервисами и внесем правки в конфиги.
В моем случае, все выше master-сервисы (NameNode, ResourceManager, JobHistory) будут выполняться на хосте master.local. Заменим localhost на master.local в конфигурации:
cd /opt/hadoop/etc/hadoop
sed -i 's/localhost/master.local/' core-site.xml hdfs-site.xml yarn-site.xml mapred-site.xml
Теперь я просто клонирую виртуальную машину, на которой я проводил сборку два раза, чтоб получить две slave-ноды. На slave-нодах нужно установить уникальное имя хоста (в моем случае это slave1.local и slave2.local). Также на всех трех нодах нашего кластера сконфигурируем /etc/hosts, чтоб каждая машина кластера могла обращаться к другим по имени хоста. В моем случае это выглядит так (одно и то же содержимое на всех трех машинах):
cat /etc/hosts
…
192.168.122.70 master.local
192.168.122.59 slave1.local
192.168.122.217 slave2.local
Дополнительно на нодах slave1.local и slave2.local нужно очистить содержимое /data/dfs/dn
rm -rf /data/dfs/dn/*
Все готово. На master.local запускаем все сервисы:
/opt/hadoop/sbin/hadoop-daemon.sh start namenode
/opt/hadoop/sbin/hadoop-daemon.sh start datanode
/opt/hadoop/sbin/yarn-daemon.sh start resourcemanager
/opt/hadoop/sbin/yarn-daemon.sh start nodemanager
/opt/hadoop/sbin/mr-jobhistory-daemon.sh start historyserver
На slave1.local и slave2.local запускаем только DataNode и NodeManager:
/opt/hadoop/sbin/hadoop-daemon.sh start datanode
/opt/hadoop/sbin/yarn-daemon.sh start nodemanager
Проверим, что наш кластер теперь состоит из трех нод.
Для HDFS посмотрим на вывод команды dfsadmin -report и убедимся, что все три машины включены в список Live datanodes:
hdfs dfsadmin -report
...
Live datanodes (3):
…
Name: 192.168.122.70:50010 (master.local)
...
Name: 192.168.122.59:50010 (slave1.local)
...
Name: 192.168.122.217:50010 (slave2.local)
Или зайдем на веб страничку NameNode:
master.local:50070/dfshealth.html#tab-datanode
Для YARN посмотрим на вывод команды node -list:
yarn node -list -all
17/01/06 06:17:52 INFO client.RMProxy: Connecting to ResourceManager at master.local/192.168.122.70:8032
Total Nodes:3
Node-Id Node-State Node-Http-Address Number-of-Running-Containers
slave2.local:39694 RUNNING slave2.local:8042 0
slave1.local:36880 RUNNING slave1.local:8042 0
master.local:44373 RUNNING master.local:8042 0
Или зайдем на веб страничку ResourceManager
master.local:8088/cluster/nodes
Все ноды должны быть в списке со статусом RUNNING.
Напоследок убедимся, что запускаемые приложения MapReduce используют ресурсы на всех трех нодах. Запустим уже знакомое нам приложение Pi из hadoop-mapreduce-examples.jar:
yarn jar /opt/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.3.jar pi 30 1000
Во время выполнения приложения еще раз посмотрим вывод yarn node -list -all:
...
Node-Id Node-State Node-Http-Address Number-of-Running-Containers
slave2.local:39694 RUNNING slave2.local:8042 4
slave1.local:36880 RUNNING slave1.local:8042 4
master.local:44373 RUNNING master.local:8042 4
Number-of-Running-Containers — 4 на каждой ноде.
Также мы можем зайти на master.local:8088/cluster/nodes и посмотреть, сколько ядер и памяти используется всеми приложениями в общей сложности на каждой ноде.
Заключение
Мы собрали Hadoop из исходного кода, установили, сконфигурировали и протестировали работоспособность на отдельной машине и в распределенном кластере. Если тема вам интересна, если есть желание подобным образом собрать другие сервисы из экосистемы Hadoop, оставлю ссылку на скрипт, который поддерживаю для собственных нужд:
github.com/hadoopfromscratch/hadoopfromscratch
С его помошью вы сможете установить zookeeper, spark, hive, hbase, cassandra, flume. Если найдете ошибки или неточности, пожалуйста напишите. Буду очень признателен.
Поделиться с друзьями
Crandel
Во всех нормальных инструкциях сборка пакетов происходит в /tmp чтобы не чистить все вручную