ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

大数据分布式集群部署与运维实战:从HDFS到高可用

大数据分布式集群部署与运维实战:从HDFS到高可用 1. 拆解“分布式集群”——先搞清到底在解决什么问题1.1 大数据的“大”不是数学问题而是工程问题很多人一听到“大数据分布式集群”第一反应就是“很多台机器连起来跑 Hadoop”。这个理解不能说错但容易让人在后续走弯路。我见过不少项目机器买了一大堆组件装了一大圈最后跑起来还不如单机快原因就是压根没想清楚分布式集群到底要解决什么问题。大数据的“大”字本质上是说单台机器的存储能力和计算能力都不够用了。存储不够解决办法是加硬盘但硬盘容量总有上限而且单块硬盘读写速度和吞吐量也就那样。计算不够解决办法是加 CPU、加内存但单个服务器能插的 CPU 和内存条总有物理极限价格也会指数级上升。所以当数据量到一定规模工程上唯一的出路就是把数据拆开放到多台机器上把计算拆开分给多台机器同时做也就是所谓的“分布式”。这里有个非常直观的类比。一家小餐馆一个厨师既能切菜又能炒菜客人少没事客人多了一个厨师忙不过来你是选择给这个厨师换一口更大的锅、配一把更快的刀还是选择再雇几个厨师、把不同菜品分给不同的人做前者就是“纵向扩展”给单机堆硬件后者就是“横向扩展”也就是搭建集群。大数据场景下纵向扩展很快会遇到性价比和物理上限所以现实的工程选择几乎都是横向扩展。所以“大数据分布式集群”不是一个产品名而是一整套工程架构思路。它至少包含三个核心诉求存储的横向扩容——数据分散在多台机器上每台机器只存一部分计算的并行化——一个大任务拆成很多小任务在不同机器上同时执行服务的高可用——某台机器挂了系统还能继续提供服务。理解了这三件事后面看所有组件的设计逻辑都会顺很多。1.2 集群里的角色划分谁当领导谁干活分布式集群里机器和机器之间不是平等的。拿最常见的 Hadoop 生态来说里面天然存在“管理者”和“劳动者”两种角色。存储层面HDFS分布式文件系统把大文件切块block分散存储在各台机器上。负责记录“哪个文件、哪些块、存在哪台机器”的元数据服务叫 NameNode它是整个存储系统的“大脑”真正存数据块的角色叫 DataNode它是干活的“手脚”。元数据丢了集群里的数据就找不到了所以 NameNode 的可靠性比 DataNode 还重要。计算层面YARN资源调度器里有 ResourceManager 负责接收任务、分配资源NodeManager 负责在每台机器上实际启动容器跑任务。这里可能有人会混淆说 Hadoop 不是有 MapReduce 吗MapReduce 是计算框架YARN 是资源调度层MapReduce 任务跑在 YARN 分配出来的资源上。两层职责不同。除了这两组角色还有一类容易被忽略的角色——协调者最典型的是 ZooKeeper。它不存业务数据也不跑业务计算它的职责是让集群里的各个角色“步调一致”比如选主、维护配置、检测节点存活。你可以把它理解成整个集群的“神经中枢”。大数据集群的很多高级特性比如高可用自动切换、Kafka 的 Broker 选举、DolphinScheduler 的主备切换底层都依赖这个组件。1.3 分布式是通用架构大数据是落地场景这里需要理清一个很容易混淆的概念。分布式架构是一套通用的系统设计思想早期在互联网后端、微服务、缓存、消息队列里都有应用而大数据是分布式思想最典型、最集中、也是工程化程度最高的落地场景。你可以这么理解写电商后端时如果流量大了你会把应用拆成多个微服务部署在多台机器上用负载均衡分发请求用 Redis 集群做缓存用消息队列做异步削峰——这也是一种分布式系统。而大数据集群更“重”一些它要面对的不只是高并发请求而是海量数据的存储和批量计算所以它对网络带宽、磁盘 IO、数据本地性、任务调度有更高要求。正因为如此做大数据分布式集群的人和做后端分布式的人虽然都在处理“分布式”的问题但使用的工具和考量的重点差异很大。前者关心的是 HDFS 块大小怎么设置、MapReduce 或 Spark 的 Executor 怎么分配、任务调度怎么排队后者关心的是服务注册发现、配置中心、网关、链路追踪。这篇博文聚焦的是前者的场景——构建以 Hadoop 为基础的分布式计算集群并让它在生产环境里稳定跑起来。2. 集群架构选型——从业务需求倒推组件清单2.1 先算数据量和计算压力再决定要不要上集群很多刚开始接触大数据的人会犯一个错误先决定“我要搭一个集群”再想拿它干什么。这个顺序其实反了。正确的顺序是先评估数据量级和计算场景再决定架构规模。有个粗略的参考口径。数据量在几百 GB 到一两 TB 之间单机 MySQL 加上索引优化、分库分表、归档冷数据通常就能扛住没必要上分布式。数据量到几十 TB 甚至 PB 级别或者需要跑全量数据的复杂分析比如几亿条日志做关联统计单机计算引擎已经力不从心这时候 HDFS 加 Spark/Presto 这类方案才有意义。除了数据量还要看计算特征。两类典型场景批处理场景一次处理全量历史数据吞吐量优先延迟不敏感。典型的是离线报表、全量同步、模型训练前的数据预处理。适合用 MapReduce、Spark、Flink批模式。在线查询场景在大量数据上做低延迟交互式查询。典型的是用户画像查询、实时大屏。适合用 ClickHouse、Doris、Kudu、HBase 等。流式计算场景数据持续产生需要秒级甚至毫秒级响应。典型的是实时风控、实时监控。适合用 Flink流模式、Kafka Streams。如果实际业务这三种都有那就不是“一套集群全搞定”而是多套组件协同。我见过很多小团队一上来就想把 Hadoop、Spark、Flink、Kafka、HBase、ClickHouse 全装齐结果运维成本直接压垮整个团队。集群越复杂故障面越大这是铁律。2.2 核心组件各自解决什么问题这里把最常见的几个组件整理成一张表方便对照组件解决什么问题什么时候必须上什么时候可以先不上HDFS海量文件分布式存储自动副本冗余有大量大文件需要廉价存储数据量几个 T 以内单机文件系统够用YARN集群资源统一管理、任务排队调度多个计算框架共用一套集群单计算框架且资源竞争不激烈ZooKeeper分布式协调、选主、节点状态管理需要高可用、主备切换实验环境、单点部署可暂时省略Spark大规模内存计算适合迭代式算法、复杂批处理有复杂 ETL、机器学习任务纯 SQL 统计分析可先用 Hive 顶Kafka分布式消息队列高吞吐数据管道数据需要削峰、解耦、多系统订阅数据量小可以直连写数仓Flink实时流式计算低延迟处理有实时风控、实时大屏需求秒级延迟可接受时可先用 Spark StreamingDolphinScheduler工作流编排、任务定时调度、依赖管理任务多、依赖复杂需要可视化任务少直接用 crontab 能搞定HBase海量数据实时读写随机查询需要千万级以上行键查询查询少且可离线用 Hive 即可Redis热点数据缓存、分布式锁、临时数据存储有高并发读、需要分布式锁没有明显的缓存需求时不必上集群表格不是让大家照着全选而是帮助建立“组件—问题”的映射关系。很多组件解决的不是同一个问题不构成互相替代关系更像是拼图的不同部分。2.3 特别提醒不要在实验阶段追求“全家桶”这里说一个我自己的体会。如果是刚接触大数据、还在学习阶段我强烈建议先把 Hadoop 伪分布式或一个三节点小集群玩熟把 HDFS 读写、YARN 提交任务、日志排查这些基本功练扎实再逐步加 Spark、Kafka 这些上层组件。原因是组件的安装和配置本身不是难点真正的难点在于组合起来之后的排障。比如一个任务提交到 YARN 后一直卡在 ACCEPTED 状态可能是内存配置问题但如果你同时装了 Spark、Flink、Hive、ZooKeeper、Kafka光是定位“到底是哪个组件抢占资源、哪个配置项写错了”就能让你排查两天。“全家桶”看着丰满实际用起来每一个都可能是新的故障源。我当年搭建第一套生产集群的时候就是只用了 HDFS、YARN、ZooKeeper 加 Spark跑通了核心链路之后才根据业务需要逐步接入 Kafka 和调度系统。这个节奏虽然慢但每一步都能稳得住出现问题也能清晰定位到具体组件。3. 部署前最容易翻车的几个基础环境项3.1 Java 版本和 JAVA_HOME问题比想象中多Hadoop 生态绝大多数组件都是 Java 写的所以 Java 环境是整套集群的地基。这个地基最容易出问题的点有两个版本不一致和 JAVA_HOME 没配好。先说版本。Hadoop 3.x 官方要求 Java 8 或 Java 11。Spark 3.x 同样兼容 Java 8/11。但有些开源组件的版本对 Java 有更严格的要求比如某些版本只支持 Java 8某些新版本只支持 Java 11/17。建议是所有节点统一使用同一个 JDK 版本最好连发行版都统一比如都用 OpenJDK 8u 系列。跨版本部署在单机上看不出问题一旦进入集群通信序列化、TLS 握手、Class 版本这些都会变成莫名其妙的异常。再说 JAVA_HOME。每台机器的 JDK 安装路径要统一比如统一装在/usr/local/jdk8然后用软链接保持路径一致。Hadoop 启动脚本里经常会去读取JAVA_HOME环境变量如果你在/etc/profile里配置了但普通用户执行脚本时没有 source 到就会出现 “JAVA_HOME is not set” 这类错误。一个容易被忽略的坑HDFS 的 DataNode 启动时如果找不到正确的 Java 环境日志里只报一句笼统的错误很多人会去查网络配置和权限却没想到是 Java 环境问题。所以部署前第一件事挨个节点执行java -version确认版本一致这花不了几分钟能省下后面大量排障时间。3.2 主机名、hosts 解析和 SSH 免密登录集群节点之间通过主机名互相访问这依赖两个东西每台机器的/etc/hostname和/etc/hosts。Hadoop 生态在通信时节点间会互相反解析 IP如果/etc/hosts里没有完整映射会出现 “Hostname cant be resolved” 或节点间连接超时的问题。我的习惯是所有节点的/etc/hosts都写全所有集群节点的 IP 和主机名包括本机。注意不要图省事用默认的127.0.1.1 主机名这种回环地址否则 NameNode 和 DataNode 通信时可能把请求发回给自己。SSH 免密是另一个高频翻车点。Hadoop 启动时脚本会通过 SSH 从主节点连到各从节点启动进程如果免密没配好进程会启动一半就挂掉还容易让人误判为“某个组件有问题”。配免密的步骤很简单但权限问题最坑# 每台机器生成密钥 ssh-keygen -t rsa -b 4096 # 把公钥追加到需要互相登录的节点 authorized_keys 里 ssh-copy-id hadoopnode01 ssh-copy-id hadoopnode02配置完成后必须逐对验证从每个节点 SSH 到其他每个节点确认不需要密码。尤其是从主节点到所有从节点、从备主节点到所有从节点这两条链路。文件夹.ssh权限必须是 700authorized_keys权限必须是 600权限过宽会被 SSH 直接忽略。3.3 时间同步、防火墙和 SELinux不处理就是埋雷先说时间同步。分布式系统里各个节点各执一词的时间会导致各种诡异问题日志时间对不上、心跳超时、Kerberos 票据校验失败。生产环境标准做法是配置 NTP 或 chrony让所有节点和同一台时间服务器同步。实验环境至少手动执行一次统一对时timedatectl set-ntp true timedatectl status再说防火墙。大数据生态的端口非常多比如 HDFS 的 8020/9000、NameNode UI 的 9870、YARN 的 8088如果按最小权限放行需要维护一堆端口规则容易漏。实验环境里最省事的是直接关闭每台机器的 firewalld 或 ufw 服务把精力省下来专注业务组件本身。生产环境如果是公司内网集群通常也有专门的网络策略团队处理不推荐自己在每台机器上单点维护防火墙。SELinux 也建议直接关闭。它确实提升了系统安全性但对于大数据集群这种高频通信、动态创建文件的场景SELinux 的安全策略经常会拦截进程访问某些目录报出来却是 Permission denied很难定位。改法很简单sed -i s/SELINUXenforcing/SELINUXdisabled/ /etc/selinux/config # 重启生效临时关闭执行: setenforce 03.4 文件句柄、进程数和 swap性能雪崩的隐患大数据组件在运行时会同时打开大量文件。DataNode 存储很多文件块、Spark 任务会启动很多 Executor每个都要占用文件描述符。默认的ulimit -n是 1024对集群来说远远不够。建议调到 65535 或更高。修改方式通常是在/etc/security/limits.conf里加上* soft nofile 65535 * hard nofile 65535 * soft nproc 65535 * hard nproc 65535修改后要重新登录会话用ulimit -n验证是否生效。我记得有一次集群跑大数据任务时DataNode 频繁报 “Too many open files”排查了一圈才发现是 limits.conf 里有个hadoop组的写法拼错了导致整个组没生效。这种基础配置改完一定要验证不能只看写了没写。swap 也需要留意。Hadoop 官方其实建议关闭大部分 swap因为它会拖慢整个集群的响应速度。如果内存不够进程很快就跑到 swap 上磁盘 IO 成为瓶颈任务会变慢几倍甚至几十倍。实验环境内存紧张的至少把vm.swappiness调低到 5 或 10sysctl vm.swappiness10 echo vm.swappiness10 /etc/sysctl.conf4. Hadoop HDFS YARN 集群部署完整过程记录4.1 三节点集群的角色规划这里以一个典型的三节点集群为例这套规划覆盖了 Hadoop 最核心的部署形态也方便日后扩展。节点主机名IP 规划示例角色节点 1node01192.168.10.11NameNode、ResourceManager、ZooKeeper节点 2node02192.168.10.12Standby NameNode、Standby ResourceManager、ZooKeeper节点 3node03192.168.10.13DataNode、NodeManager、ZooKeeper这个方案里节点 1 和节点 2 是主备关系节点 3 单纯做数据节点。生产环境如果数据量更大可以在节点 3 之外继续加 DataNode把workers文件加上新节点即可横向扩容。为什么主节点要放在不同的物理机器上因为如果主节点和备主节点在同一台物理机上这台机器一挂主备一起没高可用就名存实亡了。这个道理很多人理论上明白实际部署时图省事把两个角色放在同一台机器上测试时也确实能自动切换直到一次真的断电才发现整个集群不可用。4.2 核心配置文件的每一个关键项都别瞎抄Hadoop 的配置在$HADOOP_HOME/etc/hadoop/下最核心的是core-site.xml、hdfs-site.xml、yarn-site.xml。网上很多教程直接贴一大段配置让复制但我还是建议一个参数一个参数看明白因为生产环境的问题大多出在配置参数和你的实际资源不匹配上。core-site.xml里最关键的是 fs.defaultFS。它指定了 HDFS 的访问入口值通常写集群的逻辑名称而不是具体某个节点这是为了高可用切换时客户端能自动切换property namefs.defaultFS/name valuehdfs://mycluster/value /property property nameha.zookeeper.quorum/name valuenode01:2181,node02:2181,node03:2181/value /property你可能会问hdfs://mycluster是什么这是一个逻辑名字具体对应哪个节点在hdfs-site.xml里有 nameservice 和 namenode 的映射。这样设计的好处是当 NameNode 从 node01 切换到 node02 时客户端不需要改配置直接用逻辑名访问。hdfs-site.xml里需要注意几个参数dfs.replication副本数三节点集群建议 2 或 3。设为 3 意味着每个数据块存三份容忍两台机器同时故障严格说是宕机副本数-1台但副本数也直接占用存储空间三副本就意味存储成本三倍。dfs.nameservices和dfs.ha.namenodes高可用配置的命名空间、主备节点 ID。dfs.namenode.name.dirNameNode 元数据落盘目录最好配置多个目录且分布在不同的磁盘上避免单盘故障导致元数据全丢。dfs.datanode.data.dirDataNode 数据目录同样建议多目录配置数据块会以轮询方式分布在各个目录里。yarn-site.xml里最影响任务能否正常跑起来的是内存相关的配置。很多人搭好集群提交 Spark 任务发现任务一直 PENDING十之八九是 YARN 的内存资源计算出了问题。核心参数有yarn.nodemanager.resource.memory-mb单节点总内存、yarn.scheduler.maximum-allocation-mb单个任务最大申请内存、yarn.scheduler.minimum-allocation-mb单个任务最小申请内存。物理机 64G 内存不能全部分配给 YARN要留出操作系统和 DataNode 等进程需要的量但留少了任务跑不起来留多了资源浪费。一般建议是留物理内存的 10%20% 给系统。4.3 格式化 NameNode 只能有一次启动顺序也有讲究部署过程中最“紧张”的操作就是格式化 NameNodehdfs namenode -format这个命令会在dfs.namenode.name.dir指定的目录下生成集群 ID 和元数据。格式化本身没有风险风险在于你启动集群之后发现某个配置写错了又手欠地重新格式化了一次。这样会导致集群 ID 不一致新格式化后的 NameNode 和已经存在的 DataNode 互相不认DataNode 一直报“Incompatible clusterIDs”这是所有新手最容易踩的坑。如果启动后发现配置有误不要重新格式化更稳妥的办法是一致地处理如果 DataNode 目录里没有重要数据可以用hdfs datanode -format统一格式化数据节点如果已经有数据更推荐把错误的配置改回来重启服务。启动顺序建议按这个来启动 ZooKeeperzkServer.sh start如果配置了 HA先启动 JournalNodehdfs --daemon start journalnode在主 NameNode 上启动 NameNode然后同步元数据到备 NameNode启动 DataNodehdfs --daemon start datanode启动 YARNstart-yarn.sh为什么 ZooKeeper 要先启动因为 NameNode 启动时会连接 ZooKeeper 创建临时节点如果 ZooKeeper 没起NameNode 会一直报连接异常。4.4 部署完成后的自检清单集群启动完先别急着跑业务按下面几步验证jps查看各节点的 Java 进程是否都在。node01 应该能看到 NameNode、ResourceManager 等相关进程node03 应该能看到 DataNode、NodeManager。访问 Web UI。NameNode 的地址是http://node01:9870YARN 的地址是http://node01:8088能看到节点列表里 DataNode 数量正确。上传和下载文件验证hdfs dfs -mkdir /test hdfs dfs -put /opt/data.txt /test/ hdfs dfs -cat /test/data.txt提交一个简单的 MapReduce 任务验证计算链路完整hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount /test/input /test/output跑通 wordcount才说明存储和计算两个链路的配置都没问题。5. 高可用设计——不是加一个节点那么简单5.1 主节点单点故障是集群最大的隐患集群里最怕挂掉的不是存储节点DataNode而是管理节点NameNode、ResourceManager。DataNode 挂了它上面存的数据块会因为副本机制在其他节点上自动补齐影响有限但 NameNode 挂了数据块的元数据就没人能提供整个 HDFS 对外不可用所有读写请求全部失败。这就相当于一个图书馆的检索系统瘫痪了书还在但你找不到、取不出来。高可用HA的核心思想很朴素不让主节点成为单点提供一个热备节点时刻准备接管。但“加一个节点”只是表象真正的难点在于主节点挂了之后备节点怎么知道备节点接管时怎么保证不出现两个主节点同时抢着干活5.2 JournalNode 和 ZKFC 各司其职要回答上面两个问题需要理解高可用架构里两个关键角色。JournalNode。NameNode 的职责是维护文件系统的元数据编辑日志EditLog任何时候对文件系统的修改都会以日志的形式记录下来。在 HA 架构里Active NameNode 把自己的 EditLog 实时写到一组 JournalNode 节点上Standby NameNode 同时从 JournalNode 读取这些日志并回放到自己的内存中。这样 Standby NameNode 的元数据状态和 Active 保持一致随时可以接管。JournalNode 的数量要求是奇数个一般是 3 或 5 个。这是因为写入日志需要大多数quorum节点确认成功3 个 JournalNode 里 2 个确认就算成功容忍 1 个故障5 个里 3 个确认容忍 2 个故障。用奇数是为了避免“脑裂”时两边都无法达到法定人数。ZKFCZooKeeper Failover Controller。这是一个守护进程跑在 NameNode 所在的机器上负责监控 NameNode 的健康状态并在 ZooKeeper 中创建临时节点来参与选主。Active NameNode 的 ZKFC 会持有一个锁Standby 的 ZKFC 监控这个锁一旦发现锁消失了说明 Active 出问题了就尝试获取锁并触发自己的 NameNode 切换为 Active。整个切换过程通常在几十秒内完成对客户端来说是一个短暂的不可用窗口。5.3 脑裂Split-Brain问题与 Fencing 机制高可用设计里最怕的不是“都不干活”而是“同时干活”这在分布式系统里叫脑裂。想象一下网络抖动导致 Active NameNode 和 ZooKeeper 的会话断开但 NameNode 本身没有停下此时 ZKFC 认为它挂了让 Standby 切换为 Active。如果网络恢复两个 NameNode 同时对外提供服务同时写 EditLog元数据很快就会不一致整个集群彻底混乱。Fencing隔离机制就是为了解决这个问题。它的目的很直接在让 Standby 切换为 Active 之前先把旧的 Active 彻底踢出局让它没有能力再提供服务。常见的方案有三种SSH fencing通过 SSH 到旧 Active 上直接执行kill -9强制结束进程。shell fencing执行自定义脚本比如关闭旧 Active 的网络接口。zfence通过 ZooKeeper 的 fencing 节点实现隔离。生产环境通常用 shell 或 SSH 方案。这个机制非常关键千万别偷懒不配否则脑裂发生时整个文件系统损坏数据恢复的成本是灾难性的。5.4 ZooKeeper 节点数量为什么必须是奇数这里再展开说说 ZooKeeper 节点数量为什么必须是奇数这是面试高频题也是实际部署最容易纠结的地方。ZooKeeper 的选主和写请求处理都依赖“大多数”机制。3 个节点里需要至少 2 个正常才能选主和对外服务5 个节点里需要 3 个。如果部署 2 个节点挂 1 个就只剩 1 个达不到 2 的大多数整个服务不可用——和挂 1 个没区别浪费机器。如果部署 4 个节点挂 1 个还有 3 个但实际上 4 个和 3 个能容忍的故障数一样都是 1 个机器多花了三分之一可用性没有任何提升。所以奇数节点是最优解。实际生产部署我还有条经验ZooKeeper 不要和 NameNode 角色挤在同一个 ZKFC 的故障域里。如果一台机器同时起着 ZooKeeper 和 NameNode机器断电时这两个角色同时消失高可用的自动化切换逻辑就会变得复杂。理想情况是 ZooKeeper 独立于大数据主节点部署哪怕就是普通机器也不要省这一层。6. 集群之上的调度与协同组件部署要点6.1 为什么一定要引入任务调度平台集群搭好了存储计算链路通了但业务跑起来还有一个躲不开的问题任务怎么组织。一个完整的数据分析流程通常包含 数据抽取、清洗、宽表加工、指标计算、报表导出十几个任务之间有前后依赖关系。如果这些任务全靠 crontab 定时触发依赖关系只能靠人为保证任务失败也不会自动重试告警数据管道就会变得非常脆弱。DolphinScheduler 是这层问题里我比较推荐的开源方案尤其是 3.1.4 这个版本之后UI、调度性能和稳定性都有明显提升。它也天然支持分布式部署Master 负责任务的拆分和调度Worker 在多个节点上并行执行任务ZooKeeper 负责 Master 的选主和任务队列的状态一致性。6.2 DolphinScheduler 集群部署的三个关键点部署 DolphinScheduler 集群之前先要确认三个前置条件JDK、ZooKeeper、元数据库MySQL 或 PostgreSQL。这三个少一个安装脚本跑一半就会报错。第一元数据库的创建要手动完成。默认脚本不会帮你创建数据库你需要预先建好库并授权然后修改application-dao.properties里的数据库连接信息。库的字符集建议统一 utf8mb4否则后续处理中文数据容易出现乱码。第二ZooKeeper 连接信息要写对。在application.yaml里配置 ZooKeeper 地址列表和 root 节点路径。特别注意 root 路径不要和别的组件共用否则会互相覆盖数据。第三Master 和 Worker 是两组独立进程。安装节点上可以只启动 Master、只启动 Worker、或者两者都启动。生产环境建议把 Master 放在管理节点上和 NameNode 集群同一批机器Worker 放在计算节点上和 DataNode 同一批机器让 Worker 尽量靠近数据利用数据本地性减少网络传输。部署完成后第一件事不是建任务而是验证调度服务是否正常打开 Web UI确认 Master 和 Worker 都在线创建一个最简单的 Shell 节点任务比如echo hello手动跑一次看任务日志是否正常输出。6.3 Kafka 集群部署Broker、分区和副本的取舍Kafka 在大数据链路里的角色是“数据管道”——把业务系统的数据实时接入 HDFS 或实时计算引擎。它自身的集群部署也不复杂但有几个配置细节决定了生产环境的稳定性。Kafka 的每个 Topic 会分成多个分区Partition分区是对外的并行度单元。分区数越多单个 Topic 的吞吐上限越高但同时也会带来更多的文件句柄和选举开销。为什么分区数不能越多越好因为每个分区会对应一组 Leader 和 Follower 副本元数据管理和客户端连接开销都是跟分区数线性增长的。大家普遍的建议是单个分区吞吐按几 MB/s 粗略估算然后根据目标吞吐反推分区数不要盲目设大。副本数是 Kafka 高可用的基础。replication.factor一般设为 3允许两个 Broker 同时故障实际取决于 ISR 机制。这里有一个常见的坑如果 Broker 节点数只有 2却把副本数设为 3分区副本永远分配不齐Leader 选举反复失败整个 Topic 写入超时。副本数不能大于 Broker 数量这是硬件层面的硬约束。Kafka 通信的 session 超时参数session.timeout.ms也容易被忽略。我曾经遇到过一个情况集群中某个 Broker 因为 GC 停顿超过默认的 10 秒被其他 Broker 判定为故障并触发 leader 切换但切换期间产生大量网络重试整个集群吞吐下降了一半。后来把 GC 参数调优、超时时间调大才恢复稳定。部署 Kafka 有一条硬件层面的经验尽量把 Kafka 部署在独立的磁盘上不要和 HDFS 的 DataNode 数据目录共用物理盘。两者的 IO 模型差别很大HDFS 数据块写入是连续大块写Kafka 是顺序追加写虽然都是顺序 IO但它们同时抢占磁盘带宽时会导致两边性能同时劣化。有条件就把不同角色挂载到不同磁盘。6.4 Redis 集群部署与分布式锁的几个认知纠偏热搜词里出现 Redis 集群和分布式锁说明很多人在大数据场景之外也在做后端的分布式改造。这两块内容同样值得展开。Redis 集群有两种常见形态哨兵Sentinel模式和Cluster 模式。哨兵模式解决的是高可用问题——主节点挂了自动切换从节点但数据量仍然受限于单机内存。Cluster 模式解决的是数据容量和吞吐的横向扩展问题——数据自动分片到多个主节点上。选型建议单机内存够用、只需要高可用用哨兵单机内存不够、或者写并发高到单机无法支撑用 Cluster。再强调一次Redis Cluster 的客户端必须支持 Cluster 协议普通的 Jedis 连接池方式连接 Cluster 地址是不能自动路由到正确分片的很多人在这里踩坑。分布式锁的话题也提一下。Redis 实现分布式锁最常见的姿势是 SETNX 加过期时间SET lock_key unique_value NX PX 30000但只做这一步是不够的。锁必须设超时时间否则持有锁的进程崩溃锁永远不会释放加锁和解锁要确保是同一个客户端用唯一的 value 做校验防止误删别人的锁。这三点是分布式锁的底线少一点都会在高并发下出问题。至于 RedLock红锁这类更复杂的一致性方案业界的争议也很大。我的建议是绝大多数业务场景只要锁的过期时间设得足够宽裕加上合理的续期机制和业务幂等兜底用单机 Redis 或哨兵模式已经够用追求极端强一致性的场景应该优先考虑 ZooKeeper 或 etcd 来实现分布式锁而不是在 Redis 上强行做复杂改造。6.5 分布式事务能不用就不用最后聊一下分布式事务这也是后端同事经常问我的话题。分布式事务难的根本原因是它要求在多个独立数据库或服务之间维持原子性——要么全部成功要么全部失败。这在单体应用里靠数据库本地事务就能解决但跨服务后就涉及到网络不确定性和资源隔离复杂度成倍上升。常见的解决方案有 2PC两阶段提交、TCCTry-Confirm-Cancel、消息事务和最终一致性。2PC 的一致性最强但它的协调者本身是单点而且参与者资源锁定时间较长高并发下吞吐受影响。TCC 通过业务补偿机制规避资源锁但实现成本高每个业务要写三套逻辑。最终一致性方案比如本地消息表 MQ 消息确认是最常被推荐的生产方案因为它能把分布式事务拆成本地事务和消息消费的幂等处理。我的真实态度一直是能不用分布式事务就不用。很多所谓的分布式事务场景换一个业务建模方式就能规避。比如把多个步骤合并到同一个服务里、用本地事务处理或者接受数据的短暂不一致用定时对账来兜底。凡是让系统简单下来的方案长期看都是成本最低的方案。7. 扩容、下线、巡检——集群运维的三件常事7.1 节点扩容和下线有规范的流程集群不是搭完就一劳永逸的数据量增长后扩容是必然要做的事。扩容分为存储节点扩容和计算节点扩容对应的操作不太一样。存储扩容加 DataNode的正确流程是规划新节点配置 Java、SSH 免密、hosts、时间同步和部署前准备一致。把新节点的主机名加到workers文件。复制 HDFS 和 YARN 的配置文件到新节点。在新节点上执行hdfs --daemon start datanode和yarn --daemon start nodemanager或者直接在主节点执行start-dfs.sh一键启动。新增节点时HDFS 不会自动把旧数据重新平衡到新节点上。需要执行 rebalancehdfs balancer -threshold 10这个命令会调整副本分布让各 DataNode 的磁盘使用率之差保持在设定阈值内。阈值不要设得太小否则平衡任务会运行很久才结束而且会大量占用网络和磁盘 IO影响线上任务。下线节点比上线更敏感尤其是有数据在跑的情况下。正确做法是在dfs.hosts.exclude文件里写入要下线的节点然后执行hdfs dfsadmin -refreshNodesHDFS 会把这个节点标记为 Decommissioning开始把它的数据块逐步迁移到其他节点。等状态变成 Decommissioned 之后才允许关闭该节点。如果直接 stop数据块还没来得及迁移就会触发大量复制任务瞬间把其他节点的磁盘和网络打满。这里有个教训我见过有人本来只是临时检修一台机器却直接用kill杀掉 DataNode结果那台机器上存着不少 HDFS 的块副本数骤降集群用了好几天才把副本恢复到正常水平期间所有任务都变慢了。7.2 巡检项清单要落到每一天、每一周、每一月集群运维最忌讳的是“等告警了再去处理”。很多问题在告警之前已经有苗头只是没人看。我整理了一张很基础的巡检表可以直接抄频率检查项检查方式异常阈值每天各节点磁盘使用率df -h或监控系统查看超过 80% 预警超过 90% 必须处理每天HDFS 块损坏数量NameNode UI 或hdfs fsck /出现 corrupted 块立即排查每天关键进程存活状态jps或 systemd 服务状态有进程退出立即告警每天YARN 资源剩余YARN UI 的 Scheduler 页面队列资源长期打满注意评估扩容每周JournalNode 目录空间du -sh检查元数据目录元数据目录满会导致 NameNode 宕机每周小文件数量hdfs fsck / -files或统计目录小文件过多会拖慢 NameNode 内存每月备份验证恢复一份 NameNode 元数据备份到临时目录备份不可用等同没有备份每月节点时间偏差chronyc tracking或ntpq -p偏差超过 100ms 必须修正7.3 一次真实的故障排查DataNode 进程反复退出最后分享一个真实案例也是集群运维中很典型的排查链路。现象三节点集群中node03 的 DataNode 进程每隔几个小时就自动退出YARN 的 NodeManager 也经常失联。第一反应是内存泄漏或者进程被 OOM Killer 杀了。排查过程先看系统日志用dmesg -T | grep -i kill果然看到了 OOM Killer 的记录把 DataNode 进程当成内存占用大户干掉了。但为什么内存会不够继续查各进程的内存占用发现 DataNode 没占多少真正吃内存的是系统 buffer/cache——磁盘读写缓存。再往深处看发现 node03 的/data分区写满了DataNode 一直尝试写数据写不进去之后不断重试系统产生了大量 IO磁盘缓存激增最终触发 OOM Killer。到这里根因是磁盘写满而不是进程本身的问题。处理方式也很直接清理 node03 上无用的日志和临时文件把该节点 HDFS 的数据目录换到一块更大的盘上同时给面盘空间和 inode 使用率加了监控。这个案例也印证了一条经验——很多“进程异常退出”的表象根因都是磁盘、内存、句柄这些基础资源排查时先看资源再看组件日志顺序不要反。在大数据分布式集群这条路上踩坑是难免的但大多数坑都有固定的模式。把我上面这些经验理顺从部署前的基础环境、到选型、到高可用设计、再到日常运维每一步都稳一点集群就能少折腾你一点。
返回列表