通过windows Docker部署hadoop集群实现数据重删

通过Docker部署hadoop集群实现数据重删

集群规划

  • 1个NameNode节点
  • 1个SecondaryNameNode节点
  • 1个ResourceManager节点
  • 1个JobHistory节点
  • 5个Slave节点
  • 1个Client节点

其中Slave节点包含DataNode和NodeManager两种角色。Client节点是用来操作的节点,所有操作都尽量在这个节点上进行。以上共10个节点,7种角色。

由于官方apache/hadoop容器过于精简缺少一些hadoop运行所需的工具,所有先构建一个可以正常运行hadoop的模板镜像,再通过该模板镜像构建以上10个容器

实验环境

  • windows11
  • Docker version 27.3.1
  • hadoop 3.4.1

提前下载的工具

  • Docker Desktop Installer.exe (官网下载安装Docker)
  • jdk-8u431-linux-x64(jdk1.8.0_431)Java Downloads | Oracle

一、配置模板镜像

使用官方apache/hadoop:3.4.1构建模板容器,配置java环境变量、安装ssh,用来创建后续容器。

1. 拉取官方apache/hadoop:3.4.1镜像

docker run -itd --name=hadoop_node apache/hadoop:3.4.1 bash -c "tail -f /dev/null"

image-20241218165316490

使用官方apache/hadoop:3.4.1镜像运行容器

在主机运行

docker run -itd --name=hadoop_node apache/hadoop:3.4.1 bash -c "tail -f /dev/null"

-itd:表示交互式且后台运行,少了 -it 容器会自动停止
–name:容器名
bash:启动容器内的 Bash Shell
-c:告诉 Bash 执行一个字符串命令
tail -f /dev/null :容器不会执行任何有意义的任务,只是保持运行状态。tail -f /dev/null 是一种常见的方式,用于让容器处于空闲但活动状态,避免它因为没有前台进程而自动退出。

image-20241218165347534

从本地上传java jdk文件到容器并解压:

docker cp D:\xurui\Docker\hadoop-cluster\java\jdk-8u431-linux-x64.tar.gz hadoop_node:/root

docker cp <jdk路径> <容器id/容器名字>:<容器路径>

image-20241218173251635

进入容器并解压

#进入容器指令
docker exec -it hadoop_node bash
#解压
tar -zxvf /root/jdk-8u431-linux-x64.tar.gz 

yum换源与更新操作:

cd /etc/yum.repos.d/
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
yum clean all && yum makecache
yum update -y

2. 安装SSh

官方hadoop容器可以看做是一个非常精简的系统,很多功能没有,需要自己安装。Hadoop需要SSH,但容器没有自带,需要我们安装。

##首先需要设置密码
passwd root
##安装SSH
yum -y install openssh-clients openssh-server
##生成key文件
ssh-keygen -A
##启动sshd
/usr/sbin/sshd

查看运行进程,确认ssh启动

image-20241218170927603

然后检查配置文件,查看以下两个配置是否开启,没有则在文件末尾补充

vim /etc/ssh/sshd_config

PermitRootLogin yes
PasswordAuthentication yes
#这两个配置允许外网通过 ssh 连接该服务器(容器)

运行hadoop需要which命令,同样容器没有自带,需要安装。

yum -y install which

3. 配置环境变量并在容器启动时启动sshd

在/etc/profile.d中新建一个run.sh文件
在run.sh文件中写入下面6行内容:

##运行容器时配置环境变量
export JAVA_HOME=/root/jdk1.8.0_431
export HADOOP_HOME=/opt/hadoop
export PATH=$PATH:$JAVA_HOME/bin
export PATH=$PATH:$HADOOP_HOME/bin
export PATH=$PATH:$HADOOP_HOME/sbin

##设置用户为root
export HDFS_NAMENODE_USER=root
export HDFS_DATANODE_USER=root
export HDFS_SECONDARYNAMENODE_USER=root
export YARN_RESOURCEMANAGER_USER=root
export YARN_NODEMANAGER_USER=root

启动sshd
/usr/sbin/sshd

用exit命令退出容器,重新启动容器并进入容器,上面配置的环境变量生效,sshd启动。

配置hadoop-env.sh

##/opt/hadoop/etc/hadoop/hadoop-env.sh
export JAVA_HOME=/root/jdk1.8.0_431

配置core-site.xml

<!-- /opt/hadoop/etc/hadoop/core-site.xml -->
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://localhost:9000</value>
    </property>
</configuration>

配置hdfs-site.xml

<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>
</configuration>

配置mapred-site.xml

<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
     <property>
        <name>yarn.app.mapreduce.am.env</name>
        <value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
    </property>
    <property>
        <name>mapreduce.map.env</name>
        <value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
    </property>
    <property>
        <name>mapreduce.reduce.env</name>
        <value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
    </property>
</configuration>

配置yarn-site.xml

<configuration>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
</configuration>

4. 单节点hadoop运行测试

启动伪分布集群并运行wordcount示例程序

准备测试数据
在/root目录底下新建一个input文件夹,在这个文件夹中新建一个test.txt文件,里面随便写点单词,然后将该文件多复制几份,这里复制了5份。

image-20241218181716056

#test.txt
2012-6-9 a 
2012-6-10 b 
2012-6-11 c 
2012-6-12 d 
2012-6-13 a 
2012-6-14 b 
2012-6-15 c 
2012-6-11 c 

格式化namenode

hdfs namenode -format

集群测试时,多次格式化namenode会导致namenode与datanode之间的不一致导致,每次格式化前要删除datanode数据 rm -rf /tmp/hadoop-root/dfs/data/* (datanode存储默认路径:/tmp/hadoop-root/dfs/data)

启动HDFS

start-dfs.sh

启动YARN

start-yarn.sh

查看相关进程是否都启动

jps

image-20241218182542450

有以下5个进程,说明启动成功。

将测试数据复制到HDFS中

hdfs dfs -put /root/input /

运行wordcount示例程序

hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.4.1.jar wordcount /input /output

查看输出结果

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

image-20241218184621812

从截图可以看出输出正确。该容器配置正确。

二、利用模板镜像搭建集群

集群规划
1个NameNode节点
1个SecondaryNameNode节点
1个ResourceManager节点
1个JobHistory节点
5个Slave节点
1个Client节点

Client节点是用来操作的节点,所有操作都尽量在这个节点上进行。

将上面的模板容器打包成镜像
将上面的模板容器复制10份,修改配置文件来实现7种节点。但是Docker容器不能直接复制,需要先打包成镜像,然后用这个镜像生成10个新的容器。
命令如下:

docker commit -a "xr" -m "配置ssh以及相关环境变量" hadoop_node hadoop_node:v1

1. 创建集群

创建网络

docker network create hadoop_nw

新建一个hadoop_nw的网络,后面将10个Hadoop节点容器都加入到该网络,就能相互间通信了。而且不需要配置hosts文件

用模板镜像创建10个容器

docker run -itd --network hadoop_nw -h namenode --name namenode -p 9870:9870 hadoop_node:v1

docker run -itd --network hadoop_nw -h secondarynamenode --name secondarynamenode -p 9868:9868 hadoop_node:v1

docker run -itd --network hadoop_nw -h resourcemanager --name resourcemanager -p 8088:8088 hadoop_node:v1

docker run -itd --network hadoop_nw -h jobhistory --name jobhistory -p 19888:19888 hadoop_node:v1

docker run -itd --network hadoop_nw -h slave1 --name slave1 hadoop_node:v1

docker run -itd --network hadoop_nw -h slave2 --name slave2 hadoop_node:v1

docker run -itd --network hadoop_nw -h slave3 --name slave3 hadoop_node:v1

docker run -itd --network hadoop_nw -h slave4 --name slave4 hadoop_node:v1

docker run -itd --network hadoop_nw -h slave5 --name slave5 hadoop_node:v1

docker run -itd --network hadoop_nw -h client --name client hadoop_node:v1

打开docker桌面客户端查看容器

image-20241219181435176

namenode、resourcemanager和jobhistory这3个节点做了端口映射。端口映射的作用是将Docker宿主机的某个端口映射到容器的某个端口上,这样通过访问Docker宿主机的这个端口就能间接访问到相应的容器端口了。就像从外网访问内网中的某台机器一样。在后面通过浏览器查看集群信息的时候会用到。

2. 在每个容器内执行以下操作

docker exec -it 节点容器 bash
sudo su -
#每个节点重置密码ssh才能连接成功
passwd root
#因为在前面模板镜像容器中伪分布式部署格式化过namenode,所以所有节点都要执行一次`rm -rf /tmp/hadoop-root/dfs/data/*`
rm -rf /tmp/hadoop-root/dfs/data/*

3. 修改Hadoop配置文件

client节点修改以下配置文件,然后复制到其它节点。

配置core-site.xml

<!-- vim /opt/hadoop/etc/hadoop/core-site.xml -->
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://namenode:9000</value>
    </property>
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/opt/hadoop/data</value>
    </property>
</configuration>

配置hdfs-site.xml

<!-- vim /opt/hadoop/etc/hadoop/hdfs-site.xml -->
<configuration>
    <!-- nn web 端访问地址 -->
    <property>
        <name>dfs.namenode.http-address</name>
        <value>namenode:9870</value>
    </property>
    <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>secondarynamenode:9868</value>
    </property>
    <property>
        	<name>dfs.webhdfs.enabled</name>
        	<value>true</value>
    </property>
</configuration>

配置mapred-site.xml

<!-- vim /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>jobhistory:10020</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.webapp.address</name>
        <value>jobhistory:19888</value>
    </property>
     <property>
        <name>yarn.app.mapreduce.am.env</name>
        <value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
    </property>
    <property>
        <name>mapreduce.map.env</name>
        <value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
    </property>
    <property>
        <name>mapreduce.reduce.env</name>
        <value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
    </property>
</configuration>

配置yarn-site.xml

<!-- vim /opt/hadoop/etc/hadoop/yarn-site.xml -->
<configuration>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>resourcemanager</value>
    </property>
</configuration>

配置 workers
修改文件 /opt/hadoop/etc/hadoop/workers
将里边的内容去掉(把 localhost 删掉)然后换成5个从节点:
注意:行末不能有空格,以及不能有空行的出现

slave1
slave2
slave3
slave4
slave5

4. 配置节点间SSH免密登录

注意所有节点要重新设置一次密码passwd root,否则ssh无法连通
ssh要在root用户下才能成功,使用sudo su -切换root用户
务必测试所有节点都能通过ssh 主机名(容器名)连接后,hadoop集群才能连通

在client节点执行:

ssh-keygen
ssh-copy-id namenode
ssh-copy-id secondarynamenode
ssh-copy-id resourcemanager
ssh-copy-id jobhistory
ssh-copy-id slave1
ssh-copy-id slave2
ssh-copy-id slave3
ssh-copy-id slave4
ssh-copy-id slave5

image-20241218214411226

在resourcemanager节点执行:

ssh-keygen
ssh-copy-id slave1
ssh-copy-id slave2
ssh-copy-id slave3
ssh-copy-id slave4
ssh-copy-id slave5

5. 复制配置文件到所有节点

在client节点执行:

scp -r $HADOOP_HOME/etc/hadoop namenode:$HADOOP_HOME/etc
scp -r $HADOOP_HOME/etc/hadoop secondarynamenode:$HADOOP_HOME/etc
scp -r $HADOOP_HOME/etc/hadoop resourcemanager:$HADOOP_HOME/etc
scp -r $HADOOP_HOME/etc/hadoop jobhistory:$HADOOP_HOME/etc
scp -r $HADOOP_HOME/etc/hadoop slave1:$HADOOP_HOME/etc
scp -r $HADOOP_HOME/etc/hadoop slave2:$HADOOP_HOME/etc
scp -r $HADOOP_HOME/etc/hadoop slave3:$HADOOP_HOME/etc
scp -r $HADOOP_HOME/etc/hadoop slave4:$HADOOP_HOME/etc
scp -r $HADOOP_HOME/etc/hadoop slave5:$HADOOP_HOME/etc

6. 启动Hadoop集群

在client节点执行:

1、格式化namenode

ssh namenode
hdfs namenode -format

多次格式化namenode会导致namenode与datanode之间的不一致导致,每次格式化前要删除/tmp/hadoop-root/dfs/data rm -rf /tmp/hadoop-root/dfs/data/*

2、启动HDFS集群

ssh namenode
start-dfs.sh
#stop-dfs.sh

3、启动YARN集群

ssh resourcemanager
start-yarn.sh
#stop-yarn.sh

4、启动JobHistory

ssh jobhistory 

/opt/hadoop/bin/mapred --daemon start historyserver
#/opt/hadoop/bin/mapred --daemon stop historyserver

HDFS http://localhost:50070/

image-20241219113653969

5个slave节点作为datanode

image-20241219113714582

YARN http://localhost:8088/

image-20241219113925825

jobhistory http://localhost:19888/

image-20241219114039469

7. 运行wordcount示例程序

将测试数据复制到HDFS中

hdfs dfs -put /root/input /

运行wordcount示例程序

hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.4.1.jar wordcount /input /output

image-20241219114509394

查看输出结果

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

image-20241219114536169

三、使用hadoop mapreduce进行数据去重

思路:

  1. Mapper 阶段:
    每一行的数据(如 “2012-6-9 a”)作为键输出。
    key 是日期和字母的组合,value 可以是一个固定值(如空字符串或常量)。
  2. Reducer 阶段:
    Reducer 接收所有来自 Mapper 的键值对,利用 HashSet 或类似的数据结构去重。由于每行数据作为唯一的 key,相同的日期-字母组合会合并为一条数据。
  3. 输出:
    输出去重后的每一行数据。

1. 代码实现

Mapper 类
在 Mapper 阶段,我们将输入的每一行(日期和字母)作为 key,值部分可以为空字符串或任意常量,因为我们不需要在 Reducer 阶段使用 value。最终我们通过 key 来去重。

//DedupMapper.java

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;

public class DedupMapper extends Mapper<Object, Text, Text, Text> {

    // Mapper方法
    @Override
    public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
        // 每一行数据格式:2012-6-9 a
        // 输出键值对,键是每行数据,值是一个空字符串
        context.write(value, new Text(""));
    }
}

Reducer 类

Reducer 会接收 Mapper 输出的每一行数据,由于 Reducer 每个键只会收到一次,直接输出键即可实现去重。

//DedupReducer.java

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;

public class DedupReducer extends Reducer<Text, Text, Text, Text> {

    // Reducer方法
    @Override
    public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
        
        // 直接输出key,它已经保证了唯一性
        context.write(key, new Text(""));
    }
}

Driver 类

//DedupDriver.java

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

public class DedupDriver {

    public static void main(String[] args) throws Exception {
        if (args.length != 2) {
            System.err.println("Usage: DedupDriver <input path> <output path>");
            System.exit(-1);
        }

        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "Data Deduplication");
        job.setJarByClass(DedupDriver.class);

        // 设置 Mapper 类和 Reducer 类
        job.setMapperClass(DedupMapper.class);
        job.setReducerClass(DedupReducer.class);

        // 设置 Map 输出的类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);

        // 设置 Reduce 输出的类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);

        // 设置输入和输出路径
        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        // 提交作业并等待完成,如果成功返回 0,否则返回 1
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

2. 代码编译和文件上传

client容器中编辑代码

image-20241219161325117

编译java文件

javac -classpath $(hadoop classpath): *.java

打包java文件

jar -cvf DedupDriver.jar *.class

编辑去重文件file1、file2

#vim /root/input/file1
2012-6-9 a
2012-6-10 b
2012-6-11 c
2012-6-12 d
2012-6-13 a
2012-6-14 b
2012-6-15 c
2012-6-11 c

#vim /root/input/file2
2012-6-9 b
2012-6-10 a
2012-6-11 b
2012-6-12 d
2012-6-13 a
2012-6-14 c
2012-6-15 d
2012-6-11 c

将file1、file2上传到hdfs中

hdfs dfs -put /root/input /

image-20241219163928486

3. 运行去重程序

进行数据去重

hadoop jar DedupDriver.jar DedupDriver /input /dedup/output

查看输出结果

hdfs dfs -cat /dedup/output/part-r-00000

image-20241219164818532

结果正确

image-20241219164959314

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值