Hadoop From Scratch

bb97b3b0113a4ab98d9ee6a9b53f54d9.pngЭта статья послужит практическим руководством по сборке, начальной настройке и тестированию работоспособности 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 (для удобства):

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 
 
  fs.defaultFShdfs://localhost:8020 
 
EOF 

В файле hdfs-site.xml конфигурируем 4 параметра. Количество реплик устанавливаем в 1, так как наш «кластер» состоит всего из одной ноды. Также конфигурируем директории, где будут хранить данные NameNode, DataNode и SecondaryNameNode.

cat << EOF > /opt/hadoop/etc/hadoop/hdfs-site.xml 
 
  dfs.replication1 
  dfs.namenode.name.dir/data/dfs/nn 
  dfs.datanode.data.dir/data/dfs/dn 
  dfs.namenode.checkpoint.dir/data/dfs/snn 
 
EOF

Мы закончили настройку HDFS. Можно было бы запустить NameNode и DataNode, и поработать с ФС. Но оставим это для следующего раздела. Перейдем к конфигурации YARN.

cat << EOF > /opt/hadoop/etc/hadoop/yarn-site.xml 
 
  yarn.resourcemanager.hostnamelocalhost 
  yarn.nodemanager.resource.memory-mb4096 
  yarn.nodemanager.resource.cpu-vcores4 
  yarn.scheduler.maximum-allocation-mb1024 
  yarn.scheduler.maximum-allocation-vcores1 
  yarn.nodemanager.vmem-check-enabledfalse 
  yarn.nodemanager.local-dirs/data/yarn 
  yarn.nodemanager.log-dirs/data/yarn/log 
  yarn.log-aggregation-enabletrue 
  yarn.nodemanager.aux-servicesmapreduce_shuffle 
  yarn.nodemanager.aux-services.mapreduce_shuffle.classorg.apache.hadoop.mapred.ShuffleHandler 

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 
 
  mapreduce.framework.nameyarn 
mapreduce.jobhistory.addresslocalhost:10020 
  mapreduce.jobhistory.webapp.addresslocalhost:19888 
  mapreduce.job.reduce.slowstart.completedmaps0.8 
  yarn.app.mapreduce.am.resource.cpu-vcores1 
  yarn.app.mapreduce.am.resource.mb1024 
  yarn.app.mapreduce.am.command-opts-Djava.net.preferIPv4Stack=true -Xmx768m 
  mapreduce.map.cpu.vcores1 
  mapreduce.map.memory.mb1024 
  mapreduce.map.java.opts-Djava.net.preferIPv4Stack=true -Xmx768m 
  mapreduce.reduce.cpu.vcores1 
  mapreduce.reduce.memory.mb1024 
  mapreduce.reduce.java.opts-Djava.net.preferIPv4Stack=true -Xmx768m 
 
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 
│   └── hadoop 
├── include 
├── lib 
│   └── native 
├── libexec 
├── logs 
├── sbin 
└── share 
    ├── doc 
    │   └── hadoop 
    └── hadoop 
        ├── common 
        ├── hdfs 
        ├── httpfs 
        ├── kms 
        ├── mapreduce 
        ├── tools 
        └── 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 
│   │   │   │   │   └── VERSION 
│   │   │   │   ├── scanner.cursor 
│   │   │   │   └── tmp 
│   │   │   └── VERSION 
│   │   └── in_use.lock 
│   └── nn 
│       ├── current 
│       │   ├── edits_inprogress_0000000000000000001 
│       │   ├── fsimage_0000000000000000000 
│       │   ├── fsimage_0000000000000000000.md5 
│       │   ├── seen_txid 
│       │   └── VERSION 
│       └── in_use.lock 
└── yarn 
    ├── filecache 
    ├── log 
    ├── nmPrivate 
    └── 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 
│   │   │   │   └── subdir0 
│   │   │   │       └── subdir0 
│   │   │   │           ├── blk_1073741825 
│   │   │   │           └── blk_1073741825_1001.meta 
│   │   │   ├── rbw 
│   │   │   └── VERSION 
│   │   ├── scanner.cursor 
│   │   └── tmp 
│   └── VERSION 
└── 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 
│   └── application_1483628783579_0001 
│       ├── container_1483628783579_0001_01_000001 
│       │   ├── stderr 
│       │   ├── stdout 
│       │   └── syslog 
│       ├── container_1483628783579_0001_01_000002 
│       │   ├── stderr 
│       │   ├── stdout 
│       │   └── syslog 
│       ├── container_1483628783579_0001_01_000003 
│       │   ├── stderr 
│       │   ├── stdout 
│       │   └── syslog 
│       └── container_1483628783579_0001_01_000004 
│           ├── stderr 
│           ├── stdout 
│           └── syslog 
├── nmPrivate 
│   └── application_1483628783579_0001 
│       ├── container_1483628783579_0001_01_000001 
│       │   ├── container_1483628783579_0001_01_000001.pid 
│       │   ├── container_1483628783579_0001_01_000001.tokens 
│       │   └── launch_container.sh 
│       ├── container_1483628783579_0001_01_000002 
│       │   ├── container_1483628783579_0001_01_000002.pid 
│       │   ├── container_1483628783579_0001_01_000002.tokens 
│       │   └── launch_container.sh 
│       ├── container_1483628783579_0001_01_000003 
│       │   ├── container_1483628783579_0001_01_000003.pid 
│       │   ├── container_1483628783579_0001_01_000003.tokens 
│       │   └── launch_container.sh 
│       └── container_1483628783579_0001_01_000004 
│           ├── container_1483628783579_0001_01_000004.pid 
│           ├── container_1483628783579_0001_01_000004.tokens 
│           └── launch_container.sh 
└── usercache 
    └── root 
        ├── appcache 
        │   └── 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 
        │       │   │   └── 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 
        │       │   └── tmp 
        │       │       └── Jetty_0_0_0_0_37883_mapreduce____.rposvq 
        │       │           └── webapp 
        │       │               └── webapps 
        │       │                   └── 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 
        │       │   └── 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 
        │       │   └── 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 
        │       │   └── tmp 
        │       ├── filecache 
        │       │   ├── 10 
        │       │   │   └── job.splitmetainfo 
        │       │   ├── 11 
        │       │   │   └── job.jar 
        │       │   │       └── job.jar 
        │       │   ├── 12 
        │       │   │   └── job.split 
        │       │   └── 13 
        │       │       └── job.xml 
        │       └── work 
        └── filecache 

42 directories, 50 files

В частности видим, что логи пишутся в /data/yarn/log (параметр yarn.nodemanager.log-dirs из yarn-site.xml).

По окончании работы приложения /data/yarn приходит в свой первозданный вид:

/data/yarn/ 
├── filecache 
├── log 
├── nmPrivate 
└── usercache 
    └── root 
        ├── appcache 
        └── 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
0158f7ae86304434be75c7d6e645a658.png
Для 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
7f65956572eb440dacb118920af0aac6.png
Все ноды должны быть в списке со статусом 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 и посмотреть, сколько ядер и памяти используется всеми приложениями в общей сложности на каждой ноде.
e6b49dc1491c4740b9f5fbd7fd991d48.png

Заключение

Мы собрали Hadoop из исходного кода, установили, сконфигурировали и протестировали работоспособность на отдельной машине и в распределенном кластере. Если тема вам интересна, если есть желание подобным образом собрать другие сервисы из экосистемы Hadoop, оставлю ссылку на скрипт, который поддерживаю для собственных нужд:

github.com/hadoopfromscratch/hadoopfromscratch

С его помошью вы сможете установить zookeeper, spark, hive, hbase, cassandra, flume. Если найдете ошибки или неточности, пожалуйста напишите. Буду очень признателен.

Комментарии (0)

© Habrahabr.ru