суббота, 16 февраля 2013 г.

Hadoop - Слоники большие и маленькие

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

И так, во первых Hadoop бывает в нескольких дистрибутивных исполнениях:

1. Apache, как есть http://hadoop.apache.org/releases.html
Hadoop в чистом виде. Ставить на всех нодах надо руками, править конфиги. Учить его видеть HBase. В общем надо с головой уходить в администрирование. Свои плюсы в этом конечно есть, но где взять столько времени ?

2. Cloudera http://www.cloudera.com
Половина, из создателей Hadoop организовала свою кантору и свой дистрибутив.
Главная его изюминка - это ClouderaManager, с помощью которого можно очень легко управлять кластером, мониторить, добавлять ноды. Ставиться всё в два клика, система разворачивается на кластере, всё нужное в конфигах прописывается автоматически. В общем прелесть.
Это коммерческая система, но есть бесплатная версия, с ограничением в 50 нод на кластер.

Так выглядит Web консоль ClouderaManager


3. MapR http://www.mapr.com/
Другая половина создателей Hadoop решала сделать свой лунопарк, и выпустила свой дистрибутив. Всё как в Cloudera или в Cloudera всё как в MapR. Плюс комунити, форум, и отсутствие ограничений на размер кластера. Вобщем тоже красота.

MapR я не пробовал, а начал работать с Cloudera, по этому о всех изысках по порядку:

1. У Cloudera свой мавновский репозиторий. 
И необходимо использовать именно их зависимости в купе с версиями системы на вашем кластере
---------------------------------------------------------------------------
 <repositories>
     <repository>
         <id>apache</id>
         <url>https://repository.apache.org/content/repositories/releases/
         </url>
     </repository>
     <repository>
        <id>cloudera</id>
        <url>https://repository.cloudera.com/artifactory/cloudera-repos/
        </url>
    </repository>
</repositories>
---------------------------------------------------------------------------

<dependency>
     <groupId>org.apache.hadoop</groupId>
      <artifactId>hadoop-client</artifactId>
      <version>2.0.0-mr1-cdh4.1.2</version>
      <scope>provided</scope>
</dependency>
---------------------------------------------------------------------------

2. Минимизация Super Jar
Я привык собирать super jar. Это удобно, все нужные зависимости находятся уже внутри.
Таким способом можно избежать возможных проблем в продакшене.
Но как оказалось, для map - reduce такой подход опасен. Когда в jar включаются клоудеровские зависимости, в run time начинается какой то бардак. Такое впечатление что вместо одних методов начинают вызываются совсем другие. Поэтому из конечного архива пришлось исключить все клоудеровские зависимости. Сделать это можно просто, добавив ключ  <scope>provided</scope> к мавновской зависимости. После этого m/r работает как надо

Но если надо работать с HBase, то следует полдностью включить org.apache.hbase
Иначе хадуп просто не видит этих библиотек

3. Map-reduce значения по умолчанию
Очень важно непосредственно определять входные и выходные форматы Job-а, т.к по умолчанию они всегда текстовые.

job.setInputFormatClass(SequenceFileInputFormat.class);
job.setOutputFormatClass(SequenceFileOutputFormat.class);

Тоже самое касается форматов входа и выхода Mapper-ов и Reducer-ов. Особенно болезненно это проявляется в задачах со сцепленными Job-ми

job.setOutputKeyClass(ReportReduceWritable.class);   //Отдельно для Reduce
job.setOutputValueClass(MyKeyWritable.class);

job.setMapOutputKeyClass(Text.class);                           //Отдельно для Map
job.setMapOutputValueClass(MyReduceWritable.class);

4. Удаление директорий
Перед запуском m/r задачи надо удалять все выходные директории, которые были созданы после предыдущего запуска. Нначе Job будет ругаться и вываливаться.

5. Итератор Reducer-a нельзя использовать повторно
Интересная грабелька. Когда мы имплементируем org.apache.hadoop.mapreduce.Reducer
на вход приходит некий итератор Iterable<MyValue> values. Он позволяет считать данные только один раз. После этого перевести курсор в начало не возможно. Приходится выкручиваться сохранением элементов во временную коллекцию.


6. Запуск Job от имени другого пользователя и TableMapReduceUtil.initTableMapperJob
Убили на эти грабли день, прежде чем понять в чем дело.
Запускаем Job по крону от имени специального - системного пользователя
dmpservice. Валится !

java.io.IOException: java.lang.RuntimeException: java.io.IOException: Permission denied
at org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil.findOrCreateJar(TableMapReduceUtil.java:521)
at org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil.addDependencyJars(TableMapReduceUtil.java:472)
at org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil.addDependencyJars(TableMapReduceUtil.java:438)
at org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil.initTableMapperJob(TableMapReduceUtil.java:138)
at org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil.initTableMapperJob(TableMapReduceUtil.java:215)
at org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil.initTableMapperJob(TableMapReduceUtil.java:81)
at ru.crystaldata.analyticengine.mr.HBaseReaderJob.createJob(HBaseReaderJob.java:206)
at ru.crystaldata.analyticengine.JobsRunner.main(JobsRunner.java:52)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.hadoop.util.RunJar.main(RunJar.java:208)
Caused by: java.lang.RuntimeException: java.io.IOException: Permission denied
at org.apache.hadoop.util.JarFinder.getJar(JarFinder.java:164)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil.findOrCreateJar(TableMapReduceUtil.java:518)
... 12 more
Caused by: java.io.IOException: Permission denied
at java.io.UnixFileSystem.createFileExclusively(Native Method)
at java.io.File.checkAndCreate(File.java:1704)
at java.io.File.createTempFile(File.java:1792)
at org.apache.hadoop.util.JarFinder.getJar(JarFinder.java:156)
... 17 more

Делаем всё тоже самое но от обычного пользователя - работает.
Убираем из джоба HBase - мепперы, запускаем от dmpservice - работает.
Вывод: что то темное творится в недрах TableMapReduceUtil.initTableMapperJob

Как оказалось TableMapReduceUtil.initTableMapperJob создает темповый файл для своих внутренних нужд, и пытается разместить его в HOME, а так как у пользователя dmpservice,
не заданна данная переменная, то запись производится в root /, на который естественно нет прав.

Лечится - при создании задачи крону, прописываем HOME=var/tmp


Пока это всё. Удачи в начинаниях :)








Комментариев нет:

Отправить комментарий