讲真每年都会被各种大数据项目劝退不少人而劝退理由多数不是算法难、不是业务复杂而是卡在环境搭建这一步。你要装Zookeeper、Hadoop、Spark、Kafka、Hive、Flume、MySQL这一整套全家桶随便哪个组件的版本冲突、配置文件报错、端口被占、内存不够都能耗掉你一整天。这套组合是当前离线数仓和实时数据处理的经典底座Flume负责采集日志Kafka做消息缓冲Hadoop HDFS负责落地存储Spark做分布式计算Hive把结构化数据管理起来MySQL给Hive当元数据库Zookeeper则为整个集群提供分布式协调服务。听起来繁琐但当你在openEuler 24.03 LTS SP2上把整条链路全部跑通一次后面再遇到CDH、Ambari、K8s里的任何大数据组件都不会再发怵。这篇文章是我在虚拟机里完整搭建、反复重启测试后整理出来的实操版。标题里写明是“粗略版”意思是不做HA高可用、不做Kerberos安全认证、不做性能调优只追求一个目标用最少的工作量把这条经典大数据链路从零到一跑起来供学习、毕设、实验环境使用。点到为止细节到位照着抄就行。1. 架构设计与版本选型1.1 先搞清楚每个组件是干什么的很多人一上来就开装装完才发现不知道自己在装什么。别急先用一张表把组件角色理清楚组件核心作用依赖关系常用端口Zookeeper分布式协调、Leader选举、元数据管理无2181、2888、3888Hadoop HDFS分布式文件存储无NameNode可用ZK做HA粗略版不用9870、8020Hadoop YARN资源调度与任务管理HDFS8088、8030/31/32Spark内存计算引擎跑离线批处理/实时流HDFS、YARN4040、18080Kafka分布式消息队列数据缓冲和分发Zookeeper3.x仍可走ZK模式9092Hive数据仓库工具把SQL变成MapReduce/Spark作业HDFS、MySQL9083、10000Flume日志采集与传输把数据送进Kafka/HDFS无根据Source自定义MySQLHive元数据存储、业务库无3306这一整套的数据流动逻辑大概是这样的Flume监听日志文件变化把数据推给Kafka下游Spark或消费者从Kafka拉数据做清洗计算清洗结果落到HDFSHive建外表映射HDFS上的结构化数据供统计分析MySQL只负责保存Hive的表结构、分区、字段等元数据不存业务数据本体。理解了这个链路后面不管哪个环节出问题你都能顺着这条链路去排查而不是瞎猜。1.2 版本搭配与节点规划openEuler 24.03 LTS SP2是当前较新的长期支持版本用dnf管理软件包整体体验和CentOS/RHEL系列很像但有些默认配置不太一样后面我会专门提。这里给出我实测不冲突的一套版本组合组件版本选型理由JDK1.8.0_311Hadoop/Hive/Zookeeper对JDK8兼容性最稳Hadoop3.3.6稳定社区资料多跑通率高Zookeeper3.8.1稳定自带的AdminServer方便查看状态Spark3.4.1配Hadoop 3.x自带内置Hadoop客户端Kafka3.4.0兼容ZK模式3.4仍支持Hive3.1.3对Hadoop 3.x支持完善Flume1.11.0老牌稳定版MySQL8.0.xdnf仓库版openEuler仓库直接有省事节点规划我建议至少三台机器你可以用VMware/VirtualBox开三台虚拟机每台至少4GB内存、2核CPU这里建议你尽量给到8GB因为后面YARN和Spark吃内存挺猛的。粗略版的角色分配如下主机名IP规划示例部署组件node01192.168.1.10NameNode、ResourceManager、Zookeeper、Kafka、Hive、Flume、MySQLnode02192.168.1.11DataNode、NodeManager、Zookeeper、Kafkanode03192.168.1.12DataNode、NodeManager、Zookeeper、Kafka有人会问三台机器的ZK加Kafka会不会太挤粗略版其实不用太担心只要内存够服务之间端口独立就能跑得动。真正难受的是你只有一台机器还要硬起全套那个才叫折磨。2. openEuler系统基础准备最容易翻车的一步2.1 openEuler网络配置与主机名设置很多人在这一步就卡住了因为openEuler默认用NetworkManager管理网络和传统CentOS 7的直接改ifcfg文件有一点点区别。装好系统之后第一件事就是配静态IP不然后面节点间通信全是坑。先看网卡名用nmcli查看当前连接nmcli connection show比如你看到的网卡连接名是ens160那就可以直接这样配置# 配置静态IP按你的实际网络段调整 nmcli connection mod ens160 ipv4.method manual \ ipv4.addresses 192.168.1.10/24 \ ipv4.gateway 192.168.1.1 \ ipv4.dns 192.168.1.1 # 重启网络连接生效 nmcli connection up ens160配完确认一下ip addr show ens160能看到你刚设置的IP就代表成功了。再检查一个关键点openEuler默认防火墙是开启状态三台机器互相访问服务会被拦下来。为了方便实验我直接关掉systemctl disable --now firewalld setenforce 0注意setenforce 0只对当前会话生效临时关闭SELinux。要永久关闭就改/etc/selinux/config把SELINUXenforcing改成SELINUXdisabled。生产环境不建议这么干但实验环境这么处理能省掉大量权限类报错。接着配主机名和hosts。三台机器分别执行hostnamectl set-hostname node01 # node02/node03 相应修改然后每台机器的/etc/hosts都写入这样三行192.168.1.10 node01 192.168.1.11 node02 192.168.1.12 node03提示不要用带下划线的主机名Hadoop系列组件对主机名校验比较严格老老实实用字母加数字。2.2 JDK安装与SSH免密登录Hadoop生态基本都跑在Java上JDK装不好后面全是ClassNotFoundException。openEuler仓库自带openjdk但我更推荐用Oracle JDK 8的tar包稳定、以后切CDH之类的环境也顺手。在node01上操作mkdir -p /opt/tools cd /opt/tools tar -zxf jdk-8u311-linux-x64.tar.gz -C /opt/ mv /opt/jdk1.8.0_311 /opt/jdk8写环境变量打开/etc/profile.d/java.sh加入export JAVA_HOME/opt/jdk8 export PATH$JAVA_HOME/bin:$PATH export CLASSPATH.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar然后source /etc/profile.d/java.sh用java -version验证。三台机器都要装建议直接scp分发。SSH免密是Hadoop集群的刚需因为start-dfs.sh要靠SSH远程启动所有节点的进程。在node01上执行ssh-keygen -t rsa -P -f ~/.ssh/id_rsa ssh-copy-id node01 ssh-copy-id node02 ssh-copy-id node03三台机器的root用户之间都做一遍互信主节点到自己也要免密避免启动时报Permission denied (publickey)。3. Zookeeper集群搭建3.1 三节点ZooKeeper配置复盘Zookeeper是整个集群的“神经中枢”Kafka的broker注册、HDFS的NameNode高可用、HBase的RegionServer协调全都依赖它。虽然粗略版里它的存在感不强但没有它Kafka根本起不来。三台机器都装好Zookeeper之后进入conf目录把zoo_sample.cfg复制成zoo.cfgcp zoo_sample.cfg zoo.cfg然后重点修改下面这些项tickTime2000 initLimit10 syncLimit5 dataDir/opt/zookeeper/data clientPort2181 server.1node01:2888:3888 server.2node02:2888:3888 server.3node03:2888:3888每台机器上还要在dataDir指定的目录里创建一个myid文件内容写各自的编号比如node01写1node02写2node03写3mkdir -p /opt/zookeeper/data echo 1 /opt/zookeeper/data/myid # node01 echo 2 /opt/zookeeper/data/myid # node02 echo 3 /opt/zookeeper/data/myid # node03依次启动三台的ZK/opt/zookeeper/bin/zkServer.sh start启动后一定要看状态/opt/zookeeper/bin/zkServer.sh status正常会输出leader或follower。如果输出没有角色多半是myid写错、dataDir权限不对或者2888/3888端口被防火墙拦了。3.2 为什么必须得是奇数台很多人不理解为什么Zookeeper至少三台不能两台。这就要说它的Leader选举机制了。ZK的写入操作必须超过半数节点同意才能提交三节点集群挂掉一台剩余两台超过半数2/3仍然可用如果只有两节点挂一台就只剩1/2不满足“超过半数”整个集群就瘫了。这也是为什么粗略版我建议三节点而不是两台。另外如果你只是在本机做单机测试也可以只用一台ZK把server.1那行配置去掉即可但不建议因为后面Kafka如果连单机ZKbroker多了也容易出问题。Zookeeper本身没有太多调优空间实验环境最常踩的坑是clientPort被占用、dataDir下没有myid、以及三台机器系统时间不一致导致选举异常。建议在每台机器上装个NTP同步时间命令是dnf install -y chrony然后systemctl enable --now chronyd。4. Hadoop分布式集群4.1 五个配置文件的逻辑拆解Hadoop是这套生态中安装配置量最大的组件核心是五个文件core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml、workers。不要背配置要理解每个参数为什么会出现在这里。先看core-site.xml它决定了整个集群的“入口地址”和临时目录configuration property namefs.defaultFS/name valuehdfs://node01:8020/value /property property namehadoop.tmp.dir/name value/opt/hadoop/tmp/value /property /configurationfs.defaultFS就是告诉所有客户端你的HDFS入口在node01的8020端口这个值一旦确定就别频繁改否则格式化后的NameNode路径全得重建。再看hdfs-site.xml主要管副本数和NameNode存储路径configuration property namedfs.replication/name value2/value /property property namedfs.namenode.name.dir/name value/opt/hadoop/tmp/dfs/name/value /property property namedfs.datanode.data.dir/name value/opt/hadoop/tmp/dfs/data/value /property /configuration复制因子设成2就够了三台机器一共两个DataNode理论上两份副本能容忍挂一台数据不丢实验环境没必要设3。yarn-site.xml里最关键的ResourceManager地址和内存参数configuration property nameyarn.resourcemanager.hostname/name valuenode01/value /property property nameyarn.nodemanager.resource.memory-mb/name value4096/value /property property nameyarn.scheduler.maximum-allocation-mb/name value2048/value /property /configurationmemory-mb要写你NodeManager所在机器实际可用的内存。如果你虚拟机只有4G内存这里还写4096的话系统会直接OOM。粗略版建议每个NodeManager分2G就够NodeManager本身、DataNode、ZK、Kafka还要吃内存。4.2 格式化、启动与验证注意事项配置文件同步到三台机器之后第一次启动前必须格式化NameNodehdfs namenode -format这个命令会在node01生成HDFS的初始元数据。格式化一次之后除非你确定要推倒重建否则不要随便再执行因为它会重新生成clusterID而DataNode那边还保留着旧的clusterID两个ID对不上DataNode进程起来后会自动退出。启动顺序也有讲究start-dfs.sh start-yarn.sh然后等半分钟用jps检查进程。node01上应该能看到NameNode和ResourceManagernode02和node03上应该各有一个DataNode和NodeManager。如果发现DataNode没起来先看日志路径在$HADOOP_HOME/logs/hadoop-root-datanode-主机名.log最常见的错误就是clusterID不一致解决办法是把每台机器上hadoop.tmp.dir指定的目录全部删掉回到node01重新格式化再启动。验证HDFS是否可用可以在node01上执行hdfs dfs -mkdir /test hdfs dfs -put /etc/hostname /test/能正常写入就说明HDFS没问题。再看YARN的资源管理页面浏览器打开http://node01:8088能看到Active Nodes数量是2就代表YARN也活了。5. Spark集群配置与使用5.1 Spark on YARN部署方式Spark本身不负责存储也不负责资源调度它只是一个计算引擎。粗略版里我建议用“Spark on YARN”模式让YARN统一管理内存和CPUSpark只提交作业这样资源不会乱。解压Spark之后进入conf目录cp spark-env.sh.template spark-env.sh cp spark-defaults.conf.template spark-defaults.confspark-env.sh里写这几个关键项export JAVA_HOME/opt/jdk8 export SPARK_DIST_CLASSPATH$(hadoop classpath) export HADOOP_CONF_DIR/opt/hadoop/etc/hadoop export SPARK_HISTORY_OPTS-Dspark.history.fs.logDirectoryhdfs://node01:8020/spark-logsSPARK_DIST_CLASSPATH这行尤其重要不加的话Spark提交作业时会报NoClassDefFoundError因为它找不到HDFS和YARN的客户端库。在spark-defaults.conf里补上spark.masteryarn spark.eventLog.enabledtrue spark.eventLog.dirhdfs://node01:8020/spark-logs spark.history.fs.logDirectoryhdfs://node01:8020/spark-logs先在HDFS上建好日志目录hdfs dfs -mkdir -p /spark-logs。5.2 Spark启停与提交作业验证启动历史服务器$SPARK_HOME/sbin/start-history-server.sh然后用Spark自带的示例作业验证集群是否可用$SPARK_HOME/bin/spark-submit \ --class org.apache.spark.examples.SparkPi \ --master yarn \ --deploy-mode client \ --driver-memory 512m \ --executor-memory 512m \ --executor-cores 1 \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.4.1.jar 10跑完会看到Pi的估算值。这里注意内存尽量给小一点因为实验环境总共就那么多内存给多了会卡在YARN等待资源。如果你想直接上手写代码用pyspark或者spark-shell最直观。粗略版里可以先跑一条SQL感受一下spark-shell \ --master yarn \ --deploy-mode client在shell里执行sc.parallelize(1 to 10).sum()能算出55就说明Spark计算链路是通的。6. MySQL安装与Hive数仓初始化6.1 openEuler上装MySQL 8.0openEuler 24.03仓库里已经带了MySQL 8.0可以直接用dnf装dnf install -y mysql-server systemctl enable --now mysqld初始化并设置root密码mysql_secure_installation这里有个坑openEuler上MySQL初始化完默认root用户是auth_socket插件认证用密码登录会被拒。需要手动改成caching_sha2_password。进入MySQL命令行后执行ALTER USER rootlocalhost IDENTIFIED WITH caching_sha2_password BY 你的密码; FLUSH PRIVILEGES;接下来给Hive创建一个专用的元数据库和账号CREATE DATABASE hive_meta CHARACTER SET utf8mb4; CREATE USER hive% IDENTIFIED BY Hive123456; GRANT ALL PRIVILEGES ON hive_meta.* TO hive%; FLUSH PRIVILEGES;6.2 Hive的hive-site.xml配置细节解压Hive后配置conf/hive-site.xml重点内容如下configuration property namejavax.jdo.option.ConnectionURL/name valuejdbc:mysql://node01:3306/hive_meta?useSSLfalseamp;characterEncodingutf8/value /property property namejavax.jdo.option.ConnectionDriverName/name valuecom.mysql.cj.jdbc.Driver/value /property property namejavax.jdo.option.ConnectionUserName/name valuehive/value /property property namejavax.jdo.option.ConnectionPassword/name valueHive123456/value /property property namehive.metastore.uris/name valuethrift://node01:9083/value /property property namehive.server2.thrift.bind.host/name valuenode01/value /property /configurationjavax.jdo.option.ConnectionURL就是想尽办法让Hive找到MySQL里的hive_meta库。第一次初始化元数据时执行$HIVE_HOME/bin/schematool -initSchema -dbType mysql -verbosity如果提示Metastore schema version is not supported大概率是Hive版本和元数据版本不匹配或者是MySQL驱动版本没放到$HIVE_HOME/lib下。把mysql-connector-java的jar丢进lib目录再跑一次。启动Metastore和HiveServer2nohup $HIVE_HOME/bin/hive --service metastore /tmp/metastore.log 21 nohup $HIVE_HOME/bin/hive --service hiveserver2 /tmp/hiveserver2.log 21 用beeline连上去试一把$HIVE_HOME/bin/beeline -u jdbc:hive2://node01:10000 -n root能进到0: jdbc:hive2://node01:10000就说明Hive已经和MySQL玩到一块了。执行show databases;能看到default库。7. Kafka消息队列集群7.1 server.properties关键参数配置Kafka 3.4.0还兼容Zookeeper模式用它来复用前面搭好的ZK集群是再合适不过了。每台机器修改config/server.properties里以下参数broker.id0 # node01写0node02写1node03写2 listenersPLAINTEXT://node01:9092 advertised.listenersPLAINTEXT://node01:9092 log.dirs/opt/kafka/kafka-logs zookeeper.connectnode01:2181,node02:2181,node03:2181 offsets.topic.replication.factor2 transaction.state.log.replication.factor2 transaction.state.log.min.isr1advertised.listeners是个隐形坑。很多人在本机能连Kafka、跨节点就超时多半就是这项没写或者写成了localhost。消费者和生产者拿着Zookeeper里注册的地址去连broker如果你在这里写的是localhost别人自然连不上。启动Kafka之前建议先调一下堆内存默认1G对实验机器偏大export KAFKA_HEAP_OPTS-Xmx512m -Xms512m $KAFKA_HOME/bin/kafka-server-start.sh -daemon $KAFKA_HOME/config/server.properties用jps看进程确认三台机器各有一个Kafka。7.2 用命令行验证Topic生产消费创建一个测试Topic然后从控制台生产数据另一个终端消费数据# 创建topic kafka-topics.sh --bootstrap-server node01:9092 --create \ --topic test-topic --partitions 3 --replication-factor 2 # 生产端 kafka-console-producer.sh --bootstrap-server node01:9092 --topic test-topic # 消费端 kafka-console-consumer.sh --bootstrap-server node01:9092 --topic test-topic --from-beginning生产端随便输入几行文字消费端能同步显示这个Kafka集群就算是可用的。提示验证时如果报Connection to node -1 (node01/192.168.1.10:9092) failed第一反应不是去看网络而是去检查这台机器的/etc/hosts和Kafka的advertised.listeners。这个问题我见了太多次十有八九是hosts没配全。8. Flume日志采集接入Kafka8.1 配置一个taildir到Kafka的采集作业Flume的角色很简单就是“搬运工”。它能监听一个日志文件把新增的行实时推送到Kafka里。解压Flume后先修改conf/flume-env.shexport JAVA_HOME/opt/jdk8然后写一个采集配置文件我命名为tail-kafka.conf放到conf目录下a1.sources r1 a1.sinks k1 a1.channels c1 a1.sources.r1.type TAILDIR a1.sources.r1.positionFile /opt/flume/data/taildir_position.json a1.sources.r1.filegroups f1 a1.sources.r1.filegroups.f1 /opt/logs/.*log a1.sources.r1.fileHeader true a1.channels.c1.type memory a1.channels.c1.capacity 10000 a1.channels.c1.transactionCapacity 500 a1.sinks.k1.type org.apache.flume.sink.kafka.KafkaSink a1.sinks.k1.kafka.bootstrapServers node01:9092,node02:9092,node03:9092 a1.sinks.k1.kafka.topic flume-topic a1.sinks.k1.flumeBatchSize 500这段配置的逻辑是Flume用taildir监听/opt/logs目录下以.log结尾的文件每有新内容就写入内存channel再由KafkaSink批量发到Kafka的flume-topic主题。8.2 Flume启停与数据链路联调先创建好目录和测试文件mkdir -p /opt/logs echo hello flume kafka /opt/logs/app.log接着启动Flume$FLUME_HOME/bin/flume-ng agent \ --name a1 \ --conf $FLUME_HOME/conf \ --conf-file $FLUME_HOME/conf/tail-kafka.conf \ -Dflume.root.loggerINFO,console再看Kafka这边有没有收到数据kafka-topics.sh --bootstrap-server node01:9092 --create \ --topic flume-topic --partitions 3 --replication-factor 2 kafka-console-consumer.sh --bootstrap-server node01:9092 \ --topic flume-topic --from-beginning然后往/opt/logs/app.log里追加几行内容如果消费端能看到说明Flume到Kafka的链路打通了。至此你手上已经有了一条“日志文件 → Flume → Kafka”的实时采集通道后面接Spark Streaming还是写HDFS就是另一个故事了。9. 全链路联调与问题排查实录9.1 一条模拟日志从采集到落库的完整路径把前面所有组件串起来跑一遍是验证整套环境是否“真活”的终极办法。我在实验里是这么操作的先在Hive里建一张表映射到HDFS上的一个目录CREATE TABLE flume_logs ( line STRING ) ROW FORMAT DELIMITED FIELDS TERMINATED BY \t STORED AS TEXTFILE LOCATION /data/flume_logs;然后写一个Spark作业定期从Kafka消费数据写入HDFS路径/data/flume_logsimport org.apache.spark.sql.SparkSession val spark SparkSession.builder() .appName(KafkaToHDFS) .getOrCreate() val df spark.readStream .format(kafka) .option(kafka.bootstrap.servers, node01:9092,node02:9092,node03:9092) .option(subscribe, flume-topic) .load() .selectExpr(CAST(value AS STRING) as line) df.writeStream .format(csv) .option(path, /data/flume_logs) .option(checkpointLocation, /data/checkpoint) .start() .awaitTermination()提交这个作业后再往/opt/logs/app.log追加数据整条链路就是日志文件 → Flume → Kafka → Spark Streaming → HDFS → Hive表。最后在Hive里查SELECT * FROM flume_logs;能看到新增的数据就说明整套集群完全跑通了。9.2 高频问题排查速查表这套环境搭建周期内我踩过不少坑也帮朋友解决过不少问题整理成一张表直接收藏现象大概率原因排查思路与解法start-dfs.sh时SSH报Permission deniedSSH免密失效或hosts不对检查/etc/ssh/sshd_config的PermitRootLogin重跑ssh-copy-idDataNode反复启动即退出NameNode多次格式化导致clusterID不一致删掉所有节点的hadoop.tmp.dir数据目录重新格式化YARN Web UI看不到Active NodeNodeManager没起来或内存配置超了看NodeManager日志调低yarn.nodemanager.resource.memory-mbHive初始化报schema版本错误MySQL驱动缺失或Hive版本不匹配确认mysql-connector-java.jar在$HIVE_HOME/lib下重新initSchemaKafka生产端Connection failedadvertised.listeners配错或hosts缺失改成实际IP或主机名确认三台hosts一致Flume启动后Kafka没数据taildir监控路径不对或topic不存在确认文件路径匹配正则先手动创建好目标topicSpark提交后一直ACCEPTEDYARN资源不够调小driver/executor内存或关掉部分非必要服务释放内存ZK status无leader/followermyid写错或时间不同步检查每台myid文件所有机器同步chrony时间我给这套集群做过一次极端测试用Shell循环快速往日志里写入了一万行数据Flume的channel容量设成10000结果整个链路没有丢一条数据。原因就在于Flume的批量提交和Kafka的批量接收都是顺序写吞吐量远大于日志产生速度。但一旦遇到业务瞬时写入量暴增Flume的memory channel会自动积压最终Kafka那边延迟变大。这时候优先调大transactionCapacity而不是盲目增加并发Source后者只会让Flume进程内存暴涨。另外提醒一下整个集群都跑起来之后每台机器的内存占用是很可观的。我的虚拟机三台各8G内存跑全套之后剩2G左右可用。如果你只有4G内存的机器建议把YARN的NodeManager内存降到1GKafka的堆内存降到256MSpark作业的内存也相应减半。这套东西的优雅之处在于它跑通之后你自然就理解每个参数为什么存在而不是像我当年一样看到几百行配置就头皮发麻。根据我个人经验这套环境搭建一次后面再去看那些动辄几十台节点的生产集群心里会非常有底。接下来你要往深了走可以考虑给NameNode加ZK做HA、给ResourceManager加ZK做自动切换、用Kerberos做认证、再或者用Ambari或者新版的Cloudera Manager做界面化管理这些都是在这套底座之上的扩展。但那是后话眼下先把这条链路跑通数据能从日志文件走到Hive表里你的分布式入门就算正式过关了。