Hadoop From Scratch
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.defaultFS hdfs://localhost:8020
EOF
В файле hdfs-site.xml конфигурируем 4 параметра. Количество реплик устанавливаем в 1, так как наш «кластер» состоит всего из одной ноды. Также конфигурируем директории, где будут хранить данные NameNode, DataNode и SecondaryNameNode.
cat << EOF > /opt/hadoop/etc/hadoop/hdfs-site.xml
dfs.replication 1
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.hostname localhost
yarn.nodemanager.resource.memory-mb 4096
yarn.nodemanager.resource.cpu-vcores 4
yarn.scheduler.maximum-allocation-mb 1024
yarn.scheduler.maximum-allocation-vcores 1
yarn.nodemanager.vmem-check-enabled false
yarn.nodemanager.local-dirs /data/yarn
yarn.nodemanager.log-dirs /data/yarn/log
yarn.log-aggregation-enable true
yarn.nodemanager.aux-services mapreduce_shuffle
yarn.nodemanager.aux-services.mapreduce_shuffle.class org.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.name yarn
mapreduce.jobhistory.address localhost:10020
mapreduce.jobhistory.webapp.address localhost:19888
mapreduce.job.reduce.slowstart.completedmaps 0.8
yarn.app.mapreduce.am.resource.cpu-vcores 1
yarn.app.mapreduce.am.resource.mb 1024
yarn.app.mapreduce.am.command-opts -Djava.net.preferIPv4Stack=true -Xmx768m
mapreduce.map.cpu.vcores 1
mapreduce.map.memory.mb 1024
mapreduce.map.java.opts -Djava.net.preferIPv4Stack=true -Xmx768m
mapreduce.reduce.cpu.vcores 1
mapreduce.reduce.memory.mb 1024
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
Для 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. Если найдете ошибки или неточности, пожалуйста напишите. Буду очень признателен.