ARTICLE DETAIL

资讯详情

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

离线环境安装Ambari并替换Spark版本集成CarbonData实战

离线环境安装Ambari并替换Spark版本集成CarbonData实战 先说说这次任务的大背景。我们机房的这套集群是纯内网环境跟外网物理隔离Ambari 用的是 2.7.x 系列对应底层 HDP 3.1.x自带的 Spark2 版本锁死在 Ambari 源里的那一个。业务那边要上 CarbonData做明细数据的列式存储和高压缩比分析结果一看官方文档CarbonData 对 Spark 版本匹配非常苛刻Ambari 源里那个版本根本不在支持列表里。于是整套活儿拆成了三段先离线把 Ambari 装起来再在不破坏 Ambari 纳管的前提下偷偷换掉 Spark 版本最后把 CarbonData 的 jar 和配置塞进 Spark 并跑通验证。这套流程断断续续调了两周中途踩过不少坑网上能找到的资料要么只讲单点、要么版本对不上。我把自己落地的这套完整过程记下来给同样被困在内网、又要折腾大数据组件的朋友做个参考。不管是 Ambari 离线安装、Spark 二进制替换还是 CarbonData 集成每一步我都尽量写清楚“为什么这么做”而不只是给命令。1. 场景拆解与整体方案设计1.1 为什么离线环境还要折腾 Ambari很多人一听到离线环境就说“那你直接手动布 Hadoop 不就行了非要 Ambari 干嘛”这话有一定道理但只对了一半。手动部署 Hadoop、HDFS、YARN、Hive 确实可行可后面接业务的时候你会很痛苦组件状态看不到、日志散落一堆、某台节点挂了要手动找问题更别说加节点和滚动重启。Ambari 虽然是个重量级选手但它把监控、告警、服务启停、配置下发这些能力都集中了这在几十台机器的集群上是实打实的生产力。所以我们的选择是先花一天时间把 Ambari 离线源搞定后面所有组件都通过 Ambari 来管理。离线安装的唯一难点在初始化一旦源搭好安装 HDFSAmbari 的过程和在线基本一样。1.2 换 Spark 版本的核心矛盾Ambari 的 Spark2 服务有自己一整套目录结构、启动脚本和环境变量。默认情况下HDP 源里装好的 Spark 版本是固定的你没有直接在 Ambari UI 上“选一个其他版本”的入口。即便你去改spark.major.version、spark-version这类配置Ambari 也不会真去下载一个新版本给你它只会照着元数据写死的脚本去启动老版本。也就是说这个任务的核心矛盾是你既要保留 Ambari 的纳管能力又要让实际运行的 Spark 变成另一个版本。这就注定了不能走“纯 UI 操作”这条路必须用半手工方式去替换 Spark 二进制本体同时保证 Ambari 的启动脚本、目录检查、状态监控还能正常工作。1.3 CarbonData 与 Spark 的版本兼容性分析先解释一下 CarbonData 是什么。简单说它是一种 Hadoop 原生列式存储文件格式专门针对大数据量下的明细查询和聚合分析做了优化支持本地字典、支持压缩、支持索引下推对 BI 场景特别友好。但 CarbonData 不像 Parquet 那样跟 Spark 松耦合它的 Spark 集成模块对版本特别敏感用错版本就会出现NoSuchMethodError一类的兼容问题。我这次选了 CarbonData 1.5.0 配合 Spark 2.3.2。选择依据很简单看兼容矩阵CarbonData 版本适配 Spark 版本备注1.4.xSpark 2.3.x老项目用得比较多1.5.xSpark 2.3.x / 2.4.x当前比较稳的版本1.6.xSpark 2.4.x / 3.0.x需要 Spark 更新改动较大HDP 3.1.x 自带的 Hive、YARN 生态都还是基于 Spark 2.3 那套 API 跑的贸然升到 Spark 2.4 会引出很多隐性问题。所以我把目标版本锁定在 Spark 2.3.2既满足 CarbonData 支持范围又不会太激进导致 Ambari 脚本崩掉。2. 离线准备与 Ambari 安装实操2.1 离线资源清单与本地 yum 源搭建离线安装最怕的是装到一半发现缺包。我的习惯是在一台能联网的跳板机上先把所有需要的 tar 包全部拉下来整理固定目录后再拷进内网。以我们这套 HDP 3.1.4.0 Ambari 2.7.5 为例核心资源如下ambari-2.7.5.0-centos7.tar.gzHDP-3.1.4.0-315-centos7-rpm.tar.gzHDP-UTILS-1.1.0.22-centos7.tar.gzmysql-connector-javaAmbari 元数据库用Oracle JDK 8务必统一版本spark-2.3.2-bin-hadoop2.7.tgzapache-carbondata-1.5.0-bin-spark2.3.2.tgz内网选一台管理节点建本地源。我的习惯是/data/ambari-repo放 rpm 包用createrepo生成元数据再用 nginx 或 httpd 把目录发布出去。Ambari 的 repo 文件长这样# /etc/yum.repos.d/ambari.repo [ambari] nameambari baseurlhttp://内网管理节点IP/ambari-repo/ambari gpgcheck0 enabled1 [HDP] nameHDP baseurlhttp://内网管理节点IP/ambari-repo/HDP gpgcheck0 enabled1 [HDP-UTILS] nameHDP-UTILS baseurlhttp://内网管理节点IP/ambari-repo/HDP-UTILS gpgcheck0 enabled1注意如果 HDP 版本目录名带小版本号比如 HDP-3.1.4.0-315repo 路径要精确到具体目录不能模糊匹配。否则 yum 会一直提示找不到包。2.2 Ambari 安装过程中的注意点Ambari Server 本身安装比较简单yum install ambari-server之后执行ambari-server setup -s就能跑起来。但有几个前置条件没做好后面会让你非常难受所有节点统一 hostname 和/etc/hosts解析不能用 IP 到处凑合。管理节点到各数据节点的 SSH 免密必须提前配好Ambari agent 安装依赖它。各节点关闭 SELinux 和 firewalld否则注册 agent 时会出现莫名其妙的连接超时。所有节点时间必须同步最好配好 NTP时间漂移后 HDFS 和 YARN 都会报奇怪错误。Ambari Server 的元数据库我用的 MySQL建库时注意字符集要设成 utf8否则后面保存配置可能报错。实际执行中我踩过一次坑MySQL 密码里带了特殊字符结果ambari-server setup --jdbc-dbmysql一直失败换了个简单密码一次过。这是小事但很耽误时间。2.3 网络规划与部署架构建议离线环境下的部署架构不建议过度设计。我们这次是 1 个管理节点 2 个数据节点的最小规模Ambari Server 放在管理节点上数据节点的 Ambari Agent 注册后统一由 Server 分发安装 HDFS、YARN、Hive、Spark2 等组件。如果你面对的是上百台的集群建议单独拆一台机器跑 Ambari Server不要跟 NameNode 或 ResourceManager 混布否则后期组件多了之后 Server 本身会成为瓶颈。另外离线集群维护时务必把本地源放到独立的磁盘上避免跟 HDFS 数据盘抢 I/O。3. Spark 版本更换全过程3.1 更换前的完整备份替换 Spark 版本这事听着简单实际风险不小因为 Ambari 的 Spark2 服务通过/usr/hdp/current/spark2这个路径去调脚本、找配置。而/usr/hdp/current/spark2是一个软链接实际内容指向/usr/hdp/version/spark2目录。如果直接改链接Ambari 重新执行配置下发时很可能把链接覆盖回去。所以我采用了“替换目录内容保留目录结构”的方案。动手前先把原始环境完整备份下来# 在每一台 Spark 节点上执行 tar -czf /data/backup/spark2_orig_$(date %Y%m%d).tar.gz /usr/hdp/3.1.4.0-315/spark2 cp -a /usr/hdp/3.1.4.0-315/spark2/conf /data/backup/spark2_conf_orig同时把 Ambari 上 Spark2 服务的所有配置页面截图或者导出一份主要是spark-env.sh、spark-defaults.conf这两个文件的内容。等换完版本后很多自定义参数是要照着原来配置回填的。3.2 用 rsync 替换 Spark 二进制本体下载spark-2.3.2-bin-hadoop2.7.tgz在一台节点上解压后用 rsync 同步到 HDP 的 spark2 目录里。这里的关键是保留原conf目录、logs目录把 jars、bin、sbin、python、examples 等目录整体替换掉tar -xzf spark-2.3.2-bin-hadoop2.7.tgz -C /tmp rsync -av --delete \ --excludeconf \ --excludelogs \ --excludework \ /tmp/spark-2.3.2-bin-hadoop2.7/ \ /usr/hdp/3.1.4.0-315/spark2/有两点需要特别解释--delete参数很重要。Ambari 自带的 jars 目录里有大量旧版本的 jar如果不加这个参数新旧 jar 会混在一起Spark 启动时 ClassLoader 加载到哪个完全看运气典型的后果是NoSuchMethodError或者奇怪的序列化异常。加了--delete后目标目录里原有文件会被清掉再同步新文件进去。--excludeconf是为了保留 Ambari 在 conf 目录里生成的spark-env.sh、spark-defaults.conf。这些文件里有 Ambari 自动生成的 JDBC、Hive Metastore 连接信息全丢了会很麻烦。同步完成后把整个/usr/hdp/3.1.4.0-315/spark2目录再同步到其他所有 Spark 节点for host in node2 node3; do rsync -av --delete \ --excludelogs \ --excludework \ /usr/hdp/3.1.4.0-315/spark2/ \ $host:/usr/hdp/3.1.4.0-315/spark2/ done3.3 手动修正环境变量与核心配置Ambari 生成的spark-env.sh里有些变量是针对原版 HDP Spark 的比如SPARK_HOME、SPARK_DIST_CLASSPATH。换成官方二进制包后需要确认下面几个关键项# /usr/hdp/current/spark2/conf/spark-env.sh export JAVA_HOME/usr/java/jdk1.8.0_181 export HADOOP_HOME/usr/hdp/current/hadoop-client export HADOOP_CONF_DIR/usr/hdp/current/hadoop-client/conf export SPARK_HOME/usr/hdp/current/spark2 export SPARK_DIST_CLASSPATH$(hadoop classpath)SPARK_DIST_CLASSPATH这个变量尤其重要官方二进制包默认不知道自己要去哪找 Hadoop 的 classpath不设置的话spark-submit --master yarn会直接报Failed to connect to driver或者找不到 HDFS 文件系统。$(hadoop classpath)会把 HDFS、YARN、Hive 相关的 jar 全部带上实测下来最省事。3.4 通过 Ambari 重启并验证服务纳管配置文件改完后回到 Ambari UI找到 Spark2 服务执行 Restart。这里有一点小技巧先在 UI 上重启观察 Ambari 是否把 Spark HistoryServer 成功拉起。如果 Ambari 显示启动失败优先看/var/log/ambari-agent/ambari-agent.log里的脚本执行日志它会清清楚楚告诉你哪一步脚本返回了非 0。验证是否“真的换成功”并不难/usr/hdp/current/spark2/bin/spark-submit \ --class org.apache.spark.examples.SparkPi \ --master yarn \ --deploy-mode client \ --num-executors 2 \ --executor-memory 2g \ /usr/hdp/current/spark2/examples/jars/spark-examples_2.11-2.3.2.jar 100跑通之后再看一下 Spark HistoryServer 的 Web 页面能看到这个 Application 的日志就说明替换后的 Spark 已经正常接入了 YARN 和 HDFSAmbari 的监控也没有受影响。这一步我折腾了最久一开始图省事直接改了软链接结果 Ambari 一重启就把链接改回去了所以后来全部改成目录内容替换方案之后再也没有反复。4. CarbonData 集成与验证4.1 CarbonData 版本选型与发行包整理CarbonData 官方提供编译好的二进制发行包命名类似apache-carbondata-1.5.0-bin-spark2.3.2.tgz。如果你的集群 Spark 版本比较特殊也可以自己编译git clone https://github.com/apache/carbondata.git cd carbondata mvn clean package -DskipTests -Pspark-2.3 -Dspark.version2.3.2编译产物在assembly/target/scala-2.11/目录下会有carbondata_2.11-1.5.0-hadoop2.7.2-spark2.3.2.jar这类命名的 jar。我不推荐搞一堆零散 jar 到处塞直接用官方发行包里的jars目录最省心。4.2 部署 CarbonData 到 Spark 集群Core 配置就两条把 CarbonData 相关 jar 放进 Spark 的 jars 目录然后在spark-defaults.conf里声明扩展类。具体如下# 解压发行包 tar -xzf apache-carbondata-1.5.0-bin-spark2.3.2.tgz -C /opt cd /opt/apache-carbondata-1.5.0-bin-spark2.3.2 # 复制 jar 到 Spark 的 jars 目录 cp jars/carbondata_2.11-1.5.0-hadoop2.7.2-spark2.3.2.jar \ /usr/hdp/current/spark2/jars/然后编辑/usr/hdp/current/spark2/conf/spark-defaults.conf加上这两行spark.sql.extensions org.apache.spark.sql.CarbonExtensions spark.hadoop.datanode.heartbeat.recheck.interval10000第二行是我当时参考社区建议加的因为 CarbonData 对 DataNode 心跳感知比较敏感加大 recheck 间隔能减少偶发的文件一致性报错具体值可以按集群情况调。4.3 手工创建 Carbon 表并跑通读写部署完成后建议先用spark-sql做验证不要一上来就接 HiveServer2出问题不好定位。启动方式/usr/hdp/current/spark2/bin/spark-sql \ --master yarn \ --deploy-mode client \ --driver-memory 2g \ --num-executors 4 \ --executor-memory 4g进入 Spark SQL 命令行之后依次执行CREATE DATABASE IF NOT EXISTS test_carbon LOCATION /user/hive/warehouse/test_carbon.db; USE test_carbon; CREATE TABLE sales ( order_id BIGINT, user_id STRING, city STRING, amount DOUBLE, sale_date DATE ) STORED BY carbondata TBLPROPERTIES ( SORT_COLUMNScity,sale_date, table_blocksize128 ); INSERT INTO sales VALUES (1, u001, 北京, 100.0, 2024-01-01), (2, u002, 上海, 200.0, 2024-01-02), (3, u003, 北京, 150.0, 2024-01-03), (4, u004, 广州, 80.0, 2024-01-03); SELECT city, sum(amount) FROM sales GROUP BY city ORDER BY city;能正常出来聚合结果说明 CarbonData 集成已经成功。再用EXPLAIN看执行计划EXPLAIN SELECT city, sum(amount) FROM sales GROUP BY city;如果执行计划里出现了 CarbonData 相关的算子说明列式存储和索引确实生效了。第一次跑可能因为文件还没合并性能不明显多插一些数据再看差距比较直观。SORT_COLUMNS这个属性值得多说一句。它决定了数据按哪些列排序后再写入对过滤和下推影响很大。如果你经常按city过滤就把city放前面如果主要跑时间段的聚合就把sale_date放前面。选对了查询性能提升是肉眼可见的选错了也不至于出错但 I/O 会浪费不少。4.4 与 YARN 和 History Server 的配合验证CarbonData 集成完后还要确认它在 YARN cluster 模式下也能正常工作。因为很多线上任务不是拿spark-sql在客户端敲而是通过spark-submit提交到 YARN 上跑的。我写了一个简单的 Scala 测试任务用--master yarn --deploy-mode cluster提交验证两个点Application 能在 YARN 上正常启动并跑完不报 ClassNotFound。Spark HistoryServer 页面能看到完整的 Executor 和 Job 列表说明日志收集正常。这里要注意 jar 传递问题。如果你是在spark-submit的--jars里指定 CarbonData那每个任务都要带一次很麻烦。放进/usr/hdp/current/spark2/jars/目录的最大好处就是所有任务默认加载不用额外传参。5. 常见问题与排查实录5.1 问题速查表我在这次离线安装、换版本、集成 CarbonData 的过程中把遇到的问题整理成了一张速查表方便后面遇到同样问题的同事直接抄答案现象可能原因处理办法spark-submit --master yarn报Failed to connect to driverSPARK_DIST_CLASSPATH没设置在spark-env.sh加export SPARK_DIST_CLASSPATH$(hadoop classpath)Spark 服务在 Ambari 上反复启动失败新旧 jars 混用用rsync --delete清掉旧 jars 后重新同步ClassNotFoundException: org.apache.spark.sql.CarbonExtensionsCarbonData jar 没放进 Spark jars 目录把 CarbonData 发包的 jar 复制到/usr/hdp/current/spark2/jars/No FileSystem for scheme: carbonCarbonData jar 缺失或版本不匹配检查 CarbonData 版本与 Spark 版本是否匹配重新部署新建 Carbon 表后查询不走列式优化SORT_COLUMNS没设置或选择的排序列不对重新设计排序列把过滤频繁的列放在前面Ambari 重启 Spark2 后版本回退到旧版直接改了软链接被 Ambari 覆盖改用 rsync 替换目录内容保留目录结构5.2 几个比较有代表性的排查案例第一个案例是我刚开始做替换时犯的错误。当时图快直接ln -sfn /tmp/spark-xxx /usr/hdp/current/spark2把整个软链接指向了新目录。测试的时候一切正常结果 Ambari 里点了一个“Restart Spark2”agent 重新跑配置下发脚本直接把软链接掰回了 HDP 自带的版本。排查了很久才发现是 Ambari 每次都会重新创建软链接。后来所有节点都改成 rsync 替换版本目录内容这个问题才算彻底根治。第二个案例是 CarbonData 和 Hive 依赖冲突。HDP 3.1 环境里 Hive 的 jar 数量和版本特别多CarbonData 某些类会和 Hive 的某些旧类冲起来。典型表现是创建表时报NoSuchMethodError但 jar 明明都在。我的处理经验是优先用官方发行包提供的 shaded jar而不是自己把多个 jar 拆分出来部署。shaded jar 把依赖重新定位了包名和 Hive 的类不再冲突。第三个案例跟 HiveServer2 有关。因为 HDP 3.1 默认带了 Hive Warehouse ConnectorHiveServer2 在启动时会扫描 Spark 相关的 jar。如果你把 CarbonData jar 放进去时没有同步到 HiveServer2 所在节点的 classpathHiveServer2 可能因为拿不到 CarbonData 的类而启动变慢甚至失败。后面我干脆把 HiveServer2 和 Spark2 的节点分开处理只在与 Spark 相关的客户端环境里加载 CarbonDataHiveServer2 那边不做额外修改问题就没了。5.3 一点排障心法离线环境排障最痛苦的是查资料不方便。我的建议是把日志当作第一依据不要靠猜。遇到 Spark 启动失败先依次看这几个日志yarn.log、spark.log、ambari-agent.log如果发现异常堆栈里有类名就去jars目录里搜索这个类到底在哪个 jar 里for jar in /usr/hdp/current/spark2/jars/*.jar; do unzip -l $jar 2/dev/null | grep -q org/apache/spark/sql/CarbonExtensions.class echo $jar done这个方法帮我定位了很多“以为存在但实际没有正确部署”的问题。结尾这次折腾下来我个人最深的体会是离线环境里做版本替换安全和稳定永远排在“省事”前面。替换 Spark 这种核心组件至少要预留整套环境的回滚方案我就是所有节点先备份、再换一台测试、最后全量同步才没有把生产集群搞挂。最后再分享一个小技巧如果后续还要升级或换其他组件建议写一个脚本把 HDP 版本号、Spark 版本号、CarbonData 版本号都定义成环境变量所有命令统一引用。我一开始就是到处写死结果版本一换改配置改到怀疑人生。把这些版本号收敛到一个env.sh里之后每次升级只需要改一处集群里所有节点的同步操作都能一键跑完。这套思路同样适用于你后面要是再碰到其他组件和换版本相关的集成任务。
返回列表