乐于分享
好东西不私藏

在一台Windows电脑上搭建hadoop集群

在一台Windows电脑上搭建hadoop集群

分享一下在一台Windows电脑上搭建HA的Hadoop集群的经历。

一、背景介绍:

宿主机:

环境:Windows10专业版

CPU:i7-8550U

内存:24G

二、主要部件:

VirtualBox:创建虚拟机的应用

Vagrant:通过配置文件方便批量创建虚拟机

Hadoop:官方版本3.3.6

Zookeeper:官方版本3.8.0

三、集群各节点角色:

一共规划了5个节点,各节点的角色包括:

  1. hadoop-nn1:NameNode(active)、ResourceManager、JobHistoryServer、DFSZKFailoverController、QuorumPeerMain

  2. hadoop-nn2:NameNode(standby)、DFSZKFailoverController、QuorumPeerMain

  3. hadoop-dn1: DataNode、NodeManager、JournalNode、QuorumPeerMain

  4. hadoop-dn2: DataNode、NodeManager、JournalNode

  5. hadoop-dn3: DataNode、NodeManager、JournalNode

四、主要步骤:

  1. 下载并安装VirtualBox、Vagrant

  2. 通过配置文件Vagrantfile来自动创建5个虚拟机节点

        ①在宿主机集群目录(比如我的是D:/hadoop-cluster)下新建文件Vgrantfile(V大写,没有扩展名),具体内容如下:

    # -*- mode: ruby -*-  # 指定编辑器使用 Ruby 语法高亮# vi: set ft=ruby :   # 指定 Vim 编辑器文件类型为 Ruby# 定义集群的私有网络网段,使用 VirtualBox 默认 Host-Only 网段CLUSTER_NETWORK = "192.168.56"# 初始化 Vagrant 配置,使用 API 版本 2Vagrant.configure("2") do |config|  # =================== 全局配置(对所有虚拟机生效) ===================  config.vm.box = "centos/7"  # 使用 CentOS 7 作为基础操作系统镜像  config.vm.synced_folder ".", "/vagrant", disabled: true  # 禁用默认的/vagrant共享目录,避免权限问题  # 所有虚拟机的公共初始化脚本  config.vm.provision "shell", inline: <<-SHELL    # 设置系统时区为上海(东八区)    timedatectl set-timezone Asia/Shanghai    # 停止并禁用防火墙(开发环境简化配置,生产环境应保持开启)    systemctl stop firewalld    systemctl disable firewalld    # 临时禁用 SELinux(Security-Enhanced Linux)    setenforce 0    # 永久禁用 SELinux(修改配置文件)    sed -i 's/^SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config    # 优化系统内核参数    echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf  # 禁用 IPv6 以减少网络开销    echo "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.conf    echo "vm.swappiness = 10" >> /etc/sysctl.conf  # 降低内存交换倾向,提升性能    sysctl -p  # 重新加载 sysctl 配置使其生效    # 创建 Hadoop 生态组件所需的目录结构    mkdir -p /opt/{hadoop,zookeeper}  # 软件安装目录    mkdir -p /data/{hadoop,hdfs,zookeeper,journalnode}  # 数据存储目录  SHELL  # =================== 全局配置结束 ===================  # =================== NameNode 1 节点(Active NameNode 候选) ===================  config.vm.define "nn1" do |nn|  # 定义虚拟机,内部标识为 "nn1"    nn.vm.hostname = "hadoop-nn1"  # 设置虚拟机主机名    # 配置第一块网卡:Host-Only 网络,静态 IP,用于集群内部通信    # nn.vm.network "private_network", type: "static", ip: "#{CLUSTER_NETWORK}.10"        nn.vm.network "private_network", type: "static",ip: "#{CLUSTER_NETWORK}.10", netmask: "255.255.255.0"    # 配置第二块网卡:NAT 网络,用于访问宿主机和互联网    # nn.vm.network "public_network", use_dhcp_assigned_default_route: true    # VirtualBox 虚拟机特定配置    nn.vm.provider "virtualbox" do |vb|      vb.name = "hadoop-nn1"  # VirtualBox 管理器显示的虚拟机名称      vb.memory = 2048  # 分配 1.5GB 内存(考虑到总内存 24GB 的合理分配)      vb.cpus = 1  # 分配 1 个 CPU 核心(i7-8550U 为 4 核 8 线程,5 个节点各 1 核合理)      vb.gui = false  # 不显示图形界面,节省资源      vb.customize ["modifyvm", :id, "--vram", "16"]  # 设置显存为 16MB,避免图形驱动问题    end    # nn1 节点的专用配置脚本    nn.vm.provision "shell", inline: <<-SHELL      # 配置 /etc/hosts 文件,添加集群所有节点的主机名解析      # 这样可以通过主机名而非 IP 地址访问其他节点      echo "#{CLUSTER_NETWORK}.10 hadoop-nn1" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.11 hadoop-nn2" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.12 hadoop-dn1" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.13 hadoop-dn2" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.14 hadoop-dn3" >> /etc/hosts      # 添加宿主机 Windows 的解析,方便从虚拟机访问宿主机      echo "192.168.56.1 windows-host" >> /etc/hosts      # 为 NameNode 创建专用数据目录      mkdir -p /data/hdfs/namenode      chmod 755 /data/hdfs/namenode    SHELL  end  # =================== NameNode 1 定义结束 ===================  # =================== NameNode 2 节点(Standby NameNode 候选) ===================  config.vm.define "nn2" do |nn|  # 定义第二个 NameNode    nn.vm.hostname = "hadoop-nn2"  # 设置主机名    nn.vm.network "private_network", type: "static", ip: "#{CLUSTER_NETWORK}.11"  # Host-Only 网卡    nn.vm.network "public_network", use_dhcp_assigned_default_route: true  # NAT 网卡    nn.vm.provider "virtualbox" do |vb|      vb.name = "hadoop-nn2"      vb.memory = 2048  # 同样分配 1.5GB 内存      vb.cpus = 1      vb.gui = false      vb.customize ["modifyvm", :id, "--vram", "16"]    end    nn.vm.provision "shell", inline: <<-SHELL      # 配置 hosts 文件,确保双向解析      echo "#{CLUSTER_NETWORK}.10 hadoop-nn1" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.11 hadoop-nn2" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.12 hadoop-dn1" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.13 hadoop-dn2" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.14 hadoop-dn3" >> /etc/hosts      echo "192.168.56.1 windows-host" >> /etc/hosts      # 为 Standby NameNode 创建检查点目录      mkdir -p /data/hdfs/namesecondary      chmod 755 /data/hdfs/namesecondary    SHELL  end  # =================== NameNode 2 定义结束 ===================  # =================== DataNode 1 节点(JournalNode + DataNode) ===================  config.vm.define "dn1" do |dn|  # 定义第一个 DataNode    dn.vm.hostname = "hadoop-dn1"  # 设置主机名为 hadoop-dn1    dn.vm.network "private_network", type: "static", ip: "#{CLUSTER_NETWORK}.12"  # Host-Only 网卡    dn.vm.network "public_network", use_dhcp_assigned_default_route: true  # NAT 网卡    dn.vm.provider "virtualbox" do |vb|      vb.name = "hadoop-dn1"      vb.memory = 2048  # 分配 1GB 内存(DataNode 和 JournalNode 内存需求较低)      vb.cpus = 1      vb.gui = false    end    dn.vm.provision "shell", inline: <<-SHELL      # 配置 hosts 文件      echo "#{CLUSTER_NETWORK}.10 hadoop-nn1" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.11 hadoop-nn2" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.12 hadoop-dn1" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.13 hadoop-dn2" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.14 hadoop-dn3" >> /etc/hosts      echo "192.168.56.1 windows-host" >> /etc/hosts      # 为 DataNode 创建数据块存储目录      mkdir -p /data/hdfs/datanode      chmod 755 /data/hdfs/datanode      # 为 JournalNode 创建编辑日志存储目录      mkdir -p /data/journalnode/dn1      chmod 755 /data/journalnode/dn1    SHELL  end  # =================== DataNode 1 定义结束 ===================  # =================== DataNode 2 节点(JournalNode + DataNode) ===================  config.vm.define "dn2" do |dn|  # 定义第二个 DataNode    dn.vm.hostname = "hadoop-dn2"  # 设置主机名为 hadoop-dn2    dn.vm.network "private_network", type: "static", ip: "#{CLUSTER_NETWORK}.13"  # Host-Only 网卡    dn.vm.network "public_network", use_dhcp_assigned_default_route: true  # NAT 网卡    dn.vm.provider "virtualbox" do |vb|      vb.name = "hadoop-dn2"      vb.memory = 2048  # 分配 1GB 内存      vb.cpus = 1      vb.gui = false    end    dn.vm.provision "shell", inline: <<-SHELL      # 配置 hosts 文件      echo "#{CLUSTER_NETWORK}.10 hadoop-nn1" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.11 hadoop-nn2" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.12 hadoop-dn1" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.13 hadoop-dn2" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.14 hadoop-dn3" >> /etc/hosts      echo "192.168.56.1 windows-host" >> /etc/hosts      # 为 DataNode 和 JournalNode 创建目录      mkdir -p /data/hdfs/datanode      chmod 755 /data/hdfs/datanode      mkdir -p /data/journalnode/dn2      chmod 755 /data/journalnode/dn2    SHELL  end  # =================== DataNode 2 定义结束 ===================  # =================== DataNode 3 节点(JournalNode + DataNode) ===================  config.vm.define "dn3" do |dn|  # 定义第三个 DataNode    dn.vm.hostname = "hadoop-dn3"  # 设置主机名为 hadoop-dn3    dn.vm.network "private_network", type: "static", ip: "#{CLUSTER_NETWORK}.14"  # Host-Only 网卡    dn.vm.network "public_network", use_dhcp_assigned_default_route: true  # NAT 网卡    dn.vm.provider "virtualbox" do |vb|      vb.name = "hadoop-dn3"      vb.memory = 2048  # 分配 1GB 内存      vb.cpus = 1      vb.gui = false    end    dn.vm.provision "shell", inline: <<-SHELL      # 配置 hosts 文件      echo "#{CLUSTER_NETWORK}.10 hadoop-nn1" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.11 hadoop-nn2" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.12 hadoop-dn1" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.13 hadoop-dn2" >> /etc/hosts      echo "#{CLUSTER_NETWORK}.14 hadoop-dn3" >> /etc/hosts      echo "192.168.56.1 windows-host" >> /etc/hosts      # 为 DataNode 和 JournalNode 创建目录      mkdir -p /data/hdfs/datanode      chmod 755 /data/hdfs/datanode      mkdir -p /data/journalnode/dn3      chmod 755 /data/journalnode/dn3    SHELL  end  # =================== DataNode 3 定义结束 ===================end# Vagrant 配置块结束

        ② 打开CMD,在宿主机集群目录(D:/hadoop-cluste)执行命令:vagrant up,系统自动创建这5个虚拟机节点

        ③ vagrant status验证虚拟机是否都已启动运行

        ④ 关闭宿主机的防火墙(必须),否则虚拟机无法访问宿主机,各种服务的web服务打不开。

        ⑤ 宿主机的hosts文件增加虚拟机的IP映射

    192.168.56.10 hadoop-nn1192.168.56.11 hadoop-nn2192.168.56.12 hadoop-dn1192.168.56.13 hadoop-dn2192.168.56.14 hadoop-dn3
    1. 配置5个虚拟机节点的基础环境

      ① 安装JDK,配置相关环境变量

      ② 创建用户hadoop和用户组hadoop

      ③ 把/data目录的所有者修改为hadoop

      ④ 执行visudo,把hadoop加入到sudo权限列表,然后切换到hadoop用户

      ⑤ 删除/etc/hosts中的127.0.0.1 localhost这一行

      ⑥ 配置nn1到其他节点的免密登录,配置nn2到nn1的免密登录

    1. 下载安装Hadoop、zookeeper

    2. 配置重要文件,然后把安装目录分发到各节点

        ① hadoop-env.sh

    export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk  

        ② core-site.xml

    <?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="configuration.xsl"?><configuration>    <!-- 制定hdfs的默认名称服务地址(逻辑名称) -->    <property>        <name>fs.defaultFS</name>        <value>hdfs://mycluster</value>    </property>    <!-- 指定Hadoop临时数据存储目录 -->    <property>        <name>hadoop.tmp.dir</name>        <value>/data/hadoop/tmp</value>    </property>    <!-- 禁用IP检查 -->    <property>        <name>dfs.namenode.datanode.registration.ip-hostname-check</name>        <value>false</value>    </property>    <property>        <name>ha.zookeeper.quorum</name>        <value>hadoop-nn1:2181,hadoop-nn2:2181,hadoop-dn1:2181</value>    </property></configuration>

        ③ hdfs-site.xml

    <?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="configuration.xsl"?><configuration>    <!-- 指定hdfs副本数量 -->    <property>        <name>dfs.replication</name>        <value>3</value>    </property>    <!-- =========HA配置========== -->    <!-- 逻辑集群名称,与core-site.xml中的fs.defaultFS对应 -->    <property>        <name>dfs.nameservices</name>        <value>mycluster</value>    </property>    <!-- 定义两个namenode -->    <property>        <name>dfs.ha.namenodes.mycluster</name>        <value>nn1,nn2</value>    </property>    <!-- nn1的RPC地址 -->    <property>        <name>dfs.namenode.rpc-address.mycluster.nn1</name>        <value>hadoop-nn1:8020</value>    </property>    <!-- nn2的RPC地址 -->    <property>        <name>dfs.namenode.rpc-address.mycluster.nn2</name>        <value>hadoop-nn2:8020</value>    </property>    <!-- nn1的http地址 -->    <property>        <name>dfs.namenode.http-address.mycluster.nn1</name>        <value>hadoop-nn1:9870</value>    </property>    <property>        <name>dfs.namenode.http-address.mycluster.nn2</name>        <value>hadoop-nn2:9870</value>    </property>    <!-- JournalNode集群地址 -->    <property>        <name>dfs.namenode.shared.edits.dir</name>        <value>qjournal://hadoop-dn1:8485;hadoop-dn2:8485;hadoop-dn3:8485/mycluster</value>    </property>    <!-- HA故障转移的Java类 -->    <property>        <name>dfs.client.failover.proxy.provider.mycluster</name>        <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>    </property>    <property>        <name>dfs.ha.automatic-failover.enabled</name>        <value>true</value>    </property>    <property>        <name>dfs.ha.fencing.methods</name>        <value>sshfence</value>    </property>    <property>        <name>dfs.ha.fencing.ssh.private-key-files</name>        <value>/home/hadoop/.ssh/id_rsa</value>    </property>    <property>        <name>dfs.ha.fencing.ssh.connect-timeout</name>        <value>30000</value>    </property>    <!-- JournalNode编辑日志目录 -->    <property>        <name>dfs.journalnode.edits.dir</name>        <value>/data/journalnode</value>    </property>    <!-- NameNode元数据存储目录 -->    <property>        <name>dfs.namenode.name.dir</name>        <value>file:///data/hdfs/namenode</value>    </property>    <!-- DataNode数据块存储目录 -->    <property>        <name>dfs.datanode.data.dir</name>        <value>file:///data/hdfs/datanode</value>    </property></configuration>

        ④ yarn-site.xml

    <?xml version="1.0"?><configuration><!-- Site specific YARN configuration properties -->    <property>        <name>yarn.resourcemanager.hostname</name>        <value>hadoop-nn1</value>    </property>    <property>        <name>yarn.resourcemanager.webapp.address</name>        <value>hadoop-nn1:8088</value>    </property>    <property>        <name>yarn.nodemanager.aux-services</name>        <value>mapreduce_shuffle</value>    </property>    <property>        <name>yarn.nodemanager.local-dirs</name>        <value>/data/hadoop/yarn/local</value>    </property>    <property>        <name>yarn.nodemanager.log-dirs</name>        <value>/data/hadoop/yarn/logs</value>    </property>    <property>        <name>yarn.log-aggregation-enabled</name>        <value>true</value>    </property></configuration>

        ⑤ mapred-site.xml

    <?xml version="1.0"?><?xml-stylesheet type="text/xsl" href="configuration.xsl"?><configuration>    <property>        <name>mapreduce.framework.name</name>        <value>yarn</value>    </property>    <property>        <name>mapreduce.jobhistory.address</name>        <value>hadoop-nn1:10020</value>    </property>    <property>        <name>mapreduce.jobhistory.webapp.address</name>        <value>hadoop-nn1:19888</value>    </property>    <property>        <name>mapreduce.map.memory.mb</name>        <value>512</value>    </property>    <property>        <name>mapreduce.reduce.memory.mb</name>        <value>768</value>    </property>    <property>        <name>yarn.app.mapreduce.am.env</name>        <value>HADOOP_MAPRED_HOME=/home/hadoop/hadoop</value>    </property>    <property>        <name>mapreduce.map.env</name>        <value>HADOOP_MAPRED_HOME=/home/hadoop/hadoop</value>    </property>    <property>        <name>mapreduce.reduce.env</name>        <value>HADOOP_MAPRED_HOME=/home/hadoop/hadoop</value>    </property></configuration>

        ⑥ workers

    hadoop-dn1hadoop-dn2hadoop-dn3

        ⑦ zookeeper的zoo.cfg

    tickTime=2000initLimit=10syncLimit=5dataDir=/data/zookeeperclientPort=2181server.1=hadoop-nn1:2888:3888server.2=hadoop-nn2:2888:3888server.3=hadoop-dn1:2888:3888

        ⑧ 在nn1/nn2/dn1的/data/zookeeper目录下创建文件myid,内容分别是1/2/3

    6. 启动集群

        ① 启动JournalNode

        ② 启动Zookeeper

        ③ 初始化NameNode

        ④ 启动NameNode

        ⑤ 启动DataNode

        ⑥ 启动ResourceManager

        ⑦ 启动NodeManager

    7. 验证集群

        ① 验证zookeeper

        三个节点分别执行zkServer.sh status,返回结果应该是1个leader,2个follower

        ② 验证NameNode

      执行命令hdfs haadmin -getServiceState nn1或nn2,返回结果一个是active,一个是standby

        ③ 验证hdfs

      能正常显示hdfs目录

        ④ 验证MapReduce      

    把hadoop配置文件目录下的所有xml文件上传到hdfs的/user/hadoop/wordcount/input目录下,然后执行以下命令:

    hadoop jar ~/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar \  wordcount \  /user/hadoop/wordcount/input \  /user/hadoop/wordcount/output

    命令执行成功,且有输出结果

      五、搭建过程中遇到的问题

      1. 如果是以学习和测试为目的搭建一个Hadoop集群,个人建议就从几个裸机的centos节点开始,这样能更好地理解一个组件需要有哪些最基本的配置,各配置属性的作用是什么,有任何问题也好处理。我最开始基于Docker镜像的方式搭建集群,过程中遇到了一些问题,大概总结一下:

            ① 从某种程度上来说,Docker镜像可以看做是黑盒,不管是用bde版本的hadoop镜像,还是官方的hadoop镜像,遇到问题之后不好定位和解决(比如Container启动就秒退)。

            ② 很多Docker镜像的地址无法直接访问,需要科学上网。

            ③ 一般来说,一个容器只运行一个进程,后续如果要加服务,就要加容器,非常吃资源,对于学习来说,一台宿主机很难应付

      2. 网络问题。

          ① 如果虚拟机ping不通宿主机,大概率是宿主机的防火墙没关

          ② 如果某些服务(比如zookeeper)启动就不成功,也有可能是虚拟机的防火墙没关,针对特定端口有拦截。

      3. 如果配置ssh免密登录时出现Permission Denied的问题

          ,可以尝试通过这个命令解决:

         ① sudo sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config

                ② sudo systemctl restart sshd

        1. 启动JournalNode时报错,报错日志:

          /data目录是创建虚拟机时由vagrant用户创建的,所以hadoop用户没有写入权限,需要把/data的所有者改为hadoop用户

        1. 在启动hdfs和zookeeper的过程中,都出现了进程启动起来,但实际无法生效的情况,提交MapReduce任务测试的时候,也出现了任务失败的情况。

        最后发现都是在etc/hosts中,有这一行:127.0.0.1 localhost,导致各节点启动服务的时候,端口监听是在127.0.0.1的IP上,其他节点与其进行通话就会连接失败,所以在配置过程中,就最好删掉这行。

        1. yarn跳转到某个log时打不开

          需要宿主机的hosts增加各虚拟机节点的IP映射

            纸上得来终觉浅,搭一个集群,只看纸面步骤的话,会觉得很简单,但真正实操起来,会遇到各种问题,正是在解决问题的过程中,对知识有了更深的了解,也更有可能应用到实际工作中。执行过程中的具体问题可以问豆包、元宝或其他AI,当然AI给的答案不一定完全正确,可以互相印章参考。

        相关学习资料