1. 项目概述:为什么从“伪分布式”开始学Hadoop,而不是一上来就搭集群?
你点开这个标题,大概率不是为了收藏吃灰,而是真打算在本地或测试环境里把Hadoop跑起来——不是看文档截图,不是跑个Hello World,是实打实让NameNode启动、DataNode注册成功、能用hdfs dfs -ls /看到目录、能提交一个WordCount任务跑出结果。我带过几十个刚接触大数据的开发者和运维同学,发现一个高频痛点:90%的人卡在第一步——连伪分布式都起不来,更别说完全分布式了。他们照着官网文档改配置,core-site.xml写对了,hdfs-site.xml配错了端口,yarn-site.xml漏了ResourceManager地址,Java环境变量PATH里混进了旧版本JDK,甚至SSH免密登录连本机都失败……最后在日志里翻三天,还是报Connection refused。
这根本不是“不会”,而是Hadoop的部署逻辑和传统Web服务完全不同:它不是单进程启动,而是一组强依赖、有状态、需网络协同的守护进程;它不认“配置文件存在”,只认“配置项语义正确+服务间通信可达+权限与路径无冲突”。所以本篇不讲概念定义,不列官网参数表,而是按一个真实运维工程师的实操动线来组织:从零准备环境,到伪分布式验证核心组件闭环,再到完全分布式打通多节点协作,最后落地YARN资源调度——每一步都告诉你为什么这么配、不这么配会报什么错、日志里哪一行是关键线索、重启前必须检查的三个状态。关键词“hadoop大数据”“伪分布式部署”“完全分布式部署”“资源管理器部署”不是标签,而是你接下来要亲手敲进终端的四个阶段里程碑。适合两类人:一是刚转岗做大数据运维的Linux系统管理员,二是需要搭建测试平台的开发/算法同学——你们不需要成为Hadoop内核专家,但必须能在2小时内让集群健康运行,并准确判断是配置问题、网络问题还是权限问题。
2. 环境准备与底层依赖:别让JDK和SSH成为第一道墙
2.1 JDK版本选择与隔离部署:为什么OpenJDK 11是当前最稳的选择?
Hadoop 3.x官方明确要求JDK 8或JDK 11,但实际踩坑发现:JDK 8u292之后的某些安全补丁会导致Kerberos认证模块异常;而JDK 17虽已支持,但社区主流发行版(如Cloudera CDP、Hortonworks HDP)尚未全面适配,部分第三方工具链(如老版本Sqoop)会抛UnsupportedClassVersionError。我们实测对比了OpenJDK 8u362、11.0.22、17.0.10三版本在CentOS 7.9上的表现,结论很明确:OpenJDK 11.0.22是当前生产环境兼容性、稳定性、安全性的黄金平衡点。
安装时务必避免系统默认JDK干扰。某次给某高校实验室部署时,他们服务器预装了Oracle JDK 1.8.0_144,PATH指向/usr/java/jdk1.8.0_144/bin,而Hadoop启动脚本中JAVA_HOME被硬编码为$JAVA_HOME,导致namenode进程启动后立即OOM——因为Oracle JDK的GC策略与Hadoop堆内存分配不匹配。解决方案是彻底隔离:
# 卸载系统自带JDK(谨慎操作,确认无其他服务依赖) sudo yum remove java-1.8.0-openjdk* java-11-openjdk* -y # 下载OpenJDK 11.0.22二进制包(官方LTS版本) wget https://download.java.net/java/GA/jdk11/28/GPL/openjdk-11.0.22_linux-x64_bin.tar.gz tar -xzf openjdk-11.0.22_linux-x64_bin.tar.gz -C /opt/ # 创建软链接,便于后续升级 sudo ln -sf /opt/jdk-11.0.22 /opt/java11 # 配置全局环境变量(仅对root及hadoop用户生效) echo 'export JAVA_HOME=/opt/java11' | sudo tee -a /etc/profile.d/java.sh echo 'export PATH=$JAVA_HOME/bin:$PATH' | sudo tee -a /etc/profile.d/java.sh source /etc/profile.d/java.sh # 验证:必须显示11.0.22,且java -version与javac -version一致 java -version # 输出应为:openjdk version "11.0.22" 2024-01-16提示:不要用yum install java-11-openjdk,因为RPM包会自动注册alternatives,可能与其他Java应用冲突。手动解压+软链接方式完全可控,且Hadoop启动脚本中的JAVA_HOME可直接指向/opt/java11。
2.2 SSH免密登录:为什么必须配置root用户而非hadoop用户?
很多教程强调“创建hadoop用户并配置其SSH免密”,这在完全分布式中是标准做法,但在伪分布式阶段,强烈建议先用root用户完成首次验证。原因有三:一是Hadoop启动脚本(如start-dfs.sh)默认以当前用户身份执行,若用普通用户,需反复处理/data目录权限(chown -R hadoop:hadoop /usr/local/hadoop/data);二是某些发行版SELinux策略会阻止非root用户绑定1000以下端口(如NameNode默认50070),导致WebUI无法访问;三是调试阶段需直接查看/var/log目录下系统级日志,root权限省去大量sudo切换。
配置步骤极简,但细节决定成败:
# 生成root用户的RSA密钥对(不设密码,一路回车) ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa # 将公钥追加到本机authorized_keys(注意是>>,不是>,避免覆盖) cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys # 设置正确权限(SSH严格校验,权限不对则免密失效) chmod 0600 ~/.ssh/authorized_keys chmod 0700 ~/.ssh # 测试:必须返回"Welcome to CentOS"之类提示,且无密码输入 ssh localhost ssh 127.0.0.1注意:如果ssh localhost提示"Permission denied (publickey)",请检查sshd_config是否启用PubkeyAuthentication yes,并重启服务:systemctl restart sshd。某次在阿里云ECS上部署,发现默认sshd_config中PasswordAuthentication yes被注释,导致免密配置无效,浪费2小时排查。
2.3 系统参数调优:为什么swapiness必须设为1,而不是0?
Hadoop是内存密集型应用,NameNode需常驻内存维护整个文件系统的元数据树,DataNode需缓存块报告(BlockReport)。若系统swap分区活跃,内核会将不常访问的页换出到磁盘,当NameNode突然触发Full GC时,大量对象被回收,内存压力陡增,swap频繁读写导致GC停顿时间飙升至数分钟——集群直接“假死”。
但将vm.swappiness设为0并非最优解。Linux内核文档明确指出:swappiness=0并不完全禁用swap,只是极大降低倾向;当内存严重不足时,OOM Killer仍可能被触发。而swappiness=1是经过大规模集群验证的平衡值:既保证绝大多数情况下不使用swap,又保留极端场景下的内存保护机制。
# 临时生效 sudo sysctl vm.swappiness=1 # 永久生效(写入sysctl.conf) echo 'vm.swappiness=1' | sudo tee -a /etc/sysctl.conf sudo sysctl -p # 验证 cat /proc/sys/vm/swappiness # 应输出1此外,还需调整两个关键参数:
vm.overcommit_memory=1:允许内核在内存分配时“乐观估计”,避免Hadoop进程因内存预留失败而启动中断;net.core.somaxconn=65535:增大TCP连接队列,应对DataNode与NameNode间高频心跳(默认128,集群规模稍大即丢包)。
3. 伪分布式部署:用单机模拟集群,验证四大核心组件闭环
3.1 Hadoop解压与目录结构规划:为什么不能直接解压到/home/hadoop?
Hadoop安装路径直接影响后续维护成本。曾见某金融公司把hadoop-3.3.6解压到/home/hadoop/hadoop,结果半年后磁盘告警——/home分区只有20GB,而HDFS数据目录(默认在${hadoop.tmp.dir}/dfs)随业务增长迅速占满。正确做法是将Hadoop主程序与数据目录物理分离:
- 主程序路径:
/usr/local/hadoop(符号链接指向具体版本,如/usr/local/hadoop -> /usr/local/hadoop-3.3.6) - 数据目录:
/data/hadoop/dfs(独立挂载的大容量磁盘) - 日志目录:
/var/log/hadoop(便于logrotate统一管理)
# 创建标准目录结构 sudo mkdir -p /usr/local/hadoop /data/hadoop/dfs /var/log/hadoop sudo chown -R $USER:$USER /usr/local/hadoop /data/hadoop /var/log/hadoop # 下载并解压(以3.3.6为例,LTS稳定版) wget https://downloads.apache.org/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz tar -xzf hadoop-3.3.6.tar.gz -C /usr/local/ sudo ln -sf /usr/local/hadoop-3.3.6 /usr/local/hadoop # 配置环境变量(~/.bashrc) echo 'export HADOOP_HOME=/usr/local/hadoop' >> ~/.bashrc echo 'export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop' >> ~/.bashrc echo 'export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin' >> ~/.bashrc source ~/.bashrc3.2 四大核心配置文件详解:每个参数背后的物理意义
伪分布式本质是让所有Hadoop守护进程(NameNode, DataNode, ResourceManager, NodeManager)在同一台机器上运行,但彼此通过网络端口通信。因此配置文件的核心是显式声明“本机即集群”,而非默认的localhost环回。
core-site.xml:定义HDFS的统一入口
<configuration> <!-- fs.defaultFS是HDFS客户端的默认URI,必须指向NameNode的RPC地址 --> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> <!-- 关键!不能写127.0.0.1,某些Java DNS解析有差异 --> </property> <!-- hadoop.tmp.dir是Hadoop临时目录,影响dfs.namenode.name.dir等默认路径 --> <property> <name>hadoop.tmp.dir</name> <value>/data/hadoop/tmp</value> <!-- 必须指向有写权限的独立磁盘 --> </property> </configuration>hdfs-site.xml:定义NameNode与DataNode的存储行为
<configuration> <!-- NameNode元数据存储路径,必须是绝对路径且有写权限 --> <property> <name>dfs.namenode.name.dir</name> <value>file:/data/hadoop/dfs/name</value> <!-- 多路径用逗号分隔,提升冗余 --> </property> <!-- DataNode数据块存储路径 --> <property> <name>dfs.datanode.data.dir</name> <value>file:/data/hadoop/dfs/data</value> </property> <!-- 关键:关闭HDFS权限检查,否则普通用户无法操作 --> <property> <name>dfs.permissions.enabled</name> <value>false</value> </property> </configuration>yarn-site.xml:定义资源调度框架
<configuration> <!-- ResourceManager的Web UI地址 --> <property> <name>yarn.resourcemanager.hostname</name> <value>localhost</value> <!-- 必须与core-site.xml中fs.defaultFS的host一致 --> </property> <!-- ResourceManager的IPC地址(ApplicationMaster向其申请资源) --> <property> <name>yarn.resourcemanager.address</name> <value>localhost:8032</value> </property> <!-- NodeManager的Web UI地址 --> <property> <name>yarn.nodemanager.hostname</name> <value>localhost</value> </property> <!-- 启用MapReduce Shuffle服务(MR任务必需) --> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> </configuration>mapred-site.xml:定义MapReduce计算框架
<configuration> <!-- 指定MapReduce运行在YARN上 --> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> <!-- MapReduce历史服务器地址(用于查看已完成作业) --> <property> <name>mapreduce.jobhistory.address</name> <value>localhost:10020</value> </property> </configuration>实操心得:配置文件修改后,必须删除旧的tmp目录再格式化NameNode,否则残留的VERSION文件会导致启动失败。命令顺序固定:
rm -rf /data/hadoop/tmp && hdfs namenode -format。某次忘记清空tmp,NameNode启动后立即退出,日志只有一行“ERROR namenode.NameNode: Failed to start namenode”,实际原因是/data/hadoop/tmp/dfs/name/current/VERSION中clusterID与新格式化不匹配。
3.3 启动与验证:如何用三条命令确认伪分布式真正就绪?
启动流程必须严格遵循依赖顺序:先HDFS,再YARN,最后历史服务器。
# 1. 格式化NameNode(仅首次执行) hdfs namenode -format # 2. 启动HDFS(自动启动NameNode和DataNode) start-dfs.sh # 3. 启动YARN(自动启动ResourceManager和NodeManager) start-yarn.sh # 4. 启动MapReduce历史服务器(可选,但推荐开启) mapred --daemon start historyserver验证是否成功,不能只看终端输出“starting...”,而要看三个维度:
进程维度:
jps命令必须显示全部5个进程jps # 正确输出应包含: # 12345 NameNode # 12346 DataNode # 12347 ResourceManager # 12348 NodeManager # 12349 JobHistoryServer端口维度:
netstat -tuln | grep -E '9000|50070|8088|10020'必须显示LISTEN9000:NameNode RPC端口(HDFS客户端通信)50070:NameNode WebUI(Hadoop 3.x默认为9870,需确认版本)8088:ResourceManager WebUI(YARN资源监控)10020:JobHistoryServer端口(作业历史查询)
功能维度:执行一个端到端操作
# 创建HDFS根目录 hdfs dfs -mkdir -p /user/$USER # 上传本地文件到HDFS echo "hello hadoop" > /tmp/input.txt hdfs dfs -put /tmp/input.txt /user/$USER/input # 运行内置WordCount示例(验证MR引擎) hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount /user/$USER/input /user/$USER/output # 查看输出结果 hdfs dfs -cat /user/$USER/output/part-r-00000 # 应输出:hello 1 # hadoop 1
常见问题速查表:
现象 可能原因 快速定位命令 jps看不到DataNode/data/hadoop/dfs/data目录权限错误或磁盘满ls -ld /data/hadoop/dfs/data && df -h /dataWebUI打不开(50070) NameNode未启动或防火墙拦截 curl -I http://localhost:50070WordCount报"Connection refused" fs.defaultFS地址写错或NameNode未监听telnet localhost 9000start-yarn.sh报"no resourcemanager to stop"YARN配置未生效,检查 yarn.resourcemanager.hostnamegrep -r "resourcemanager.hostname" $HADOOP_CONF_DIR
4. 完全分布式部署:从单机到多节点,解决网络与角色分离难题
4.1 节点规划与角色分配:为什么NameNode和SecondaryNameNode不能同机?
完全分布式意味着将Hadoop守护进程分布到多台物理/虚拟机上,核心挑战是角色隔离与网络可达性。典型三节点集群规划如下:
| 节点主机名 | IP地址 | 部署角色 | 关键说明 |
|---|---|---|---|
| master | 192.168.1.10 | NameNode, ResourceManager, SecondaryNameNode | 绝不与DataNode/NodeManager共存,避免内存争抢 |
| slave1 | 192.168.1.11 | DataNode, NodeManager | 存储与计算节点,可横向扩展 |
| slave2 | 192.168.1.12 | DataNode, NodeManager | 同上,形成最小高可用单元 |
为什么SecondaryNameNode不能和NameNode同机?因为它需要定期从NameNode拉取edits日志和fsimage镜像进行合并,生成新的checkpoint。若两者同机,当NameNode所在机器宕机,SecondaryNameNode也无法工作,checkpoint丢失,NameNode恢复时需重放全部edits,耗时可能达数小时。最佳实践是将其部署在独立节点(如master),或与ResourceManager共存(因两者负载特性互补)。
4.2 免密登录拓扑构建:如何用一条命令实现全节点互通?
伪分布式只需ssh localhost,完全分布式需确保任意节点都能无密码登录其他所有节点,包括反向登录(slave1→master)。手动配置效率低且易出错,采用批量脚本:
# 在master节点执行(假设节点列表存于hosts.txt) for node in $(cat hosts.txt); do echo "Configuring $node..." ssh-copy-id -i ~/.ssh/id_rsa.pub $node ssh $node "mkdir -p ~/.ssh && chmod 700 ~/.ssh" done # 验证全联通(在master上运行) for node in $(cat hosts.txt); do ssh $node "hostname; date" 2>/dev/null && echo "$node: OK" || echo "$node: FAIL" done注意:
ssh-copy-id会自动追加公钥到~/.ssh/authorized_keys,但需确保目标节点~/.ssh目录权限为700,否则SSH拒绝读取。某次在CentOS 7上,ssh-copy-id后slave1的.ssh目录权限为755,导致master无法免密登录slave1,日志报"Bad owner or permissions on ~/.ssh/config"。
4.3 配置文件集群化改造:从localhost到真实IP的映射逻辑
完全分布式配置的核心是将所有localhost替换为对应节点的真实主机名/IP,并确保DNS解析正常。修改前先统一各节点的/etc/hosts:
# 所有节点执行(master, slave1, slave2) echo "192.168.1.10 master" | sudo tee -a /etc/hosts echo "192.168.1.11 slave1" | sudo tee -a /etc/hosts echo "192.168.1.12 slave2" | sudo tee -a /etc/hosts然后修改配置文件(以hdfs-site.xml为例):
<configuration> <!-- NameNode只在master上运行,故地址为master --> <property> <name>dfs.namenode.http-address</name> <value>master:9870</value> <!-- Hadoop 3.x WebUI端口 --> </property> <!-- DataNode需向NameNode注册,地址必须是NameNode的RPC地址 --> <property> <name>dfs.namenode.rpc-address</name> <value>master:9000</value> </property> <!-- SecondaryNameNode部署在master,但需独立配置 --> <property> <name>dfs.namenode.secondary.http-address</name> <value>master:9868</value> </property> </configuration>最关键的workers文件(Hadoop 3.0+替代slaves文件)定义DataNode/NodeManager节点列表:
# $HADOOP_HOME/etc/hadoop/workers 文件内容(master节点上) slave1 slave2 # 注意:此处写主机名,不是IP,且不能有空格或注释实操技巧:配置完成后,用
scp批量分发到所有节点,但切勿覆盖目标节点的workers文件!因为每个节点的workers文件内容不同(master节点的workers是slave1/slave2,而slave1的workers应为空或仅自身)。正确做法是分别生成:# 在master上生成slave1的workers文件 echo "slave1" > workers_slave1 scp workers_slave1 slave1:$HADOOP_HOME/etc/hadoop/workers # 在master上生成slave2的workers文件 echo "slave2" > workers_slave2 scp workers_slave2 slave2:$HADOOP_HOME/etc/hadoop/workers
4.4 分布式启动与状态诊断:如何一眼看出哪个节点掉线?
启动命令与伪分布式相同,但执行位置和预期结果不同:
# 在master节点执行(所有操作必须在master发起) start-dfs.sh # 自动在master启NameNode,在slave1/slave2启DataNode start-yarn.sh # 自动在master启ResourceManager,在slave1/slave2启NodeManager验证状态不再是jps,而是HDFS WebUI和YARN WebUI:
- 访问
http://master:9870→ 点击"Utilities" → "Browse the file system",应看到/目录; - 点击"Datanodes"标签页,应显示2个DataNode(slave1和slave2),状态为"Live";
- 访问
http://master:8088→ 点击"Nodes",应显示2个NodeManager,状态为"RUNNING"。
掉线诊断三板斧:
- 网络层:
ping slave1和telnet slave1 9000(测试NameNode RPC端口);- 进程层:
ssh slave1 "jps | grep DataNode",若无输出则DataNode未启动;- 日志层:
ssh slave1 "tail -50 $HADOOP_HOME/logs/hadoop-*-datanode-*.log",重点找Exception或Connection refused。
某次部署中,slave1的DataNode始终无法注册,日志显示java.net.ConnectException: Connection refused。排查发现slave1的/etc/hosts中master解析为127.0.0.1,导致DataNode尝试连接本机9000端口(无NameNode),而非master的9000端口。修正/etc/hosts后立即恢复。
5. 资源管理器部署深度解析:YARN不只是“启动就完事”
5.1 ResourceManager高可用(HA)原理:ZooKeeper如何避免单点故障?
伪分布式和基础完全分布式中,ResourceManager是单点——一旦master宕机,所有正在运行的MapReduce/YARN应用立即失败。生产环境必须启用YARN HA,其核心是用ZooKeeper协调多个ResourceManager实例的主备切换。
架构逻辑如下:
- 部署两个ResourceManager(rm1在master,rm2在备用节点backup);
- 两者同时向ZooKeeper注册临时节点(ephemeral znode);
- ZooKeeper通过watch机制检测rm1会话超时,自动将rm2提升为Active;
- 客户端(如yarn client)通过ZooKeeper获取当前Active RM地址。
配置要点(yarn-site.xml):
<configuration> <!-- 启用YARN HA --> <property> <name>yarn.resourcemanager.ha.enabled</name> <value>true</value> </property> <!-- 定义RM IDs --> <property> <name>yarn.resourcemanager.ha.rm-ids</name> <value>rm1,rm2</value> </property> <!-- 配置每个RM的地址 --> <property> <name>yarn.resourcemanager.hostname.rm1</name> <value>master</value> </property> <property> <name>yarn.resourcemanager.hostname.rm2</name> <value>backup</value> </property> <!-- ZooKeeper集群地址 --> <property> <name>yarn.resourcemanager.zk-address</name> <value>zk1:2181,zk2:2181,zk3:2181</value> </property> </configuration>注意:启用HA后,
start-yarn.sh不再适用,必须分别启动:# 在master启动rm1 yarn --daemon start resourcemanager # 在backup启动rm2 yarn --daemon start resourcemanager启动后,
yarn rmadmin -getServiceState rm1返回active或standby,实时反映状态。
5.2 NodeManager内存与CPU资源精细化控制:为什么mapreduce.map.memory.mb不能超过yarn.nodemanager.resource.memory-mb?
YARN的资源调度基于Container(容器),每个Container封装了内存、CPU、磁盘等资源。若配置不当,会出现“资源超卖”:NodeManager上报16GB内存,但用户提交的Map任务申请2GB内存×10个=20GB,YARN强行分配导致OOM。
关键参数关系:
yarn.nodemanager.resource.memory-mb:NodeManager总可用内存(如16384 MB)yarn.nodemanager.resource.cpu-vcores:NodeManager总可用vcore(如8)mapreduce.map.memory.mb:单个Map Task申请的内存(如2048 MB)mapreduce.reduce.memory.mb:单个Reduce Task申请的内存(如4096 MB)
约束公式:mapreduce.map.memory.mb ≤ yarn.nodemanager.resource.memory-mb / max_map_tasks
其中max_map_tasks = yarn.nodemanager.resource.memory-mb / mapreduce.map.memory.mb
例如:NodeManager内存16GB,Map Task申请2GB,则最多并发8个Map;若申请4GB,则最多4个。若强行设置mapreduce.map.memory.mb=8192,则单个NodeManager只能运行2个Map,资源利用率极低。
实操经验:某电商公司集群中,运维误将
mapreduce.map.memory.mb设为8GB,而NodeManager仅16GB,导致同一节点无法并行执行Map和Reduce任务(Reduce默认4GB),作业排队时间激增。调整为2GB后,单节点并发能力提升4倍,平均作业耗时下降60%。
5.3 ApplicationMaster容错机制:Container失败后如何自动重试?
YARN中,每个作业启动一个ApplicationMaster(AM),负责向ResourceManager申请资源、监控Task状态。若AM所在Container失败,YARN默认重试次数为2(yarn.resourcemanager.am.max-attempts),但每次重试需重新申请资源、重建状态,耗时长。
优化方案是启用AM重试的快速失败机制:当AM在指定时间内(如30秒)未向RM发送心跳,则立即判定失败并重试,避免长时间等待:
<property> <name>yarn.resourcemanager.am.max-attempts</name> <value>4</value> <!-- 提高重试上限 --> </property> <property> <name>yarn.resourcemanager.am.expiry-interval-ms</name> <value>30000</value> <!-- AM心跳超时设为30秒 --> </property>验证AM容错:手动kill掉AM进程(
yarn application -list | grep RUNNING | awk '{print $1}' | xargs yarn application -kill),观察YARN WebUI中Application状态是否从ACCEPTED→RUNNING→FAILED→ACCEPTED(重试),且作业最终成功。
6. 常见问题与排查技巧实录:那些文档里不会写的血泪教训
6.1 “NoRouteToHost”错误:防火墙不是唯一背锅侠
现象:start-dfs.sh后,slave节点DataNode日志报java.net.NoRouteToHost: No route to host,但ping master通,telnet master 9000也通。
真相:这是Linux内核的rp_filter(反向路径过滤)机制作祟。当slave1向master发送数据包时,master回复包的源IP是master的IP,但slave1的路由表认为该回复包应从另一张网卡进入(如eth0收,却从eth1回),触发rp_filter丢弃。
解决方案(所有节点执行):
# 临时关闭(验证用) sudo sysctl net.ipv4.conf.all.rp_filter=0 sudo sysctl net.ipv4.conf.eth0.rp_filter=0 # 替换为实际网卡名 # 永久关闭(写入sysctl.conf) echo 'net.ipv4.conf.all.rp_filter=0' | sudo tee -a /etc/sysctl.conf echo 'net.ipv4.conf.eth0.rp_filter=0' | sudo tee -a /etc/sysctl.conf sudo sysctl -p这个问题在云服务器(如AWS EC2、阿里云ECS)上高频出现,因为云平台默认启用严格反向路径检查。某次在AWS上部署,折腾两天才定位到此,文档从未提及。
6.2 “Safe mode is ON”:NameNode启动后为何拒绝一切写操作?
现象:HDFS WebUI显示Safe mode is ON,hdfs dfs -put报org.apache.hadoop.hdfs.server.namenode.SafeModeException。
原因:NameNode启动后进入安全模式,等待足够多的DataNode注册并报告块信息(达到阈值,默认99.9%)。若DataNode未注册或块报告延迟,安全模式会持续。
强制退出(仅测试环境):
hdfs dfsadmin -safemode leave但治本之策是检查:
- DataNode是否真的启动?
jps确认; - DataNode日志是否有
Registered to master:9000?没有则网络或配置错误; dfs.namenode.safemode.threshold-pct参数是否被误调为1.0(100%)?默认0.999。
经验:安全模式持续超过5分钟,90%是DataNode未连接。此时应立刻查
$HADOOP_HOME/logs/hadoop-*-datanode-*.log,而非盲目执行-safemode leave。
6.3 “Too many open files”:为什么Hadoop会耗尽系统文件描述符?
现象:DataNode运行几小时后突然退出,日志报java.io.IOException: Too many open files。
根源:Linux默认单进程文件描述符限制为1024,而DataNode需为每个块、每个客户端连接、每个日志文件打开句柄,轻松突破此限。
永久解决(所有节点):
# 修改系统级限制 echo '* soft nofile 65536' | sudo tee -a /etc/security/limits.conf echo '* hard nofile 65536' | sudo tee -a /etc/security/limits.conf echo 'root soft nofile 65536' | sudo tee -a /etc/security/limits.conf echo 'root hard nofile 65536' | sudo tee -a /etc/security/limits.conf # 重启sshd使limits生效 sudo systemctl restart sshd # 验证(新SSH会话中) ulimit -n # 应输出65536提示:此限制必须在Hadoop进程启动前生效。若已启动,需重启所有Hadoop服务。
6.4 WebUI无法访问:端口被占用的隐蔽真相
现象:http://master:9870打不开,但netstat -tuln | grep 9870无输出,curl http://localhost:9870也失败。
排查路径:
- 检查NameNode是否真在运行:
jps | grep NameNode; - 若进程存在,检查NameNode日志末尾是否有
Jetty bound to port 9870; - 若无此日志,说明端口被占用,但
netstat未捕获——因为Hadoop启动时可能指定了--bind-host参数,绑定到特定IP(如0.0.0.0:9870),而netstat默认不显示0.0.0.0; - 正确命令:
sudo netstat -tulnp | grep ':9870',显示占用进程PID; - 杀掉冲突进程(如旧版Hadoop、Tomcat)。
血泪教训:某次在Docker容器中部署,宿主机的8080端口被Nginx占用,而容器内Hadoop配置了`