
先交代一下背景我这边接了一个机房内网的大数据环境搭建任务技术栈指定Ambari HDP但Ambari自带的Spark版本偏旧业务方要求换成更新的Spark随后还要把Carbondata集成进去最麻烦的是整个环境不能访问外网所有软件包都得提前准备好再离线安装。整套流程跑下来最折腾的其实不是Ambari安装本身而是版本替换和组件集成阶段的那些“隐藏坑”。这篇把整个操作链路、版本匹配思路、踩过的坑和最终验证方式完整梳理一遍给后面要做类似离线大数据环境的朋友一个参考。1. 项目核心任务与整体架构设计1.1 为什么要离线安装Ambari很多机房环境是物理隔离的不能访问公网Yum源也不能直接从Maven中央仓库拉依赖。Ambari本身的架构是Server Agent模式Server端负责集群状态管理、配置下发和服务部署Agent装在每台机器上。如果在线安装yum会自动拉取Ambari和HDP的RPM包离线环境就必须把这些RPM包提前准备好放到内网的一台机器上通过HTTP服务模拟一个本地Yum源。这里有一个容易忽略的点离线安装不只是准备好Ambari的RPM就行HDP的主服务包、HDP-UTILS工具包、以及它们依赖的系统级软件比如mysql-connector-java、ntp、python相关的包都得一并准备好。我这次提前准备好了一套完整的RPM目录涵盖Ambari 2.7.x、HDP 3.1.x和HDP-UTILS还有Oracle JDK 8的安装包。如果漏了任何依赖安装过程中会直接报“依赖缺失”错误排查起来很头疼。离线方式还有个好处所有机器从同一个本地源安装版本完全可控不会出现“这台机器装的是Ambari 2.7.4另一台却是2.7.5”这种版本漂移问题。对于后续要做的Spark版本替换和Carbondata集成这种一致性非常关键。1.2 Spark版本更换的两种主流路线Ambari管理下的Spark服务和普通手动部署的Spark集群有一点不同Ambari会通过配置模板和目录约定来管理Spark的启动脚本、配置文件和Jar包位置。想在Ambari里换Spark版本通常有两条路一条路是修改Ambari的Stack定义也就是重新定义Spark2服务的包版本信息。这条路的优点是可以让Ambari UI里显示的版本号同步改变缺点是需要修改/var/lib/ambari-server/resources/stacks/HDP/3.1/services/SPARK2下的目录结构、metainfo.xml和相关的Python脚本操作繁琐而且Ambari版本升级时改动可能会丢失。另一条路是保留Ambari的Spark2服务不动直接替换Spark2安装目录下的二进制文件。也就是把新版本的Spark解压后覆盖到旧Spark所在的目录然后清理旧Jar包调整配置文件重启相关服务。这条路的实现成本低服务监控和启停仍然由Ambari管理风险相对可控。我这次选的就是这条路线实际使用中唯一要注意的是Ambari在重启服务时可能会用模板重新生成配置文件所以不能只改业务目录里的配置还要把Ambari页面对应的配置项也同步修改。1.3 Carbondata在技术栈中的位置Carbondata是华为开源的一种列式存储格式定位是“大数据领域的一种高效列式存储方案”在Spark生态里可以当做一个数据源来使用。它和Parquet、ORC是同一类东西但Carbondata更强调数据文件的自索引、多维索引和增量更新能力在某些过滤和聚合场景下性能比Parquet更稳定。Carbondata并不是一个独立运行的服务它是以Spark插件的形式存在的。你在Spark SQL里执行建表语句时指定表的存储格式为carbondata后续的写入和查询就会自动走Carbondata的存储引擎。所以集成Carbondata的核心工作就是把Carbondata的Jar包放进Spark的Classpath然后在spark-defaults.conf里注册扩展类。理解了这一点后面配置时就不会一头雾水。2. 离线环境准备与Ambari集群落地2.1 离线Yum仓库搭建离线源是整个安装的“地基”没有它后面全都跑不起来。我的做法是在一台可以内网访问的机器上安装httpd和createrepo然后把准备好的RPM包按目录放好逐个生成repodata。# 安装HTTP服务和createrepo工具 yum install httpd createrepo -y # 假设RPM包已经拷贝到/data/repo目录下目录结构大致如下 # /data/repo/ambari # /data/repo/HDP # /data/repo/HDP-UTILS # 将仓库目录软链到HTTP服务的根目录 ln -s /data/repo /var/www/html/repo # 启动HTTP服务并设置开机自启 systemctl start httpd systemctl enable httpd # 为每个仓库目录生成元数据 createrepo /var/www/html/repo/ambari createrepo /var/www/html/repo/HDP createrepo /var/www/html/repo/HDP-UTILS然后在内网所有机器上配置Yum源文件。以Ambari为例/etc/yum.repos.d/ambari.repo内容是这样[ambari] nameambari baseurlhttp://本地源服务器IP/repo/ambari gpgcheck0 enabled1HDP和HDP-UTILS也照着写只是baseurl换成对应目录。这里把gpgcheck设为0内网环境没必要去验证签名能减少很多麻烦。2.2 Ambari Server与Agent安装源配好之后安装Amabri Server本身很快# 在Server节点上执行 yum install ambari-server -y # 初始化JAVA环境如果机器上已经装了Oracle JDK 8可以直接选“手动指定Java路径” ambari-server setup # 启动Ambari Server ambari-server start我这次在setup阶段用的是默认嵌入式的PostgreSQL数据库没有额外装MySQL。如果业务要求必须用MySQL存元数据记得先把mysql-connector-java的Jar包放到/usr/share/java里不然setup时驱动检测过不去这是很多人在线装Ambari时忽略的细节。Agent的安装方式和Server类似每台要加入集群的机器上都执行yum install ambari-agent -y然后修改每台机器上的/etc/ambari-agent/conf/ambari-agent.ini把hostname改成Ambari Server的地址[server] hostnameambari-server.example.com改完之后启动systemctl start ambari-agent systemctl enable ambari-agent接下来就是打开Ambari的Web界面通过向导安装HDP集群。这个向导会引导你指定集群名称、选择服务HDFS、YARN、MapReduce2、Spark2、Hive、Livy等、分配节点角色最后进行安装和启动。如果你的服务器数量不多建议在向导页面里把所有服务的Master和Client角色都规划好不要后面再改。我这次是三台机器的小集群NameNode、ResourceManager、Ambari Server放在同一台另外两台做Worker节点Spark2的History Server放在第一台。2.3 集群组件安装初体验Ambari向导里安装Spark2服务时它会自动帮你建好Spark2的目录并在/usr/hdp/current下生成spark2-client的符号链接。安装完成之后可以在心跳机器上执行如下命令确认Spark版本/usr/hdp/current/spark2-client/bin/spark-submit --version正常情况下输出的是Ambari对应的HDP版本里内置的Spark版本。以我这次的环境为例HDP 3.1.4对应的是Spark 2.3.2。这个版本对于Carbondata来说可以用但如果业务方希望用更新版本比如Spark 2.4.x就需要继续做版本替换。此时先别急着替换我建议在Ambari的Spark2服务页面上把History Server的启动参数、YARN集成相关的配置都走一遍确认Spark2在Ambari下能正常提交任务。这样后续做替换时如果出现问题可以明确排除“基础环境没装好”这个因素。3. 更换Spark版本的核心实操3.1 替换二进制目录的完整步骤Ambari管理下的Spark2虽然配置文件由Ambari接管但实际的Jar包和二进制文件都放在HDP版本号对应的目录下。对我这个环境来说目录是/usr/hdp/3.1.4.0-315/spark2。替换的核心逻辑很简单下载目标版本的Spark例如Spark 2.4.7选择Hadoop 2.7对应的编译包解压然后把内容覆盖进去。实际操作前先把Ambari上的Spark2服务停掉包括Spark History Server和Livy Server避免有进程占用文件句柄。# 停掉相关服务后备份原始Spark2目录 cp -r /usr/hdp/3.1.4.0-315/spark2 /usr/hdp/3.1.4.0-315/spark2.bak.20240101 # 解压新版本的Spark包 tar -zxvf spark-2.4.7-bin-hadoop2.7.tgz # 将新版本内容同步到Ambari管理的Spark2目录 # 这里用rsync带上--delete参数确保旧目录中被移除的Jar包也能被清理 rsync -av --delete spark-2.4.7-bin-hadoop2.7/ /usr/hdp/3.1.4.0-315/spark2/这里有一个很关键的细节不要只把新版本的Spark解压到一个全新目录然后去改Ambari的配置指向新目录。因为Ambari的脚本和监控逻辑大量依赖/usr/hdp/current/spark2-client这个符号链接你改了指向Ambari重启服务时会按原配置把路径“纠正”回来反而导致服务起不来。直接在原目录上覆盖是最稳妥的做法。3.2 处理Ambari对配置的“管理覆盖”问题如果你只替换了二进制目录重启Spark History Server的时候就会发现配置被“重置”了。原因是Ambari会把页面上填写的配置项渲染成/usr/hdp/current/spark2-client/conf下的文件启动服务前用它自己的模板覆盖原文件。所以替换版本之后要同步去Ambari UI上检查Spark2服务的配置。重点看两个地方一个是spark-defaults.confAmbari UI上的路径是“Spark2 - Configs - Advanced spark2-defaults”。把业务需要的关键参数在这里维护比如spark.history.fs.logDirectory、spark.eventLog.enabled、spark.yarn.jars等。否则你改成新版Spark之后History Server可能找不到事件日志目录或者YARN提交时找不到Spark的Jar包。另一个是spark-env.sh模板路径是“Spark2 - Configs - Advanced spark2-env”。如果你有自定义的JAVA_HOME或者SPARK_HOME变量需要在这里修改而不是直接改节点上的spark-env.sh文件。我这次就在这上面栽了个跟头直接改了节点上的spark-defaults.conf加了一段driver内存参数结果Ambari一重启服务配置就被覆盖回去任务提交后瞬间OOM。后来改成在Ambari UI上改配置保存并“重启所需组件”之后问题才解决。3.3 升级后的服务重启与验证替换完二进制并同步配置后通过Ambari UI依次重启HDFS、YARN、Spark2 History Server和Livy Server。这里有个顺序问题建议先重启HDFS和YARN让NameNode和ResourceManager把节点状态恢复正常再启动Spark2相关服务。启动完成后验证方案分三步走第一步运行spark-submit --version确认版本号/usr/hdp/current/spark2-client/bin/spark-submit --version输出应该显示Spark 2.4.7。第二步提交一个简单的Spark Pi任务验证YARN集成是否正常/usr/hdp/current/spark2-client/bin/spark-submit \ --class org.apache.spark.examples.SparkPi \ --master yarn \ --deploy-mode cluster \ /usr/hdp/3.1.4.0-315/spark2/examples/jars/spark-examples_2.11-2.4.7.jar \ 10如果任务能在YARN上以cluster模式跑完说明Spark替换后的基础提交链路是通的。第三步去Spark History Server的Web界面看一眼确认历史任务能够正常加载事件日志。这一步经常被忽略但如果eventLog配置有问题任务虽然能跑完历史界面却是一片空白后面排查问题会非常痛苦。至此Spark版本替换就算完成了。4. Carbondata集成流程与参数配置4.1 版本匹配Carbondata与Spark的兼容关系Carbondata从底层依赖Spark的内部API所以版本匹配非常重要。我用的是Spark 2.4.7所以选择Carbondata 1.6.x版本这个系列是针对Spark 2.4.x编译的。如果还在用Spark 2.3.x就应该选Carbondata 1.5.x不能混用。很多人容易忽略的是Scala版本。Spark 2.4.x是基于Scala 2.11编译的所以Carbondata也要选_2.11的版本。假如你下载的Carbondata包是_2.12后缀运行时会直接报NoSuchMethodError或者类版本不兼容的异常排查起来非常闹心。我这里的做法是在准备离线包时把Carbondata的release包例如carbondata_2.11-1.6.0.jar提前下载好随其他软件包一起拷到内网机器上。4.2 环境变量与Spark扩展注册Carbondata集成到Spark有两种方式一种是把Carbondata的Jar文件放到Spark的jars目录里另一种是通过--jars参数在提交任务时临时附加。对于Ambari管理的集群我更推荐前者因为这样可以保证所有通过spark-submit、Spark SQL、Livy提交的任务都能自动加载Carbondata。具体操作如下# 将Carbondata的核心Jar复制到Spark的jars目录 cp carbondata_2.11-1.6.0.jar /usr/hdp/3.1.4.0-315/spark2/jars/ # 如果不想污染全量jar目录也可以建一个独立的ext目录然后在SPARK_CLASSPATH中引用 mkdir -p /usr/hdp/3.1.4.0-315/spark2/carbondata-lib cp carbondata_2.11-1.6.0.jar /usr/hdp/3.1.4.0-315/spark2/carbondata-lib/然后修改spark-defaults.conf加入Carbondata的扩展类。这一步是核心Carbondata的SQL解析和优化器扩展都靠这个类来触发spark.sql.extensions org.apache.spark.sql.CarbonExtensions spark.carbondata.storePath hdfs://nameservice1/user/carbondata/store spark.carbondata.metaPath hdfs://nameservice1/user/carbondata/metastorePath和metaPath是两个基础路径。storePath是实际的数据文件存放目录metaPath是元数据目录。我建议把它们都放在HDFS上这样集群里任何节点上的Spark任务都能访问到数据。在Ambari UI上这些配置要填到“Advanced spark2-defaults”的自定义项里然后在页面上保存并重启Spark2服务。如果不走Ambari UI直接在节点上修改spark-defaults.conf服务重启后大概率会被覆盖。4.3 建表、导入与查询验证配置完成后用spark-sql进交互式命令行验证。先创建一张Carbondata表CREATE DATABASE IF NOT EXISTS carbon_test; USE carbon_test; CREATE TABLE IF NOT EXISTS student ( id INT, name STRING, age INT ) USING carbondata;在Spark 2.4 Carbondata 1.6.x这个组合下推荐使用USING carbondata的方式建表语法清晰写作STORED AS carbondata的形式在旧版兼容性更好但新版也能识别。然后插入几条数据INSERT INTO student VALUES (1, Alice, 20), (2, Bob, 21), (3, Charlie, 22);再查一下SELECT * FROM student WHERE age 20;如果能够正常返回结果说明Carbondata已经工作。这里多说一句Carbondata列式存储适合大数据量下的过滤和聚合小数据量看不出性能优势但不影响我们验证功能链路。真正测试存储性能的时候建议用TPC-H或者TPC-DS的数据集去压测。4.4 与Hive元数据协同的几个细节Ambari集群里通常会同时装Hive Metastore那么Carbondata的表元数据可以和Hive Metastore打通。这样你用HiveServer2或者Spark SQL查表时元数据是一致的。要在Carbondata里启用Hive Metastore需确保Hive相关的Jar包已经在Spark的Classpath里。Ambari默认已经把Hive的lib目录加入到了SPARK_CLASSPATH中所以一般不需要额外配置。如果遇到表元数据只在当前SparkSession内可见、换一个会话就消失的问题大概率是元数据存储方式没配对确认spark.sql.hive.metastore.version和对应的metastore jar版本是否匹配。另外在纯离线环境里HDFS的UserGroupInformation认证也可能成为一个问题。如果集群开启了KerberosCarbondata的读写路径会涉及大量HDFS文件操作需要确保提交Spark任务的用户对storePath和metaPath有读写权限。我这次环境没有开Kerberos但如果你的集群开了建议提前用kinit认证并在启动Spark服务时加上合适的principal和keytab配置。5. 常见问题与排查实录5.1 问题速查表从Ambari离线安装到Spark版本替换再到Carbondata集成这一路下来遇到的问题不少整理一个速查表方便后续对照排查。问题现象可能原因处理方式Amabri Server启动失败日志提示找不到JDBC驱动MySQL驱动未放到/usr/share/java下载对应版本mysql-connector-java改名后放进去重新执行ambari-server setupambari-agent启动后Server界面显示主机心跳丢失agent上的hostname配置错误或网络不通检查/etc/ambari-agent/conf/ambari-agent.ini确认hostname指向Server地址再检查防火墙spark-submit --version输出旧版本号替换目录后没有重启History Server或者current符号链接没有更新确认/usr/hdp/current/spark2-client指向正确重启Spark2服务Spark任务提交到YARN后一直处于ACCEPTED状态YARN没有启用或识别Spark Shuffle服务检查YARN的aux-services配置确认spark_shuffle对应服务类存在任务启动时出现NoClassDefFoundError卡在SparkCommons类上Spark目录下有多个版本的Jar包冲突清理Spark2目录里旧版本的重复Jar包重启用rsync --delete同步Carbondata建表报SparkUnsupportedOperationExceptionCarbondata版本和Spark版本不匹配升级或降级Carbondata版本确保对应Spark的编译版本Carbondata创建表成功后新会话无法看到表元数据存到了默认本地位置没有和Hive Metastore打通配置Hive Metastore相关参数确认hive-site.xml可以正常加载这张表只是记录了我遇到的高频问题实际环境中可能还会有更多奇怪的现象。遇到问题先别急优先去/var/log/ambari-server和/var/log/ambari-agent里看服务端日志再到/usr/hdp/3.1.4.0-315/spark2/logs里看Spark的日志定位的速度会快很多。5.2 一个典型案例Store路径与Meta路径权限不对我在第一次跑CarbonData的INSERT语句时遇到过Permission denied异常。原因很直接我的storePath和metaPath配置的是HDFS路径但Spark任务提交时使用的用户对那个路径没有写权限。解决方式很简单先在HDFS上创建好目录并授权然后再跑任务sudo -u hdfs hdfs dfs -mkdir -p /user/carbondata/store sudo -u hdfs hdfs dfs -mkdir -p /user/carbondata/meta sudo -u hdfs hdfs dfs -chown -R spark:spark /user/carbondata如果你的Spark任务以yarn用户提交就把chown的对象换成对应的用户。这个坑之所以常见是因为很多人只关注Jar包和扩展配置忽略了底层文件系统权限。5.3 运维这种混合集成环境的三条经验整个流程走下来我个人觉得有三条经验值得记录。第一条Ambari这类集群管理工具会主动管理配置文件直接改业务节点上的配置只能解决“一次性问题”服务重启就会被打回原形。所有配置必须通过Ambari UI或API去改否则排查配置丢失的问题会耗费大量时间。第二条Spark版本替换时尽量在业务低峰期操作并且做好备份。我建议不要在旧版本Spark上跑关键任务的同时做替换即便有备份一旦Jar包冲突导致节点上多个任务挂掉恢复现场的成本远高于等待一个维护窗口。第三条Carbondata集成遇到奇怪错误时优先检查版本组合。Spark、Scala、Hadoop、Carbondata这四者缺一不可任何一层的版本错位都会变成运行时的“玄学问题”。建议在一开始就把版本匹配矩阵记录下来贴在运维笔记里后续排查能省很多事。最后再说一个小细节Carbondata集成成功后如果想要让HiveServer2里的Sessions也能自动识别Carbondata记得把spark.sql.extensions配置同步到Hive on Spark的配置里。我这次就是因为只改了Spark2的spark-defaults导致通过HiveServer2提交SQL时依然用不了Carbondata后来在Ambari的Hive服务配置里补上同样参数才解决。这种“配置不同步”的问题在混合组件环境下尤其容易发生值得多看几眼。