做大数据运维这行有一种“特别熟悉的恐惧”——手机屏幕在凌晨三点亮起来群里跳出一条“任务失败”的告警你闭着眼睛都能感觉到机房或者云端的哪个进程正在哀嚎。我过去五年的大部分精力都耗在这上面从CDH到MRS一条迁移路走下来踩过的坑比吃过的盐还多。今天这篇不聊虚的就是把那些让我凌晨爬起来改配置的血泪教训一条条捋给你看。之所以想写这个是因为后台经常有人问你们当初为什么从CDH迁到MRS迁移过程中最痛的是什么迁完之后是不是就好了答案可能和很多人想象的不太一样。迁移本身的技术难度其实有限真正磨人的是那些藏在配置项、依赖关系、权限模型里的“隐形地雷”。这篇博文适合所有正在做或准备做大数据平台迁移的朋友不管是机房自建的CDH、HDP还是云上的MRS、EMR思路都能复用。我会把选型考量、迁移准备、实操步骤、迁后运维和问题排查一次讲透尤其是那些常规文档里不写的细节都会拿出来晒一晒。1. 五年前盯上CDH后来为什么又决定迁走1.1 初始选型时看中的恰恰是后来最拖后腿的当年上这套CDH集群图的是省心。Cloudera Manager一装整个Hadoop生态的组件像商品货架一样摆在那里勾选、配置、部署一条龙。那时候团队里就三个人前端后端一大堆业务要支持没精力也没能力从零编译、手工编排一套Apache Hadoop。CDH的版本管理和组件兼容性测试做得确实可以Spark、Hive、HBase、Kafka装好基本能跑日常运维靠Web界面点一点也算岁月静好。但现在回头看这种“省心”的代价是黑盒化。CM虽然把配置项都给你摆出来了可背后的默认值、依赖关系、服务发现机制是封装好的。你习惯了在界面上点“重启”点“刷新”却并不清楚它到底改了哪几个配置、动了哪些目录。等到要迁走或者要做深度调优的时候这种黑盒就成了大麻烦。我记得当时想查清楚HiveServer2实际生效的JDBC连接池参数翻半天CM界面也没找到明细最后是去节点上抓进程的JVM参数才看明白。1.2 迁移的真实原因成本、演进和可控性这里得说清楚不是因为CDH跑出了什么大事故才被迫迁移更多是主动选择。首先是成本账CDH的商业授权和整个集群的硬件运维成本逐年上涨而业务增量已经趋于平稳用同样的钱放在云上能买到更弹性的资源。其次是技术演进CDH的版本更新节奏偏慢新组件、新特性跟不上主流社区很多新工具没法直接用。最后是可控性我们希望把配置、监控、权限全部用代码管理起来而不是依赖一个图形界面。所以某一天我们决定用MRS作为新的底座。MRS是托管式大数据集群服务组件形态和CDH比较接近但底层是云原生基础设施——跨可用区部署、对象存储对接、弹性扩缩容这些都是机房环境给不了的。定这个方向的时候团队内部其实争论过好几轮要不要直接上原生Apache Hadoop要不要顺便切到数据湖方案后来达成的共识是先从CDH平稳迁到MRS把架构问题和新组件引入解耦降低一次性改造的风险。2. 迁移前夜必须摸清的三张清单很多迁移项目失败不是死在迁的过程中而是死在根本没想清楚自己有什么。CDH用久了组件多到你想不起来有哪些版本混乱到连CM都记录不全。我们当时准备了一个完整的盘点期核心是拉三张清单组件清单、端口目录清单、权限清单。这三张清单做完迁移路径基本上就清晰了一半。2.1 组件与版本依赖清单先搞清楚家里有多少“老古董”CDH集群长期运行上面养了一批“老古董”作业有的用Hive on MapReduce有的用Spark 2.x个别甚至还有靠HBase协处理器跑的业务。迁移第一件事就是把所有组件和版本摸清楚。我们用的办法是导CM配置快照再结合节点上的rpm包和进程命令行反推最终列出一张表HDFS、YARN、Hive、HBase、Kafka、ZooKeeper、Spark、Sqoop、Flume每样对应CDH版本里的具体子版本。这里有个容易被忽略的点CDH虽然统一打包但组件之间的版本兼容是被Cloudera测试过的到了MRS上哪怕大版本名字一样底层细节也可能不一样。最典型的是Spark和Hive的整合方式。CDH里Spark默认不关联Hive元数据跑SQL要么走Hive on Spark要么靠SparkSession自己连MetaStore而MRS往往默认做好了Spark与Hive的元数据互通直接读表就能用但一些隐含的jar包冲突就得靠环境变量和classpath策略来处理。我们在迁移前就盯着这张清单做差异核对把每一个组件的版本和连接方式都记下来后续改造脚本时才没乱套。2.2 端口、目录与配置规划清单默认值就是最深的坑迁到MRS之后最直接、最气人的变化就是端口和目录。CDH有自己的一套命名习惯比如NameNode的HTTP页面走50070YARN的ResourceManager页面走8088这些端口在MRS上不一定对得上甚至有些组件为了安全直接把Web UI绑到内部地址外部根本访问不了。目录方面CDH默认把数据放在/dfs、/yarn、/tmp这样的根路径下而MRS会使用独立的挂载点命名空间完全不一样。如果一个作业里硬编码了IP、端口或者绝对路径迁移之后必炸。我们当时干了一件笨但管用的事写了一个脚本扫描所有作业代码、配置文件里的IP和路径关键词把所有“看起来像地址和路径”的地方全部列出来然后对照MRS的规划逐项修正。这块别嫌麻烦漏一个就是一次凌晨事故。我后来总结出一个原则凡是写在代码里的环境信息全部抽出来放到配置中心凡是能在集群层面统一的资源路径全部用抽象路径代替。宁可多写几行代码也不要把环境细节散落在业务逻辑里。2.3 认证与权限清单Kerberos不是你想开就能开CDH环境里我们之前没开Kerberos因为内网环境加上应用紧耦合一直没狠下心启用认证。而MRS的默认安全模式下Kerberos认证是强制性的所有客户端、所有作业都得先过认证才能访问服务。这一步的改造量其实比迁移大得多。首先要给每类业务创建独立的Kerberos账号配置keytab文件然后所有代码连接组件时都得带认证上下文最后就Kerberos票据过期还要配置自动续期否则凌晨跑批一定会出现“票据过期导致连接失败”的告警。权限方面CDH如果用的是简单的Linux用户映射到了MRS上则需要适配新的权限模型。比如Hive表的授权、HDFS目录的授权、YARN队列的授权三种权限体系的判定逻辑不完全一样。我记得迁移之后有个业务组反馈说能提交任务但删不了表排查半天就是Ranger权限策略没把delete权限配上。所以迁移前一定要把权限清单整理出来每个应用的用户、需要的组件访问范围、数据目录的读写要求逐条映射到新的权限模型。3. 迁移实操从CDH到MRS的完整闭环3.1 元数据迁移的硬骨头Hive、HBase、Kafka的差异先说Hive元数据这是最典型的例子。CDH一般用外部MySQL承载Hive的元数据库表结构是Cloudera定制过的和Apache版本有细微差异。迁移时要把这些表数据导入到MRS对应的元数据库里听起来简单实战里全是坑。比如自增主键的起始值可能不对导致新增分区ID冲突又比如表字段里某些特殊字符在字符集转换时会乱掉再比如分区信息量大到跑一晚上都导不完。我们当时是分两步做的先把所有元数据导出成SQL文件做一遍全量校验然后在目标集群重置自增序列再按依赖顺序导入。这里尤其注意外键关联Hive元数据表之间是有关联的导入顺序错一个就全崩。我的建议是别嫌弃“先drop再导入”的笨办法在维护窗口内用最保守的方式保证数据一致比追求所谓“增量同步”然后半夜出问题要强得多。我也见过用第三方工具做在线迁移的但版本兼容和特殊表处理始终是高危区非必要不冒险。HBase元数据相对简单主要是坐标和Split信息但要注意协处理器的jar包——业务自定义的协处理器如果在目标集群没有对应的jar包RegionServer起来后会直接崩溃。Kafka要处理的是Topic的分区数和副本数规划CDH上如果用过老版本Kafka消息格式可能和MRS默认支持的不一致需要开兼容参数或做数据迁移。3.2 数据同步那场“马拉松”distcp的调参与重试数据迁移的主体是HDFS数据我们用的工具是distcp。这个概念跟名称一样直白只要两个集群放通网络就能从源路径拷贝到目标路径。但真跑起来几十个TB的数据隔着网络同步各种各样的超时和Map失败就来了。distcp的坑集中在几个参数上。第一是带宽限制如果源集群和MRS之间的链路不是独占的不设限的distcp会直接把专线带宽打满影响线上业务。解决方式是加-bandwidth参数给任务设置上限跑慢一点但求稳。第二是Map数Map太多会造成大量小文件太少又发挥不了并行度通常按文件数量和总体积来估算我一开始直接按默认值跑结果几十万个文件的元数据扫描阶段就把NameNode搞得不轻。第三是文件校验distcp默认会做CRC校验但有些文件可能是特殊格式或者之前损坏过校验失败会使得任务反复重试定位起来特别费劲。我们的做法是先跑一遍不带校验的批量拷贝再用快照对比文件数和大小最后随机抽一批文件做内容比对既控制了时间又保证了可靠性。还有个大坑是网络层面的RPC端口映射。机房环境直接访问MRS的NameNode RPC端口可能受网络策略限制需要配置跳板扇出或者端口映射。这个在文档里通常只给一个连接示例实际跑的时候还得调整客户端侧的参数比如dfs.client.use.datanode.hostname、dfs.client.socket-timeout之类。遇到任务频繁中断别急着重跑先抓客户端的日志看是哪一层的超时对症下药。3.3 作业和调度器的适配从CM API到RESTful的一次性改造CDH时代很多运维脚本依赖Cloudera Manager API去查服务状态、触发重启。这套API在MRS上没有得换成MRS提供的RESTful接口。不要小看这个转换接口路径、参数结构、返回字段全变了写正则去解析的老脚本基本废掉。我们当时把所有脚本分层处理最底层封装一个“服务操作模块”统一提供启停、状态查询、日志拉取的方法上层脚本只调用模块不直接跟API打交道。这样改造完一次之后再换平台只改底层模块就够了。作业治理方面CDH默认的YARN调度器很多是Fair SchedulerMRS则普遍用Capacity Scheduler的模型。两个调度器的队列配置思路完全不同迁移后不仅队列名要变每个队列的容量比例、最大资源、优先级也要重新设计。我们有过一次深刻的教训原来的高频小任务队列在Fair策略下可以快速抢占资源到了Capacity模型里如果没有给足最大容量一到晚高峰就被大任务挤兑跑批从半小时拖到三小时。后来重新按业务重要度划分队列大任务和小任务物理隔离才稳定下来。建议迁移前就根据业务流量模型画出队列规划而不是迁完再按报错反应式调整。4. 迁完之后真正的“噩梦”才刚开始4.1 监控告警体系重建别把“修复”当成“迁移”迁移完成不是终点运维体系的适配才决定你后续能不能睡好觉。CDH的监控界面是CM自带的那套界面友好集成度高。MRS的监控入口在云管侧指标字典和阈值语义都不太一样。如果直接把旧告警规则翻译过来很可能告警根本不触发或者天天误报。我推荐的做法是先对照两份指标清单逐个确认核心指标在目标平台的语义。比如CDH的“HDFS容量使用率”和MRS的“HDFS容量使用率”虽然名字一样但统计口径可能不同一个是除以总配置容量一个是除以当前实际挂载容量阈值完全对不上。我们踩过一次迁移后按老规矩在80%设告警结果容量到90%了还没提示排查才发现新平台的统计方式把预留空间剔除了。监控体系一定要在迁移完成后的冷启动阶段逐项验证用脚本模拟故障看告警是否及时、通知是否到位。4.2 Kerberos和权限模型的自适应调整MRS默认安全模式下Kerberos和权限的问题会在你第一次提交作业时扑面而来。常见的问题是业务账号没有正确的服务端主体导致从客户端连接HDFS时报如下错误“Server not found”还有keytab文件过期未更新任务一跑就是一串“Permission denied”。这些都需要写自动化运维脚本来管理不能靠人工。最让我头疼的是YARN队列和HDFS目录权限的叠加。之前CDH的权限模型比较粗一个用户基本能访问所有目录MRS下Ranger策略管得细目录、表、列、函数都能控制。好处是安全可控坏处是每个新任务都有可能踩“权限边界不够”的雷。我们当时的应对是建了一个权限申请审批的自动化流程业务提交权限申请单脚本根据模板生成Ranger策略并自动刷新。这样才能在安全和效率之间找到平衡不然运维光处理权限工单就不用干别的了。4.3 凌晨三点爬起来调配置的三次实录讲几个真实发生过的凌晨事故比任何原理都直观。第一次是迁完后第二周的凌晨跑批任务大面积变慢。爬起来查看YARN界面发现某个大任务把队列资源全占了其他任务都在等。调整了容量调度器的最大容量和抢占策略让批处理任务在高峰期间不被单一任务阻塞。这个调整其实早该做纯粹是迁移时漏了队列规划导致的问题。第二次是HDFS节点掉线触发了NameNode切换当时业务侧直接报“文件系统无法使用”。排查发现客户端缓存的NameNode地址还是旧的切换后不知道更新。MRS的客户端配置一般会用nameservice而不是具体的Active节点地址但机房老作业里写死了IP。那次是全局搜索替换把所有代码和配置里的NameNode节点IP改成nameservice名称凌晨四点改完五点半跑批恢复。第三次是磁盘写满导致HDFS进入安全模式。根本原因是OBS转储任务配置的Overflow目录和数据目录在同一块盘上日志把磁盘撑爆了。处理方式是把临时目录和冷数据目录拆分到不同存储然后调低安全模式的自动触发阈值留出人工介入时间。那次之后我的巡检脚本里加了一项磁盘使用率Top10目录变化趋势超过阈值提前扩容清理不能等告警来叫你起床。5. 五大经典坑排查思路直接照着做5.1 从报错反推原因的分析框架运维终究是大量时间在排查问题与其大海捞针不如建立一个稳定的分析框架。我平时的排查路径是这样的先看任务日志最后1MB确认是应用报错还是框架报错再看YARN日志和集群总体状态排除资源瓶颈然后看组件目标日志比如HDFS的NameNode日志、HBase的HMaster日志最后看系统层面的指标CPU、IO、网络、GC。只要按这个顺序走80%的问题都能在半小时内定位到根因。使用频率最高的排查命令汇总一下# 查看YARN任务日志定位应用退出原因 yarn logs -applicationId application_id # 确认HDFS文件读写状态检查是否处于安全模式 hdfs dfsadmin -safemode get # 检查Kerberos票据情况确认认证是否过期 klist -e -k -t /etc/security/keytabs/xxx.keytab # 查看节点磁盘和文件系统挂载状态 df -h mount | grep dfs # 追踪组件进程的GC情况排查JVM层面的异常 jstat -gcutil pid 1000 20举一个实际例子某天凌晨Spark任务频繁报“Failed to connect to ... StandbyException”。按照上面的流程先看YARN日志发现TaskExecutor反复尝试连接NameNode失败再看HDFS状态发现当前NameNode是Standby但客户端配置的地址还是旧主节点。最终解决就是把客户端配置文件里的nameservice参数统一修正并重启客户端服务。如果不按框架来可能还在Spark参数里找半天白白浪费一两个小时。5.2 问题速查表这里整理一份我在MRS环境中长期使用的问题速查表每一条都是真金白银换来的经验。无论是自己排查还是分享给团队新手都很实用。问题现象常见根因排查方向解决建议作业提交到YARN后一直PENDING队列资源不足或队列配置错误查看YARN的队列状态和正在运行的任务数调大队列容量或优化单个任务资源配置访问HDFS报StandbyException客户端缓存了旧的NameNode地址检查客户端配置中的nameservice参数统一配置文件替换为nameservice并重启客户端Hive SQL跑着跑着卡住元数据连接池满或锁竞争查看HiveServer2日志和元数据库连接数调大连接池大小拆分耗时查询或加索引Kerberos票据过期导致认证失败keytab未续期或脚本未刷新票据执行klist检查票据时间在调度脚本中增加kinit刷新逻辑磁盘空间告警但找不到大文件隐藏目录或临时文件没清理用du按目录逐层排查配置目录配额每日巡检Top目录HBase RegionServer频繁宕机协处理器jar包缺失或配置冲突看RegionServer日志中的Error信息将协处理器统一放到指定目录并同步到所有RegionServer数据写入慢且伴随小文件激增文件块大小设置不合理或并发过高查看NameNode的Block数量和文件数调整写入批次大小使用CombineFile或小文件合并策略还有一个容易触发、但常常被忽略的坑是小文件问题。Hive、Spark写数据默认会按并行度产出文件如果每五分钟跑一个批一天下来几千个几十KB的小文件就堆在HDFS上。元数据膨胀、NameNode内存压力上升整个集群都会“变重”。处理方案是要么在写入时加参数做文件合并要么定时用工具做小文件归并绝不能让小文件躺在那里不管。5.3 给新环境留一个“应急逃生门”运维的规则永远是先恢复后诊断但恢复手段要提前准备好。我的习惯是在目标MRS集群之外保留一份经过验证的回滚脚本内容包含全量配置备份、关键组件重启顺序、数据校验指令。真出现短时间内无法解决的问题先按回滚脚本把服务恢复到上次稳态再细看原因。别把时间浪费在“当时为什么这么改”的悔恨上。我在实际使用中还发现每两周做一次配置整体备份非常有效不止是云管上导出的配置还包括客户端所有环境变量、properties文件、keytab清单、调度脚本的版本。这个备份塞在一个Git仓库里和集群状态有个简单的任一一对应关系。出问题的时候先把改动和备份做diff往往一秒钟就能定位到人为误操作。末尾补一句实在话从CDH到MRS这条迁移路走了整整一年多。网上很多人只讲架构、讲方案很少讲那些凌晨拉起来改配置的狼狈。我想说的是迁移方案做得再漂亮最终拼的还是运维细节——那个端口通不通、那个路径名一致不一致、那个keytab过期了没有。把这些细节管好迁移就成功了一大半。如果你现在正处在这个阶段希望这篇笔记能让你少瞪几次凌晨三点的天花板。