IDEA调试Docker上的Hadoop

上一篇Docker搭建Hadoop环境文章中我介绍了一下如何在Docker上搭建Hadoop环境,这篇文章讲介绍如何通过IDEA连接Docker容器里的Hadoop并且调试Hadoop的代码
注意:文中多次提到容器终端是hadoop-master这个容器的终端

安装JDK

  • Ubuntu默认安装的是OpenJDK,会少很多东西,我们需要重新下载JDK替换默认的OpenJDK,配置过程请参考Ubuntu修改默认JDK

安装IDEA

  • 方式一:可以直接通过Ubuntu的应用商店安装Community版本的IDEA
  • 方式二:通过Jetbrains官网下载https://www.jetbrains.com/idea/download/#section=linux下载IDEA,将其解压找到里面的启动文件双击之后就能运行了
  • Ultimate版本的IDEA只有30天的试用期,可以在网上查找IDEA的激活服务器或者破解方法,学生可以通过学校的邮箱申请学生账号,学生账号可以免费试用IDEA

创建Maven项目

打开IDEA之后选择新建Maven项目,如下图所示,后续步骤设置即可
创建maven项目
创建完成后,IDEA右下角会提示是否自动导入Maven以来,选择自动导入,这样每次只要修改保存了pom.xml文件Maven就会自动下载导入添加的依赖。然后往pom.xml文件里添加Hadoop依赖并保存

    <dependencies>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>2.7.2</version>
        </dependency>

        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
            <version>2.7.2</version>
        </dependency>

        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.7.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

添加Hadoop依赖

编写WordCount程序

import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class WordCount {

    public static class TokenizerMapper
            extends Mapper<Object, Text, Text, IntWritable> {

        private final static IntWritable one = new IntWritable(1);
        private Text word = new Text();

        public void map(Object key, Text value, Context context
        ) throws IOException, InterruptedException {
            StringTokenizer itr = new StringTokenizer(value.toString());
            while (itr.hasMoreTokens()) {
                word.set(itr.nextToken());
                context.write(word, one);
            }
        }
    }

    public static class IntSumReducer
            extends Reducer<Text, IntWritable, Text, IntWritable> {
        private IntWritable result = new IntWritable();

        public void reduce(Text key, Iterable<IntWritable> values,
                           Context context
        ) throws IOException, InterruptedException {
            int sum = 0;
            for (IntWritable val : values) {
                sum += val.get();
            }
            result.set(sum);
            context.write(key, result);
        }
    }

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "word count");
        job.setJarByClass(WordCount.class);
        job.setMapperClass(TokenizerMapper.class);
        job.setCombinerClass(IntSumReducer.class);
        job.setReducerClass(IntSumReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

本地执行MapReduce程序

项目中导入Hadoop的依赖,可以在没有配置Hadoop分布式环境的情况下采用本地方式测试MapReduce程序

创建测试数据

在项目的根目录下创建一个input目录,并在该目录下输入一些测试数据

Hadoop
Spark
Hive
HBase
BigData
Hadoop

添加测试数据

执行WordCount程序

首先我们先配置执行参数,如下图所示,Main class选择前面写的WordCount,Program arguments里填写两个参数——输入路径和输出路径:input/ output/
配置执行参数
执行程序,执行完成后项目根目录下会多出一个output目录,查看part-r-00000文件可以看到执行任务的结果,输出了单词的统计数量
WordCount执行结果

连接Docker集群执行WordCount

修改Hadoop配置

在IDEA通过Docker集群执行MapReduce任务是会报一个异常,说本机的用户名没有权限访问Docker容器内的HDFS,此时需要修改容器内Hadoop的两个配置文件,通过master的终端执行

vim /usr/local/hadoop/etc/hadoop/core-site.xml

添加以下内容

    <property>
        <name>hadoop.http.staticuser.user</name>
        <value>root</value>
    </property>

修改core-site.xml

vim /usr/local/hadoop/etc/hadoop/hdfs-site.xml

添加以下内容

    <property>
        <name>dfs.permissions</name>
        <value>false</value>
    </property>

hdfs-site.xml
重新启动Hadoop

# 先停止Hadoop相关程序
stop-all.sh

stop-all.sh

# 再次启动Hadoop
./start-hadoop.sh

配置log4j

hadoop默认使用了log4j输出日志,如果没有自定义配置log4j,控制台不会输入mapreduce过程,所以需要在resources下配置日志信息,在IDEA中项目的src/main/resources下创建一个log4j.properties文件,将以下内容复制进去并保存(读者可以尝试不配置这一步,看控制台的输出信息是什么)

log4j.rootLogger = info,stdout

### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志文件设置 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = vincent_player_debug.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志文件设置 ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File = vincent_player_error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n 

删除output目录

由于前面我们执行了一次./run-wordcount.sh脚本,这个脚本已经生成了一个目录output,hadoop默认是不能覆盖原来的执行的输出结果,因此在执行MapReduce时会报错,提示output已经存在,因此做实验时需要将原来的output删除,在容器终端通过以下命令删除

hdfs dfs -rm -R /user/root/output

在这里插入图片描述

配置执行参数

将原来本地执行的参数/input /output改成Docker容器中的hdfs

hdfs://localhost:9000/user/root/input 
hdfs://localhost:9000/user/root/output

配置执行参数
如果使用localhost显示无法连接,可能是端口没映射成功,这里可以先把localhost改为hadoop-master的IP地址,可通过在本地终端中执行以下命令查看hadoop-master的地址(也可在hadoop-master容器里使用ifconfig命令查看)

# sudo docker inspect 【contanier-id】
sudo docker inspect hadoop-master

执行WordCount

此时所有配置过程已经完成,可以执行WordCount程序了,在控制台可以看到有Docker容器集群的执行结果了
集群执行结果
此时在容器终端输入以下命令可以看到单词统计结果

hdfs dfs -cat /user/root/output/part-r-00000

执行结果

补充内容

备份镜像

1、配置好Docker容器之后建议将自己配置好的容器备份一份,方便迁移到其他机器使用,首先通过以下命令将容器保存为镜像

# docker commit [container-id] [new image-id]
docker commit hadoop-master my-hadoop-master
docker commit hadoop-slave1 my-hadoop-slave1
docker commit hadoop-slave2 my-hadoop-slave2

此镜像的内容就是你当前容器的内容,接下来你可以用此镜像再次运行新的容器
2、镜像备份,这一步是将上一步保存的镜像保存到文件中,执行以下命令

# docker  save -o [filename.tar] [image-id]
docker  save -o my-hadoop-master.tar my-hadoop-master
docker  save -o my-hadoop-slave1.tar my-hadoop-slave1
docker  save -o my-hadoop-slave2.tar my-hadoop-slave2

3、通过以下命令恢复镜像或将镜像迁移到其他设备

# docker load -i [image-file]
docker load -i my-hadoop-master.tar
docker load -i my-hadoop-slave1.tar
docker load -i my-hadoop-slave2.tar

可能要用到的一些命令

将hdfs里的文件移动到本地

hdfs dfs -copyToLocal [src] [local target]

或者

hadoop fs -copyToLocal [src] [local target]

本文是在本人第一次配置完成很久之后才写的,可能有些容易出错的细节没写清楚或者有错误,如果有问题,欢迎在下方留言纠正或提问

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐