Hadoop 与 Spark 单节点大数据环境搭建

这篇记录整理单节点 Hadoop 与 Spark 环境的搭建过程。整体目标是在一台云服务器上完成 JDK、Hadoop、HDFS、YARN、MapReduce、Spark、spark-submit 和 PySpark/Jupyter 的基本验证。

一、前期准备

这次使用华为云服务器完成搭建,配置为 8 核 16G 内存。整个过程中只有一个节点 node01,因此以单节点伪分布式方式运行 Hadoop 和 Spark,服务器的详细配置如下:

操作截图 3

1. 网络配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# ls /etc/netplan/
# vi /etc/netplan/01-netcfg.yaml
network:
version: 2
ethernets:
ens3:
dhcp4: false
match:
macaddress: fa:16:3e:b1:41:1c
set-name: ens3
addresses: [192.168.1.101/24]
routes:
- to: default
via: 192.168.1.1
metric: 100
nameservers:
addresses: [202.201.0.131,202.201.0.132]
# netplan apply

操作截图 4

2. 设置ssh免密认证

1
2
3
4
5
6
7
# ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
# cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
# chmod 0600 ~/.ssh/authorized_keys
# vi ~/.ssh/config
Host *
StrictHostKeyChecking no
# chmod 600 ~/.ssh/config

操作截图 5

3. 主机名设置和解析

1
# ip addr

操作截图 6

1
2
3
4
5
6
# vi /etc/hosts
127.0.1.1 localhost
127.0.1.1 hadoop hadoop
127.0.1.1 node01
192.168.1.242 node01
# hostnamectl set-hostname node01

4. 设置时区和时钟同步

华为云服务器本身设置了时区和时钟同步,不需要额外设置

详细设置可以参考之前的实验

1
# timedatectl set-timezone Asia/Shanghai

5. 优化系统关闭不需要的服务

1
2
3
4
5
6
7
8
9
10
# vi /etc/sysctl.conf
fs.file-max = 20480000
fs.nr_open= 10240000
# sysctl -p
# vi /etc/security/limits.conf
root soft nofile 102400
root hard nofile 104800
* soft nofile 102400
* hard nofile 104800
# reboot

操作截图 7

1
2
3
4
5
# systemctl set-default multi-user.target
# systemctl disable cloud-config.service cloud-final.service cloud-init-local.service cloud-init.service cloud-init-hotplugd.socket cloud-config.target cloud-init.target multipathd.service multipathd.socket iscsid.socket apparmor.service systemd-resolved.service ufw.service fwupd.service graphical.target cron
# rm /etc/resolv.conf
# vi /etc/resolv.conf
nameserver 202.201.0.132

最后升级一下系统

1
# apt update && apt upgrade -y

二、环境搭建过程

1. 在虚拟机上创建新用户

1
2
3
4
5
6
7
8
9
10
# adduser hadoop
# sudo su - hadoop
hadoop@node01:~$
$ ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
$ chmod 0600 ~/.ssh/authorized_keys
$ vi ~/.ssh/config
Host *
StrictHostKeyChecking no
$ chmod 600 ~/.ssh/config

操作截图 8

2. 安装JDK8并配置java环境

在hadoop用户下完成jdk8的安装配置:

1
2
3
$ wget http://bigdata.cg.lzu.edu.cn/bigdata_software/jdk-8u401-linux-x64.tar.gz
$ tar -zxvf jdk-8u401-linux-x64.tar.gz
$ ./jdk1.8.0_401/bin/java -version

操作截图 9

在.bashrc文件中添加jdk8环境变量

1
2
3
4
5
6
$ vi .bashrc
export JAVA_HOME=/home/hadoop/jdk1.8.0_401
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar
$ source .bashrc
$ java -version

操作截图 10

因为我们只有一个节点node01,所以到这里java环境就配置完毕了,不需要将jdk8包和修改后的环境变量scp到其他节点。

3. 安装Apache Hadoop 3.3.6并验证HDFS、YARN、MapReduce服务

下载hadoop-3.3.6二进制包,解压

1
2
$ wget http://bigdata.cg.lzu.edu.cn/bigdata_software/hadoop-3.3.6.tar.gz
$ tar -zxvf hadoop-3.3.6.tar.gz

设置haodoop环境变量

1
2
3
4
5
6
7
8
9
10
11
$ vi ~/.bashrc
export HADOOP_HOME=/home/hadoop/hadoop-3.3.6
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
export HADOOP_MAPRED_HOME=$HADOOP_HOME
export HDFS_NAMENODE_USER=hadoop
export HDFS_DATANODE_USER=hadoop
export HDFS_SECONDARYNAMENODE_USER=hadoop
export YARN_RESOURCEMANAGER_USER=hadoop
export YARN_NODEMANAGER_USER=hadoop
$ source ~/.bashrc
$ hadoop version

操作截图 11

修改hadoop配置文件:

1. etc/hadoop/hadoop-env.sh

1
2
3
4
5
$ cd $HADOOP_HOME
$ vi etc/hadoop/hadoop-env.sh
export JAVA_HOME=/home/hadoop/jdk1.8.0_401
export HADOOP_HOME=/home/hadoop/hadoop-3.3.6
export HADOOP_CONF_DIR=${HADOOP_HOME}/etc/hadoop

2. etc/hadoop/core-site.xml:

1
2
3
4
5
6
7
8
9
10
11
$ vi etc/hadoop/core-site.xml
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://node01:9000</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/home/hadoop/hadoop-3.3.6/tmp</value>
</property>
</configuration>

创建tmp目录

1
$ mkdir /home/hadoop/hadoop-3.3.6/tmp

3. etc/hadoop/hdfs-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ vi etc/hadoop/hdfs-site.xml
<configuration>
<property>
<name>dfs.namenode.http-address</name>
<value>node01:9870</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>/home/hadoop/hadoop-3.3.6/dfs/name</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>/home/hadoop/hadoop-3.3.6/dfs/data</value>
</property>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<property>
<name>dfs.permissions.enabled</name>
<value>false</value>
</property>
</configuration>

4. etc/hadoop/mapred-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ vi etc/hadoop/mapred-site.xml
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.application.classpath</name>
<value>$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/*:$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/lib/*</value>
</property>
<property>
<name>mapreduce.admin.user.env</name>
<value>HADOOP_MAPRED_HOME=/home/hadoop/hadoop-3.3.6</value>
</property>
<property>
<name>yarn.app.mapreduce.am.env</name>
<value>HADOOP_MAPRED_HOME=/home/hadoop/hadoop-3.3.6</value>
</property>
</configuration>

5. etc/hadoop/yarn-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ vi etc/hadoop/yarn-site.xml
<configuration>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.aux-services.mapreduce_shuffle.class</name>
<value>org.apache.hadoop.mapred.ShuffleHandler</value>
</property>
<property>
<name>yarn.resourcemanager.hostname</name>
<value>node01</value>
</property>
<property>
<name>yarn.nodemanager.env-whitelist</name>
<value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME</value>
</property>
<property>
<name>yarn.nodemanager.vmem-check-enabled</name>
<value>false</value>
</property>
</configuration>

6. etc/hadoop/workers

1
2
$ vi etc/hadoop/workers
node01

操作截图 12

初始文件系统

1
$  hdfs namenode -format

启动分布式文件系统hdfs服务:

1
$  start-dfs.sh

第一次启动会创建数据目录和日志目录

查看进程:

1
$ jps

启动资源管理服务yarn:

1
2
start-yarn.sh
$ jps

操作截图 13

上述资源启动均成功

资源管理默认web页面为http://node01:8088/

因为这里使用的是云服务,因此这里地址为http://121.36.29.177:8088/

操作截图 14

node01交互界面: http://121.36.29.177:9870/

操作截图 15

4. 学习使用HDFS

1
2
$ hdfs dfs -mkdir /user
$ hdfs dfs -mkdir /user/hadoop

创建input测试目录,上传测试文件:

1
2
3
4
5
$ hdfs dfs -mkdir input
$ cd $HADOOP_HOME
hadoop@node01:~/hadoop-3.3.6$ hdfs dfs -put etc/hadoop/*.xml input
hadoop@node01:~/hadoop-3.3.6$ hdfs dfs -ls
hadoop@node01:~/hadoop-3.3.6$ hdfs dfs -ls input

操作截图 16

关于hdfs dfs命令可以参考帮助

1
$ hdfs dfs -h

下面给出hdfs的基本命令以及解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
hdfs dfs -ls:列出 HDFS 中的文件和目录。
hdfs dfs -put <local_path> <hdfs_path>:将本地文件上传到 HDFS 中。
hdfs dfs -get <hdfs_path> <local_path>:从 HDFS 中下载文件到本地文件系统。
hdfs dfs -mkdir <hdfs_path>:在 HDFS 中创建一个新目录。
hdfs dfs -copyFromLocal <local_path> <hdfs_path>:从本地文件系统复制文件到 HDFS 中。
hdfs dfs -copyToLocal <hdfs_path> <local_path>:从 HDFS 复制文件到本地文件系统。
hdfs dfs -mv <source_path> <destination_path>:移动文件或目录到新的位置。
hdfs dfs -chown <owner> <hdfs_path>:更改文件或目录的所有者。
hdfs dfs -chgrp <group> <hdfs_path>:更改文件或目录的所属组。
hdfs dfs -chmod <permissions> <hdfs_path>:更改文件或目录的权限。
hdfs dfs -appendToFile <local_path> <hdfs_path>:将本地文件内容追加到 HDFS 文件中。
hdfs dfs -rm <hdfs_path>:删除 HDFS 中的文件。
hdfs dfs -rmdir <hdfs_path>:删除 HDFS 中的空目录。
hdfs dfs -du <hdfs_path>:显示指定目录的大小。
hdfs dfs -expunge:清空 HDFS 中的垃圾文件。

运行例子

1
2
$ cd $HADOOP_HOME
hadoop@node01:~/hadoop-3.3.6$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar grep input output 'dfs[a-z.]+'

查看输出:

1
2
$ bin/hdfs dfs -cat output/*
$ hdfs dfs -cat output/*

操作截图 17

运行pi例子:

1
2
$ cd $HADOOP_HOME/share/hadoop/mapreduce
$ hadoop jar hadoop-mapreduce-examples-3.3.6.jar pi 10 20

操作截图 18

最后,可以运行以下命令停止服务:

1
2
$ stop-yarn.sh
$ stop-dfs.sh

5. 完成WordCount examples

1. Create java sourcecode file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ vi WordCount.java
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);

1
2
3
4
5
6
7
8
9
}
}
}
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();

1
}

result.set(sum);

context.write(key, result);

1
2
3
4
5
}
}
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);

1
2
3
4
5
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}

2. Compile WordCount.java and create a jar:

1
2
3
$ export HADOOP_CLASSPATH=$JAVA_HOME/lib/tools.jar
$ hadoop com.sun.tools.javac.Main WordCount.java
$ jar cf wc.jar WordCount*.class

操作截图 19

3. Create text file and upload it to hdfs

1
2
3
4
5
6
7
8
9
$ vi file1
Hello World Bye World
Hello Hadoop Bye Hadoop
Bye Hadoop Hello Hadoop
$ hdfs dfs -rm -r input
$ hdfs dfs -rm -r output
$ hdfs dfs -mkdir input
$ hdfs dfs -put file1 input
$ hdfs dfs -ls input

操作截图 20

4. Run the application:

1
2
3
$ hadoop jar wc.jar WordCount /user/hadoop/input/file1 /user/hadoop/output/
$ hdfs dfs -ls output
$ hdfs dfs -cat output/part-r-00000

操作截图 21

6. 启动spark并且验证spark交互界面

安装scala语言支持

1
$ sudo apt update && sudo apt install scala -y

下载解压spark-3.4.2

1
2
$ wget http://bigdata.cg.lzu.edu.cn/bigdata_software/spark-3.4.2-bin-hadoop3.tgz
$ tar -zxvf spark-3.4.2-bin-hadoop3.tgz

添加环境变量

1
$ vi .bashrc

#spark env config

1
2
3
4
export SPARK_HOME=~/spark-3.4.2-bin-hadoop3
export PATH=$PATH:$SPARK_HOME/bin
export LD_LIBRARY_PATH=$HADOOP_HOME/lib/native/:$LD_LIBRARY_PATH
$ source .bashrc

配置

1
2
$ cd $SPARK_HOME/conf
$ cp workers.template workers

添加工作节点(单节点只添加本机)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ vi workers
node01
$ cp spark-env.sh.template spark-env.sh
$ vi spark-env.sh
export JAVA_HOME=/home/hadoop/jdk1.8.0_401
export HADOOP_HOME=/home/hadoop/hadoop-3.3.6
export HADOOP_CONF_DIR=/home/hadoop/hadoop-3.3.6/etc/hadoop/
export SPARK_MASTER_HOST=node01
export SPARK_PID_DIR=/home/hadoop/spark-3.4.2-bin-hadoop3/data
export SPARK_LOCAL_DIR=/home/hadoop/spark-3.4.2-bin-hadoop3
export SPARK_EXECUTOR_MEMORY=512M
export SPARK_WORKER_MEMORY=2G
export SCALA_HOME=/usr/share/scala
$ cp spark-defaults.conf.template spark-defaults.conf
$ vi spark-defaults.conf
spark.master spark://node01:7077

这里不需要拷贝到其他节点,因为我使用的是单机伪集群。

操作截图 22

启动服务

1
2
$ $SPARK_HOME/sbin/start-all.sh
$ jps

操作截图 23

访问Web查看

http://node01:8080/

这里对应为:http://121.36.29.177:8080/

可以看到服务均成功启动。

操作截图 24

运行一个例子

1
$ $SPARK_HOME/bin/run-example SparkPi 10

操作截图 25

测试spark-shell

1
2
3
4
$ spark-shell
scala> val textFile=sc.textFile("file:///home/hadoop/spark-3.4.2-bin-hadoop3/README.md")
scala> textFile.count()
scala> :quit

操作截图 26

测试pyspark

1
2
3
4
5
$ pyspark
>>> lines=sc.textFile("file:///home/hadoop/spark-3.4.2-bin-hadoop3/README.md")
>>> lines.count()
125
>>> exit()

操作截图 27

7. 完成spark-submit exercise

1
2
3
4
5
6
7
8
9
10
$ wget http://bigdata.cg.lzu.edu.cn/bigdata_software/spark_examples/try1.txt
$ vi try1.py
from pyspark import SparkConf,SparkContext
conf = SparkConf().setMaster("spark://node01:7077").setAppName("My try1")
sc = SparkContext(conf=conf)
sc.setLogLevel('WARN')
txt = sc.textFile("try1.txt")
print(txt.count())
as_lines = txt.filter(lambda line: 'as' in line.lower())
print(as_lines.count())

将文件上传到hdfs

1
$ hdfs dfs -put try1.txt

提交任务

1
$ spark-submit  try1.py

操作截图 28

使用jupyter notebook

1
2
3
4
5
6
7
8
9
$ sudo apt update && sudo apt install pip
$ sudo pip3 install jupyter
$ sudo pip3 install pyspark
$ vi ~/.bashrc
export PYTHONPATH=$PATH:$SPARK_HOME/python
export PYSPARK_PYTHON=python3
$ source ~/.bashrc
$ jupyter notebook --ip=node01
$ $SPARK_HOME/sbin/stop-all.sh

操作截图 29

至此,Hadoop、Spark、spark-submit、PySpark 和 Jupyter Notebook 的基本测试都已经跑通。

三、一些记录

这次搭建主要覆盖了以下几个方面:

(1)Linux 用户和运行环境管理:创建独立的 hadoop 用户,配置 SSH 免密、JDK 和环境变量,保证 Hadoop/Spark 不依赖 root 用户运行。

(2)Hadoop 基础组件:安装并配置 HDFS、YARN 和 MapReduce,理解 NameNode、DataNode、ResourceManager、NodeManager 等组件的基本关系。

(3)HDFS 与 MapReduce:通过 HDFS 命令完成文件上传、下载、浏览和删除,并用 WordCount 理解 MapReduce 作业的提交和输出流程。

(4)Spark 生态:启动 Spark Standalone,使用 spark-shellpysparkspark-submit 验证交互式计算与作业提交。