上个月接到一个内部任务:在一批物理机上,把 Zookeeper、Hadoop、Spark、Kafka、Hive、Flume、MySQL 这套大数据全家桶完整搭起来,操作系统是 openEuler 24.03 LTS SP2。说实话,网上能搜到的整合教程大多建立在 CentOS 7 或 Ubuntu 上,openEuler 的资料比较零散,照着改又容易踩到系统差异的坑。我自己的打法是先跑通一条全链路数据流,再谈性能调优,这篇文章就是在这个前提下整理出来的实战记录,适合正在研究 openEuler 上部署大数据组件、或者想快速验证整套数据链路的同学直接参考。
标题里写了“粗略版”,我解释一下这个定位:不是偷工减料,而是把优先级摆正。第一目标是让 Zookeeper 集群能选出 Leader,HDFS 能正常读写,Spark 能提交任务,Kafka 能收发消息,Flume 能把日志灌进 Kafka,Hive 能把元数据落到 MySQL。至于 HA 双 NameNode、Kerberos 安全认证、多租户资源池这些生产级能力,是在链路通了之后才有意义的事。先把地基夯实,后面再加高大上的东西都不会慌。
1. 集群拓扑与版本选型:先把边界框定
1.1 三节点起步的规划
我这边用的是三台物理机,8 核 16G 内存、500G 数据盘,这里直接给出节点规划表。三台机器看起来性能不夸张,但跑通整套验证链路绰绰有余。为什么是三台而不是两台?核心原因是 Zookeeper 集群最少三台才能形成过半选举机制,HDFS 的副本数也方便设成 3,测试环境基本不用再改配置。
| 节点名 | IP | 操作系统 | 核心角色 |
|---|---|---|---|
| node1 | 192.168.10.11 | openEuler 24.03 LTS SP2 | Zookeeper、HDFS NameNode、YARN ResourceManager、Spark、Hive、Flume、MySQL、Kafka |
| node2 | 192.168.10.12 | openEuler 24.03 LTS SP2 | Zookeeper、HDFS DataNode、YARN NodeManager、Kafka |
| node3 | 192.168.10.13 | openEuler 24.03 LTS SP2 | Zookeeper、HDFS DataNode、YARN NodeManager、Kafka |
这套拓扑里 node1 承担的角色偏重,但粗略验证环境下没问题。如果你的机器资源允许,建议把 MySQL 和 Flume 挪到独立节点,否则后面做压力测试时 node1 会先吃满资源。
1.2 版本选型:为什么是这些数字
大数据组件最忌讳“全家桶最新版”,相互之间的兼容性要求非常苛刻。我这里选型不是拍脑袋,是逐对核过兼容性再定的,最终组合如下:
| 组件 | 版本 | 关键兼容说明 |
|---|---|---|
| JDK | Temurin 8(OpenJDK 8) | Hadoop 3.3、Spark 3.3、Kafka 3.6、Hive 3.1.3 都能正常跑 JDK 8 |
| Zookeeper | 3.8.3 | 原生支持 JDK 8,和 Hadoop 3.3 配合稳定 |
| Hadoop | 3.3.6 | 支持 Zookeeper 3.8 系列,HDFS+YARN 一体 |
| Spark | 3.3.4 | 依赖 Hadoop 3.x 客户端,兼容 Hive 3.1 metastore |
| Kafka | kafka_2.13-3.6.2 | 使用 Zookeeper 元数据模式,不启用 KRaft |
| Hive | 3.1.3 | 与 Hadoop 3.3 的 RPC 协议兼容 |
| Flume | 1.9.0 | 自带 Kafka Sink,可直连 Kafka 3.6 |
| MySQL | 8.0.x Community | 承担 Hive metastore 元数据库 |
这里多说一句 Kafka 的选型。Kafka 3.6 虽然已经支持 KRaft 模式、可以不依赖 Zookeeper,但如果你需要和其他大数据组件共存,Zookeeper 本来就是必须存在的基础设施,没必要在这个时候把 Kafka 切成 KRaft,徒增排查复杂度。我实际用下来,Zookeeper 模式在中小集群里足够稳定,改造成本也低。
2. 节点系统配置的细节:不处理好这些,后面每一步都可能报错
2.1 hosts 映射与 SSH 免密
大数据集群内部通信完全依赖主机名解析,这一步不做,后面 HDFS 和 YARN 都会出现诡异节点连不上现象。我在三台机器上统一写入/etc/hosts:
192.168.10.11 node1 192.168.10.12 node2 192.168.10.13 node3这里有个 openEuler 容易被忽略的细节:系统自带的/etc/hosts会带一条127.0.1.1 主机名的映射,如果你没删掉,部分组件解析localhost或本机 hostname 时会走 IPv6 或者回环地址,导致端口绑定失败。建议直接注释掉127.0.1.1那行。然后把每台机器 hostname 设置好:
hostnamectl set-hostname node1 hostnamectl set-hostname node2 hostnamectl set-hostname node3接着配置免密登录。三台机器都执行:
ssh-keygen -t rsa -b 2048 -N "" -f ~/.ssh/id_rsa在 node1 上把公钥分发过去:
ssh-copy-id node1 ssh-copy-id node2 ssh-copy-id node3如果 openEuler 没有预装ssh-copy-id,手动追加也很快:
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys免密配好之后,第一件事是验证三台机器之间能互相 ping 通主机名,再执行ssh node2确认不需要密码。绝大多数组件部署失败,最后都是栽在这种基础环节上。
2.2 JDK 8 的选择与安装
openEuler 24.03 默认工具链带的可能是 OpenJDK 17 或者 21,直接用它跑 Hadoop 3.3 大概率会报Unsupported major.minor version 52.0这类 Class 版本错误。稳妥做法是统一安装 JDK 8,我用的是 Temurin 8,解压版对系统侵入最小,方便在多台机器间复制。
将jdk8u422的 tar 包解压到/opt/java/jdk8后,统一配置环境变量:
cat >> /etc/profile.d/bigdata.sh <<EOF export JAVA_HOME=/opt/java/jdk8 export PATH=\$JAVA_HOME/bin:\$PATH EOF source /etc/profile.d/bigdata.sh java -version三台机器都要执行这一步,并且必须确认输出里是1.8.0_xxx,不是 17 或 21。我在实际部署时吃到过亏:node2 上忘记改 JAVA_HOME,结果 Hadoop 启动日志一直在报Java HotSpot(TM) 64-Bit Server VM warning,排查半天才发现是 JDK 版本不一致。
2.3 防火墙、时间同步与文件句柄
openEuler 默认可能开启 firewalld,大数据组件端口非常多,粗略环境建议直接关闭,等生产化时再按最小权限原则放行:
systemctl stop firewalld systemctl disable firewalld还有 SELinux,openEuler 默认可能是 Enforcing,会拦截 HDFS DataNode 的某些文件访问,我可以负责任地说,跑大数据组件时直接 setenforce 0 能省下大量时间:
setenforce 0 sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config时间同步这里容易被人忽略,但 Zookeeper 和 HDFS 对时钟漂移非常敏感。集群里如果某台机器慢了几十秒,会出现大量connection loss和Session expired报错。我是用 chrony 做同步,所有节点指向同一台时间服务器,配置好后执行chronyc sources确认同步状态。
最后调大文件句柄和线程数限制,编辑/etc/security/limits.conf:
* soft nofile 65535 * hard nofile 65535 * soft nproc 4096 * hard nproc 4096同时把系统vm.swappiness降到 10,减少大数据组件的内存换页:
sysctl -w vm.swappiness=10 echo "vm.swappiness=10" >> /etc/sysctl.conf3. Zookeeper 集群:先把整个集群的“命脉”立起来
3.1 安装与核心配置
Zookeeper 解压到/opt/zookeeper/apache-zookeeper-3.8.3-bin,然后设置软链:ln -s /opt/zookeeper/apache-zookeeper-3.8.3-bin /opt/zookeeper/current。这样后续升级版本时不需要改一堆配置路径。三台机器统一目录结构,后面写脚本批量操作也方便。
修改/opt/zookeeper/current/conf/zoo.cfg:
tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper/data clientPort=2181 server.1=node1:2888:3888 server.2=node2:2888:3888 server.3=node3:2888:38882888是 Leader 和 Follower 之间的数据同步端口,3888是选举投票端口,2181是客户端连接端口。这三个端口在后续排障时非常关键,你用netstat -tlnp看到监听端口的时候要能对应上。
数据目录要单独创建并写入 myid 文件,这个文件的内容就是服务器编号,和server.x对应:
mkdir -p /data/zookeeper/data echo 1 > /data/zookeeper/data/myid # node1 上执行 echo 2 > /data/zookeeper/data/myid # node2 上执行 echo 3 > /data/zookeeper/data/myid # node3 上执行3.2 启动顺序与验证方法
启动时有一个新手特别容易慌的现象:先在 node1 上执行zkServer.sh start会立刻报错,原因是它连接不上 node2 和 node3。这个不用担心,Zookeeper 集群就是这么个启动顺序,只要把三台机器陆续启动起来,最终会自动完成选举并收敛到正常状态。
启动完成后依次验证:
zkServer.sh status三台机器上应该能看到一台显示Leader,另外两台显示Follower。如果全部显示Error contacting service,先用zkServer.sh start-foreground看具体日志,最常见原因就是防火墙没关、myid写错、或者/etc/hosts映射有问题。
再用客户端创建测试节点验证读写能力:
zkCli.sh -server node1:2181 create /bigdata-test testdata get /bigdata-test能返回testdata,说明 Zookeeper 这块已经可以放心交给上层组件使用了。
4. Hadoop HDFS + YARN:分布式存储与调度
4.1 目录规划
Hadoop 解压到/opt/hadoop/hadoop-3.3.6,做软链/opt/hadoop/current。数据目录我单独放在/data/hadoop下,没有用默认的/tmp。这块不是矫情:默认配置里hadoop.tmp.dir会指向/tmp,系统一清理目录,NameNode 的元数据就没了,等于集群重新来过,而且/tmp目录的权限有时候也会影响 DataNode 的写入。数据盘单独挂载到/data之后再做 Hadoop 安装,是我自己的习惯。
在hadoop-env.sh中设置 JDK 路径:
echo "export JAVA_HOME=/opt/java/jdk8" >> /opt/hadoop/current/etc/hadoop/hadoop-env.sh还要确认export HADOOP_HOME已写入系统环境变量,否则后续 Spark、Hive 启动时找不到 Hadoop 客户端。
4.2 三个核心配置文件的修改
先说core-site.xml:
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://node1:8020</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/data/hadoop/tmp</value> </property> </configuration>fs.defaultFS是客户端访问 HDFS 时拿到的默认文件系统地址,8020是 NameNode 的 RPC 通信端口。没有它,所有依赖 HDFS 的组件都不知道要连哪台机器。
再看hdfs-site.xml:
<configuration> <property> <name>dfs.namenode.name.dir</name> <value>/data/hadoop/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/data/hadoop/datanode</value> </property> <property> <name>dfs.replication</name> <value>3</value> </property> <property> <name>dfs.namenode.http-address</name> <value>node1:9870</value> </property> </configuration>dfs.replication设成 3,刚好匹配三台 DataNode。9870是 NameNode 的 Web UI 端口,浏览器访问http://node1:9870就能看到集群状态。
然后是yarn-site.xml:
<configuration> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.resourcemanager.hostname</name> <value>node1</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>8192</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>8192</value> </property> <property> <name>yarn.nodemanager.vmem-check-enabled</name> <value>false</value> </property> </configuration>最后一项vmem-check-enabled一定要设成 false,这是 Spark 任务在 YARN 上反复失败的经典坑。YARN 默认会检查容器虚拟内存使用量,超过比例就会直接 Kill 掉对应的 Executor,中文报错通常是 “Container killed” 后面跟着虚拟内存超限的提示。
4.3 格式化与启动
格式化 NameNode 只在一个节点执行,这一步会清空 NameNode 元数据目录,确定是全新环境后再操作:
hdfs namenode -format看到Storage directory /data/hadoop/namenode has been successfully formatted就算成功。然后启动:
start-dfs.sh start-yarn.sh用jps检查进程:
- node1 上应有
NameNode、ResourceManager、SecondaryNameNode - node2、node3 上应有
DataNode、NodeManager
再验证 HDFS 基本读写:
hdfs dfs -mkdir -p /data/test echo "hello hadoop" > /tmp/test.txt hdfs dfs -put /tmp/test.txt /data/test/ hdfs dfs -cat /data/test/test.txt能读到hello hadoop,HDFS 主链路就通了。入口不着急优化,先把这一步跑直。
5. Spark 集群:跑在 YARN 上还是自己玩?
5.1 选型逻辑
有人会把 Spark 配成 Standalone 模式,另外起 Master 和 Worker 进程。但既然 Hadoop 的 YARN 已经在统一管资源,再搞一套 Spark 自带资源调度,完全就是给自己增加运维负担。我从一开始就决定 Spark 跑在 YARN 上,提交任务时指定--master yarn,让 YARN 统一分配 CPU 和内存。集群里同时跑多个 Spark 任务,或者需要和 MapReduce 任务共享资源池的时候,YARN 的调度优势会非常明显。
5.2 配置要点
Spark 解压到/opt/spark/spark-3.3.4,同样做软链/opt/spark/current。spark-env.sh里至少要写三行:
export JAVA_HOME=/opt/java/jdk8 export HADOOP_CONF_DIR=/opt/hadoop/current/etc/hadoop export SPARK_HOME=/opt/spark/currentHADOOP_CONF_DIR指向 Hadoop 配置目录后,Spark 在提交任务时会自动读取core-site.xml、hdfs-site.xml,因此客户端才能正确解析 HDFS 地址。
然后编辑spark-defaults.conf:
spark.master yarn spark.yarn.jars hdfs://node1:8020/spark-jars/*.jar spark.driver.memory 2g spark.executor.memory 2g spark.executor.cores 2spark.yarn.jars这一项经常有人忘写,Spark 默认会在每个任务启动时把本地 Spark 的 jar 包传到 YARN 的临时目录,速度慢还容易失败。我单独把 Spark 的 jar 包扔到 HDFS 上,路径是/spark-jars:
hdfs dfs -mkdir /spark-jars hdfs dfs -put /opt/spark/current/jars/* /spark-jars/5.3 提交验证
验证 Spark 最简单的方式是用自带的示例程序:
spark-submit --class org.apache.spark.examples.SparkPi \ --master yarn \ --deploy-mode client \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.3.4.jar 100任务跑到最后会出现Pi is roughly 3.1xxxx。如果一直卡在Submitting application to YARN,优先检查spark.yarn.jars的路径是否能通过hdfs dfs -ls访问。如果 Executor 反复被杀,回到yarn-site.xml检查刚才埋的vmem-check-enabled配置。
6. Kafka 集群:消息总线接入
6.1 关键配置项
前面选型时已经说了不启用 KRaft,Kafka 3.6.2 继续走 Zookeeper 模式。解压到/opt/kafka/kafka_2.13-3.6.2,做软链/opt/kafka/current。修改config/server.properties,最核心的几个参数:
broker.id=1 listeners=PLAINTEXT://node1:9092 log.dirs=/data/kafka-logs zookeeper.connect=node1:2181,node2:2181,node3:2181 num.partitions=3 default.replication.factor=2 offsets.topic.replication.factor=2 transaction.state.log.replication.factor=2 transaction.state.log.min.isr=2broker.id在集群里必须唯一,node1、node2、node3 分别设成 1、2、3。log.dirs指的是消息数据目录,单独放在数据盘上。
num.partitions和default.replication.factor是测试环境里比较省事的默认值,主题创建不指定分区数时就用它。副本因子设成 2 而不是 3,是为了在某个 broker 宕机时还能正常选举 Leader;测试环境三节点全量副本反而会拖慢写入速度。
6.2 启动与验证
启动命令:
kafka-server-start.sh -daemon /opt/kafka/current/config/server.properties创建测试主题并验证:
kafka-topics.sh --bootstrap-server node1:9092 \ --create --topic test-topic --partitions 3 --replication-factor 2这里如果设置--replication-factor 3且只有两个 broker 承载副本,会直接报错Replication factor: 3 larger than available brokers: 2,属于最常见的主题创建失败原因。主题创建成功后,开两个终端分别执行:
kafka-console-producer.sh --bootstrap-server node1:9092 --topic test-topic kafka-console-consumer.sh --bootstrap-server node1:9092 --topic test-topic --from-beginningproducer 终端输入一串字符,consumer 终端能看到同样内容,消息链路就通了。我在 node1 上同时起了 producer 和 consumer 做验证,随后在 node2 上再跑一个 consumer,确认集群跨节点通信正常。
7. Hive 数仓与 MySQL 元数据存储
7.1 MySQL 初始化
MySQL 在这个架构里不是被业务读写的数据源,而是 Hive metastore 的元数据库,表结构、分区信息、序列化器等全存在 MySQL 中。MySQL 8.0 安装完毕并启动服务后,执行初始化:
CREATE DATABASE hive CHARACTER SET utf8mb4; CREATE USER 'hive'@'%' IDENTIFIED BY 'hive123'; GRANT ALL PRIVILEGES ON hive.* TO 'hive'@'%'; FLUSH PRIVILEGES;hive用户授权了%远程访问,是因为 Hive metastore 服务可能运行在 node1,但 Spark、Flume 等组件也可能需要连接元数据,统一走远程权限更省事。如果在 node2 上跑 Hive client 时碰到Access denied,优先看 MySQL 的用户授权匹配情况。
7.2 hive-site.xml 关键配置
Hive 解压到/opt/hive/apache-hive-3.1.3,做软链/opt/hive/current。修改$HIVE_HOME/conf/hive-site.xml(这个文件默认不存在,从hive-default.xml.template复制后,建议把模板里大量用不到的配置删掉只留核心项,否则日志排障时非常痛苦):
<configuration> <property> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:mysql://node1:3306/hive</value> </property> <property> <name>javax.jdo.option.ConnectionDriverName</name> <value>com.mysql.cj.jdbc.Driver</value> </property> <property> <name>javax.jdo.option.ConnectionUserName</name> <value>hive</value> </property> <property> <name>javax.jdo.option.ConnectionPassword</name> <value>hive123</value> </property> <property> <name>hive.metastore.warehouse.dir</name> <value>/user/hive/warehouse</value> </property> <property> <name>hive.metastore.uris</name> <value>thrift://node1:9083</value> </property> <property> <name>hive.server2.thrift.bind.host</name> <value>node1</value> </property> <property> <name>hive.server2.thrift.port</name> <value>10000</value> </property> </configuration>hive.metastore.uris决定了 HiveServer2 和 SparkSQL 连接元数据的方式,默认是嵌入式 Derby 模式,本地启动没问题但跨节点查询就会崩。搞成 Thrift 协议传输后就稳定多了。
7.3 guava 冲突:必踩的经典坑
Hive 3.1.3 自带的 guava 是 19.0,Hadoop 3.3.6 自带的是 27.0-jre。不做替换的话,Hive 启动或建表时经常会报NoSuchMethodError或ClassNotFoundException,而且报错位置很随机,有时候在启动 metastore 时,有时候在客户端执行 SQL 时。处理方式很简单:
rm /opt/hive/current/lib/guava-19.0.jar cp /opt/hadoop/current/share/hadoop/common/lib/guava-27.0-jre.jar /opt/hive/current/lib/然后把 MySQL JDBC 驱动拷进 Hive lib:
cp /opt/mysql-connector-j-8.0.33.jar /opt/hive/current/lib/这一步属于讲了无数遍、但每次新环境都会再踩一遍的经典操作。
7.4 初始化 schema 与启动验证
首次执行:
$HIVE_HOME/bin/schematool -initSchema -dbType mysql看到Initialization script completed说明元数据表建好了。如果第二次执行报 schema 已存在,记得先删掉 MySQL 里hive库再重新初始化,不要反复对同一个库跑 init。
启动 metastore 和 hiveserver2:
nohup hive --service metastore > /data/hive/logs/metastore.log 2>&1 & nohup hive --service hiveserver2 > /data/hive/logs/hiveserver2.log 2>&1 &用 beeline 验证:
beeline -u jdbc:hive2://node1:10000 -n hive进入 beeline 后执行一条建表语句:
CREATE TABLE test_orders (id INT, amount DOUBLE); INSERT INTO test_orders VALUES (1, 99.9); SELECT * FROM test_orders;能正常返回结果,Hive 到 MySQL 的链路就完整了。
8. Flume 日志采集接入 Kafka
8.1 Flume 在链路中的分工
Flume 在这个项目里承担的是日志采集器的角色:监控日志目录、读取新增内容、把数据推送到 Kafka 的指定 Topic。相比让业务系统直接写 Kafka,好处是采集端天然带断点续传、背压控制,而且配置完 source 后不需要动业务代码就能接入。链路结构就是Flume (taildir source) -> Kafka sink -> Kafka topic。
8.2 配置示例
Flume 解压到/opt/flume/apache-flume-1.9.0,软链/opt/flume/current。写一个kafka-sink.conf:
a1.sources = r1 a1.channels = c1 a1.sinks = k1 a1.sources.r1.type = taildir a1.sources.r1.positionFile = /data/flume/taildir_position.json a1.sources.r1.filegroups = logs a1.sources.r1.filegroups.logs = /data/logs/.*log a1.sources.r1.fileHeader = true a1.channels.c1.type = memory a1.channels.c1.capacity = 1000 a1.channels.c1.transactionCapacity = 100 a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink a1.sinks.k1.kafka.bootstrap.servers = node1:9092,node2:9092,node3:9092 a1.sinks.k1.kafka.topic = test-topic a1.sinks.k1.flumeBatchSize = 100 a1.sources.r1.channels = c1 a1.sinks.k1.channel = c1用taildir而不是简单 exec source,是因为它可以在 Flume 重启后通过 positionFile 记录上次读取位置,防止日志重复采集或漏采。positionFile目录要先建好并保证 Flume 运行用户有写权限。
Flume 1.9.0 自带的 kafka-clients 版本偏旧,可能出现 sink 上报时的兼容性异常。建议把/opt/kafka/current/libs/kafka-clients-3.6.2.jar直接替换到 Flume 的 lib 下,并把其他旧版本 kafka-clients 移除。
启动 Flume:
/opt/flume/current/bin/flume-ng agent \ --conf /opt/flume/current/conf \ --conf-file /opt/flume/current/conf/kafka-sink.conf \ --name a1 -Dflume.root.logger=INFO,console &8.3 验证 Flume 到 Kafka
往/data/logs/下追加一行内容:
mkdir -p /data/logs echo "{\"id\":1,\"msg\":\"flume test\"}" >> /data/logs/test.log在另一个终端消费 Kafka topic:
kafka-console-consumer.sh --bootstrap-server node1:9092 --topic test-topic --from-beginning能看到那行 JSON 就是整条链路通了。如果 Kafka sink 报TimeoutException,最常都是bootstrap.servers写成了localhost,或者 Kafka 服务本身没起全。
9. 全链路验证与运维心得
9.1 一条数据完整走通
所有组件就绪后,我很推荐做一次“从日志文件到数仓表”的完整验证,把前面各自打通的环节串起来。大致流程:
- Flume 采集日志文件,写入 Kafka 的
test-topic - 写一个简单的 Spark Streaming 程序消费 Kafka topic,再把结果写入 HDFS 指定目录
- 在 Hive 中创建外部表,指向 HDFS 目录
- 用 SparkSQL 或 beeline 查询该表
这一步跑通后,离线数仓和实时链路就算有了雏形。如果只想粗略验证,也可以简化成“Flume 采集 -> Kafka 消费 -> Hive 手动 load 数据”,重点是看清各个组件间的数据格式流转。
9.2 排障对照表
我把这次部署过程中最常见的问题整理成一张表,希望能帮你少走弯路:
| 现象 | 原因 | 处理 |
|---|---|---|
zkServer.sh status报 Error contacting service | 防火墙未关、myid 不一致、hosts 映射错误 | 逐台检查 2181/2888/3888 端口监听情况 |
| DataNode 启动但集群中看不到 | NameNode 格式化后 data 目录残留上次集群 ID | 清理干净元数据后重新初始化 |
| Spark 任务 Executor 被 Kill | YARN 虚拟内存检查误判 | yarn-site.xml将vmem-check-enabled设为 false |
| beeline 连接失败 | metastore 未启动、端口未放行 | 检查 9083 和 10000 端口 |
| Hive 建表报 NoSuchMethodError | guava 版本冲突 | 按第 7.3 节替换 jar |
| Kafka 创建主题报 replication 过大 | 副本数超过可用 broker 数 | 调整--replication-factor |
| Flume 上报 Kafka 超时 | bootstrap 写错或 kafka-clients 版本太旧 | 修正配置并替换客户端 jar |
9.3 几点个人体会
整套环境搭下来,我的最大感受是:大数据集群搭建的难点从来不在某个组件的“启动成功”,而在组件之间的配置如何对齐。比如HADOOP_CONF_DIR不配,Spark 就找不到 HDFS 地址;hive.metastore.uris不配,SparkSQL 就只会在本地起一个嵌入式 metastore,数据完全和 Hive 表对不上;Kafka 客户端 jar 版本不统一,Flume 端就可能出现诡异超时。
另外,openEuler 24.03 作为新系统,大部分软件包管理和 systemd 行为和主流 Linux 发行版一致,但真要省事,我的建议是:把每一个组件的数据目录单独规划出来,放数据盘,不要挤在/tmp或根分区;日志全部重定向到独立目录,避免前台进程把终端吞掉;每次修改配置文件之后,先grep -v '^\s*#'过一遍确认没有空属性和重复键。
这套粗略版搭建,目的就是先把主干链路打通,数据能往前跑。后面要加认证、加权限、加监控、加高可用,都是在已有骨架上做增量的工作。项目再复杂,第一原则永远是——数据能流起来,才谈得上优化。