1. 环境整体设计与版本矩阵梳理CDH6.2 装 Spark 集群基础环境这件事说难不难说简单也真不简单。我见过太多人卡在明明机器都能 ping 通、端口都开着就是 spark-submit 报类找不到这种问题上折腾一整天最后发现是某个节点的 JDK 小版本不一致。所以这篇内容我不打算从点下一步讲起而是先把版本矩阵、节点规划、资源估算这些看起来不性感但决定成败的东西捋清楚再往下落到实操命令。适合刚接手大数据平台搭建的同学也适合那些已经装过一版、但集群跑着跑着总出怪问题的老手回头对照。CDH 6.2 这个发行版对 Spark 的绑定关系是死的它自带的是Spark 2.4.0并且是基于 Hadoop 3.0.0 编译的。这一点必须记住因为后面所有关于 Hadoop 版本、Scala 版本、Guava 依赖的判断都从这里出发。你从外部拿一个 Spark 2.4.5 的包塞进去理论上能跑但和 CMCloudera Manager托管的配置分发机制是两套逻辑出问题排查起来会让你怀疑人生。我的建议是服务端用发行版自带的 parcel客户端工具版本与之严格对齐不要自作聪明地顺手升个级。1.1 为什么必须先锁版本再动手很多人装集群的习惯是先把系统装好再去官网下最新包这个顺序在 CDH 场景下是反的。原因有三层。第一层CDH 6.2 依赖JDK 8而且对 8u 的具体小版本有要求官方推荐 8u141 以上实测 8u181、8u202 都比较稳8u302 之后的某些版本在 Kerberos 相关类上有行为变化非安全集群感知不到一旦你后面开 Kerberos 就会踩。第二层Hadoop 3.0.0 的内部依赖里 Guava 是 11.0.2而 Spark 2.4.0 编译时用的是 Guava 14CM 会在 classpath 顺序上做处理但如果你自己往/opt/cloudera/parcels/CDH/lib/spark/jars里手动丢 jar 包很容易把顺序搞乱症状就是NoSuchMethodError或者IllegalAccessError。第三层Python 版本Spark 2.4 同时支持 Python 2.7 和 3.4但 PySpark 在 Python 3.7 上和 2.4.0 搭配有已知的 pickle 兼容问题稳妥做法是节点上装 Python 3.6或者干脆只跑 Scala/Java 作业、PySpark 单独指定PYSPARK_PYTHON。把这三个版本钉死之后写一份文档记录下来所有节点严格照抄。我个人的习惯是在每台机器的/etc/version-lock.md里写清楚OS 版本、JDK 完整版本号、Python 版本、CDH parcel 版本、MySQL 驱动版本。后面任何人扩容机器照着这份文件装能省掉 80% 的新节点行为诡异问题。1.2 节点规划与内存资源估算节点规划这件事网上很多文章直接甩一句三台起步但不告诉你为什么、怎么分角色。我按一套典型的3 节点测试/小生产集群来讲规格是 16 核 / 64G 内存 / 2TB 数据盘 500G 系统盘。角色分配上节点主机名承担的守护进程node01cdh-master-01NameNode(Active)、ResourceManager、CM Server、CM DB、ZooKeeper、Spark History Servernode02cdh-worker-02DataNode、NodeManager、ZooKeeper、CM Agent、JournalNodenode03cdh-worker-03DataNode、NodeManager、ZooKeeper、CM Agent、JournalNode注意这里我把CM Server 和元数据库放在 master这是测试环境的省事做法生产环境一定要拆开CM 的监控数据写库很凶和 NameNode 抢 IO 会让整个集群的写入延迟上去。同理Spark History Server 也不应该和 NameNode 同机因为它要读大量的 event log 文件属于典型的重读轻写负载跟 NameNode 的元数据操作抢页缓存。内存怎么算以 64G 的机器为例OS 和基础进程留8GCM Agent 和监控留2G剩下的给 YARN。NodeManager 可用内存我一般配yarn.nodemanager.resource.memory-mb 4915248G留 6G 缓冲。核数上yarn.nodemanager.resource.cpu-vcores 14留 2 核给系统和其他守护进程。然后反推 Spark executor单个 executor 内存 12Goverhead 按 10% 算约 1.2G加上 container 额外开销实际占 YARN 约 13.5G。48G / 13.5G ≈ 3.5取 3 个 executor/节点每个 4 核正好 12 核留 2 核余量。这套数字不是拍脑袋是先用默认值跑起来再看监控调的结果。新手常犯的错是把executor.memory设成 40G 想一次吃完结果 GC 停顿时间直接飙到秒级因为堆太大、老年代回收成本指数上升。经验值单 executor 堆内存不建议超过 24G超过就该拆成多个 executor。1.3 系统层的前置条件清单装之前有几件事必须做完缺一件后面都会返工。清单如下主机名规范化且hostname -f能正确返回 FQDN不能只返回短名。CM 的 Agent 心跳是靠 FQDN 注册的短名会导致 Agent 已存在但主机名不匹配。所有节点的/etc/hosts内容完全一致包含全部节点的 IP 与 FQDN 映射。关闭 SELinux/etc/selinux/config改为disabled并重启生效。这一条不是可选项SELinux 的 enforcing 模式会让 DataNode 写盘直接 Permission denied日志里还看不出原因。时间同步。三节点集群如果不做 NTPKerberos 开了之后 ticket 会直接失效即使不开ZooKeeper 的 session 也会莫名超时。用 chronyd 就行。关闭透明大页THP。Hadoop 全家桶对这个都很敏感echo never /sys/kernel/mm/transparent_hugepage/enabled并写进 rc.local 或 systemd unit。vm.swappiness 调低到 1 甚至 0避免内存压力下把 JVM 堆换出到磁盘。这些操作单独看每条都很小但它们是集群能不能长期稳定跑的地基。我在一个项目上见过 THP 没关导致 NodeManager 在压力测试时出现几十秒的假死最后靠sar和vmstat一路追才定位到。2. 基础环境落地实操这一节是纯手工活命令都是可以直接抄的但我建议你抄之前理解每条在干什么因为不同发行版CentOS 7 和 Ubuntu 20.04的细节不一样我按 CentOS 7 系写其他系统自行转换。整个流程按单节点配置 → 批量分发 → 统一验证的顺序走比一台台手敲可靠得多。2.1 主机名、hosts 与免密登录先在三台机器上分别执行hostnamectl set-hostname cdh-master-01 # 另两台分别改然后编辑/etc/hosts三台保持完全一致10.10.20.11 cdh-master-01 10.10.20.12 cdh-worker-02 10.10.20.13 cdh-worker-03改完ping cdh-worker-02和hostname -f都要能通、能返回完整域名。这一步有个坑某些云主机的cloud-init会在重启后重写 hosts把 127.0.1.1 那行加回去建议把manage_etc_hosts关掉。免密登录方面CM 的安装向导要求提供 root 或具备 sudo 权限的账号。测试环境我直接用 root简化流程生产环境建议建一个cdhadmin用户配置NOPASSWD的 sudoers 条目并在 CM 向导里指定这个账号。免密做法就是 master 上生成密钥对ssh-copy-id分发到三台包括 master 自己然后ssh cdh-worker-02 date验证不需要输入密码。提示CM 向导会用你提供的账号去各节点执行命令并分发 parcel账号必须具备无交互 sudo 权限否则会在检查主机阶段报 SSH 相关错误而且错误信息很含糊容易误判成网络问题。2.2 JDK 与 Python 的安装细节JDK 我推荐用系统包管理器装 OpenJDK 8或者手工解压 Oracle JDK 8 到/usr/java/jdk1.8.0_202。两种方式都可以关键是三台路径一致。CM 的 Java 路径配置在主机 → 所有主机 → Java 主目录里一旦某台路径不同那台上的所有角色都启动不了。mkdir -p /usr/java tar -zxf jdk-8u202-linux-x64.tar.gz -C /usr/java/ echo export JAVA_HOME/usr/java/jdk1.8.0_202 /etc/profile.d/java.sh echo export PATH$JAVA_HOME/bin:$PATH /etc/profile.d/java.sh source /etc/profile.d/java.sh java -version # 确认输出为 1.8.0_202Python 这块先确认python -V。CentOS 7 自带 2.7.5Spark 2.4 能跑但很多新库已经不兼容 Py2。我的做法是装 Python 3.6 并保留系统 Python 不动然后在 CM 里给 Spark 服务配置PYSPARK_PYTHON/usr/bin/python3.6。注意不要动系统的 python 软链CentOS 的 yum 就是靠 python2 跑的改坏了连 yum 都用不了。顺手把 MySQL 客户端和 JDBC 驱动也准备好后面配元数据库要用yum install -y mysql mysql-connector-java驱动 jar 的位置很关键CM 有个机制叫JDBC 驱动目录默认在/usr/share/java/把mysql-connector-java-5.1.47.jar放进去再在 CM 里配置数据库驱动路径否则初始化元数据库时会报No suitable driver。2.3 内核参数与文件句柄调整Hadoop 家族对文件句柄的需求非常大一个 DataNode 每个块一个文件句柄几百万块就是几百万个 fd。默认 1024 完全不够。修改/etc/security/limits.conf* soft nofile 65536 * hard nofile 65536 * soft nproc 65536 * hard nproc 65536同时在/etc/systemd/system.conf里也要调DefaultLimitNOFILE因为 CentOS 7 之后服务是通过 systemd 拉起的limits.conf 对 systemd 管理的服务不生效这是个非常隐蔽的坑——你明明改了 limits.conf用su -进去看ulimit -n是 65536但 DataNode 进程的 fd 上限还是 1024因为它是 systemd 启动的。内核参数方面编辑/etc/sysctl.confvm.swappiness 1 net.core.somaxconn 1024 net.ipv4.tcp_max_syn_backlog 1024 vm.overcommit_memory 1 kernel.pid_max 4194303vm.overcommit_memory1这条是因为 JVM 启动时会预留较大虚拟内存默认策略在某些内存紧张的情况下会让 HBase 或 Spark 的 executor 启动失败。改完sysctl -p生效。注意kernel.pid_max调大是为了避免高并发时出现fork: Cannot allocate memory这在执行大量 spark-submit 或 MapReduce 小作业时很常见症状是进程数一到几千就起不来。2.4 防火墙、SELinux 与时间同步测试集群里我直接systemctl stop firewalld systemctl disable firewalld减少排查干扰。生产环境不建议关而是精确放行 CM 用到的端口段不过那是另一个话题。SELinux 前面提过要关这里再补一句改完配置必须重启setenforce 0只是临时生效重启后又变回 enforcing然后 DataNode 突然起不来你会以为是配置改错了。验证方式getenforce输出Disabled才算真的关掉。时间同步用 chronyyum install -y chrony systemctl enable chronyd systemctl start chronyd chronyc tracking # 看 System time 偏差正常应在毫秒级三台机器的时间偏差要控制在几十毫秒内。这个要求不是洁癖ZooKeeper 的 leader 选举对时间敏感Kerberos 的票据默认允许 5 分钟偏差超过就Clock skew too great。曾有一次某台虚拟机的时钟漂了 7 分钟导致该节点上所有服务注册到 ZK 一直失败日志里全是Session expired排查了两小时才想到看时间。2.5 关掉透明大页与批量分发THP 的关闭方式echo never /sys/kernel/mm/transparent_hugepage/enabled echo never /sys/kernel/mm/transparent_hugepage/defrag写进/etc/rc.local并给执行权限。然后验证cat /sys/kernel/mm/transparent_hugepage/enabled输出里带[never]。到这里单节点的基础环境就干净了。我的做法是把上面所有操作写成一个init.sh用 scp 分发到另外两台执行一遍最后统一跑一遍校验脚本检查hostname -f、java -version、python3.6 -V、getenforce、ulimit -n、chronyc tracking、THP 状态。全部通过再进下一步别信应该没问题集群搭建里应该是最贵的词。3. CDH 6.2 集群安装与 Spark 服务落地基础环境干净之后进入正式安装。这一步的核心思路是元数据库先建好 → CM Server 和 Agent 装好 → 用向导纳管节点和分发 parcel → 添加 Spark 服务。顺序不能乱尤其是元数据库CM 启动时会去连连不上就起不来你在界面上什么也看不到只能在日志里干瞪眼。3.1 元数据库的准备与参数CDH 6.2 需要三类数据库CM 自身的库以及 Hive、Oozie、Hue 等服务的库。测试环境只装 Spark 和 HDFS、YARN 的话其实只需要 CM 库和 Hive 的 Metastore 库Spark SQL 要读写 Hive 元数据。建库语句如下CREATE DATABASE scm DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; CREATE DATABASE hive DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; CREATE USER cdhuser% IDENTIFIED BY YourStrongPass; GRANT ALL PRIVILEGES ON scm.* TO cdhuser%; GRANT ALL PRIVILEGES ON hive.* TO cdhuser%; FLUSH PRIVILEGES;字符集必须是utf8不能用latin1否则后面 Hive 里建中文注释的表会报错。另外 MySQL 5.7 之后默认的sql_mode包含ONLY_FULL_GROUP_BY某些 CM 的查询语句会因此报错建议在my.cnf里显式设置为[mysqld] transaction-isolation READ-COMMITTED character-set-server utf8 max_connections 500maximum-connections调高是因为 CM 的监控采集会开不少连接默认 151 在小集群就够但一旦你装了多个服务很容易撞到Too many connections。3.2 离线仓库与 parcel 分发的取舍CM 的安装方式有两种在线 repo 和离线 parcel。只要不是完全隔离的内网环境我强烈建议离线 parcel。原因很实际在线模式下yum会去访问外部仓库内网防火墙策略一变、或者仓库地址调整安装就会卡在正在安装软件包半小时然后失败而且失败后残留的包状态很难清理往往要重装系统。离线 parcel 就是把CDH-6.2.0-1.cdh6.2.0.p0.967373-el7.parcel和对应的.sha文件放到 master 的/opt/cloudera/parcel-repo/下CM 会自动校验并分发行为完全可控。parcel 文件大约 2G.sha文件是它的校验和。这里有个必踩的坑.sha文件名必须是 parcel 名加.sha例如CDH-6.2.0-1.cdh6.2.0.p0.967373-el7.parcel.sha而且内容里要带正确的 SHA1 值不能是空文件。CM 是靠这个文件判断 parcel 是否完整的写错了它会一直显示正在下载或者校验失败。同时把 CM 的 rpm 包准备好yum install -y cloudera-manager-daemons-6.2.0-968364.el7.x86_64.rpm \ cloudera-manager-agent-6.2.0-968364.el7.x86_64.rpm \ cloudera-manager-server-6.2.0-968364.el7.x86_64.rpmServer 只在 master 装Agent 三台都装。3.3 CM Server 初始化与 Agent 注册Server 装完后初始化元数据库/opt/cloudera/cm/schema/scm_prepare_database.sh mysql scm cdhuser YourStrongPass这条命令会创建 CM 需要的一系列表成功会输出 All done, your SCM database is configured correctly!。如果报驱动找不到就回去检查 2.2 节的 JDBC 驱动目录。然后启动 server 和 agentsystemctl start cloudera-scm-server systemctl start cloudera-scm-agentServer 首次启动比较慢要等 1 到 2 分钟日志在/var/log/cloudera-scm-server/cloudera-scm-server.log看到Started Jetty server才算好。浏览器访问http://cdh-master-01:7180默认账号密码都是admin。接着用向导为集群安装 Cloudera Manager Agent填主机名列表填账号密码CM 会通过 SSH 去各节点装 Agent。这一步失败率最高九成是 SSH 免密没配好、sudo 权限不够、或者某台机器上已经装了旧 Agent 导致端口冲突7182。排查方法是在 master 上手动ssh cdh-worker-02 sudo -n true看是否无交互成功。3.4 向导配置从 parcel 到服务分配纳管成功后进入 parcel 分发。选择仅使用本地 parcel或者把 URL 指向 master 的 7180 仓库CM 会把 parcel 分发到三台并解压。解压完之后在/opt/cloudera/parcels/下能看到CDH软链指向版本目录这一步完成意味着所有 Java 组件都就位了包括 Spark。服务分配时Spark 本身不需要独立守护进程它依赖三个东西HDFS存数据、YARN跑计算、Hive Metastore存表结构。所以添加 Spark 服务之前HDFS、YARN、ZooKeeper、Hive 必须已经正常。我在小集群上的角色安排是角色所在节点说明NameNodecdh-master-01单机部署非 HADataNodecdh-worker-02/03两个副本节点ResourceManagercdh-master-01与 NN 同机测试环境可接受NodeManagercdh-worker-02/03计算节点Hive Metastorecdh-master-01依赖 MySQLSpark History Servercdh-worker-02避免和 NN 抢 IO网关角色三台全部供 spark-submit 使用HDFS 的副本数两 DataNode 的情况下默认 3 会一直处于 under-replicated 状态必须改成 2否则界面上一片黄条看着糟心还会影响写入性能。这个参数在hdfs-site.xml的dfs.replication通过 CM 改并重启。3.5 Spark 服务添加与网关配置落地添加 Spark 服务时CM 会让你指定History Server 存储目录填hdfs://nameservice/spark-history或hdfs://cdh-master-01:8020/spark-history。非 HA 环境就用后者注意端口是 8020写错了 History Server 会启动失败日志里是Connection refused。然后是Spark 网关角色这个概念很多新手搞不清。网关不是守护进程它的作用是把客户端配置文件hive-site.xml、hdfs-site.xml、yarn-site.xml、core-site.xml软链到/etc/spark/conf/下让本地spark-submit能读到集群配置。所以每台要跑 spark-submit 的机器都必须有网关角色并且部署网关后要执行部署客户端配置。部署完成后在任意节点上ls -l /etc/spark/conf/应该能看到一堆指向/opt/cloudera/parcels/CDH/lib/spark/conf或/etc/hadoop/conf的软链。如果只有一个spark-defaults.conf孤零零躺着说明客户端配置没部署成功spark-submit会报Could not resolve host: nameservice之类的错。接着配置 Spark 的重要参数通过 CM 的Spark → 配置搜索修改spark.executor.memory12g spark.executor.cores4 spark.dynamicAllocation.enabledtrue spark.dynamicAllocation.minExecutors1 spark.dynamicAllocation.maxExecutors12 spark.shuffle.service.enabledtrue spark.eventLog.enabledtrue spark.eventLog.dirhdfs://cdh-master-01:8020/spark-historyspark.shuffle.service.enabledtrue必须配合 YARN 的yarn.nodemanager.aux-services里加上spark_shuffle否则动态分配一缩容就把 executorshuffle 数据删了任务直接失败。CM 会自动处理这个联动但建议装完去yarn-site.xml里确认一下。内存超配这块CDH 默认开启了yarn.nodemanager.resource.memory-mb的严格校验spark.executor.memory overhead超过 container 申请的spark.yarn.executor.memoryOverhead就会被 kill症状是 executor 启动后立刻Container killed by YARN for exceeding memory limits。超配默认关闭我一般保持关闭宁可多算一点。4. 客户端验证与性能基线装完不等于可用。很多集群看起来服务都绿了实际 spark-submit 一次就暴露问题。这一节讲怎么用最小代价把隐藏问题逼出来顺便建个性能基线后面出问题好对比。4.1 三个递进式的验证用例第一个用例纯 local 模式验证 Spark 本身spark-submit --master local[2] \ --class org.apache.spark.examples.SparkPi \ /opt/cloudera/parcels/CDH/lib/spark/examples/jars/spark-examples_2.11-2.4.0-cdh6.2.0.jar 10能打出Pi is roughly 3.14说明 jar 包、JDK、Scala 版本链路都是通的。如果这一步就报NoClassDefFoundError: org/apache/hadoop/fs/FSDataInputStream说明HADOOP_CONF_DIR没配好回 3.5 节检查网关。第二个用例YARN client 模式验证和 YARN 的联动spark-submit --master yarn --deploy-mode client \ --num-executors 2 --executor-memory 2g --executor-cores 2 \ --class org.apache.spark.examples.SparkPi \ /opt/cloudera/parcels/CDH/lib/spark/examples/jars/spark-examples_2.11-2.4.0-cdh6.2.0.jar 100这一步能暴露 90% 的问题资源队不上、AKKA 通信超时、本地临时目录权限不对。如果卡在Running Spark using the REST application submission protocol很久基本是 ResourceManager 地址解析不了检查yarn-site.xml里的yarn.resourcemanager.hostname。第三个用例读 HDFS 写 HDFS验证数据通路和序列化spark-submit --master yarn --deploy-mode cluster \ --class org.apache.spark.examples.SparkPageRank \ /opt/cloudera/parcels/CDH/lib/spark/examples/jars/spark-examples_2.11-2.4.0-cdh6.2.0.jar \ hdfs://cdh-master-01:8020/tmp/pagerank/data /tmp/out 10这个例子依赖输入数据需要先放个小文件进去。跑通它说明 HDFS 读写、shuffle、history server 写日志全链路都没问题。4.2 参数计算与实际压测记录用spark-sql或spark-shell跑个简单的聚合观察资源占用spark.sql(SELECT count(*) FROM range(100000000)).show()10 亿行不太现实一亿行足够看趋势。我在 3 节点 × 16 核 64G 的配置上实测一亿行 count 大约 18 秒executor 数自动扩到 6 个每节点 2 个每个 12G/4 核。这个数字只作参考重点是通过 History Server 看shuffle read/write 量、GC 时间占比、任务倾斜。我发现一个常见现象任务总时长 20 秒其中 executor 启动占了 12 秒——这就是小作业开动态分配的代价这种情况下把spark.dynamicAllocation.minExecutors调到 3或者干脆关掉动态分配更划算。再补一个内存参数的关系表方便你调的时候对照参数建议值计算依据executor.memory8-16g不超过 NM 可用内存的 1/3避免单点抢占executor.cores3-5超过 5 后 HDFS 并发读写效率下降memoryOverhead至少 384m 或堆的 10%PySpark 场景要 20%dynamicAllocation.max集群总核数 / executor.cores防止占满队列导致其他任务饿死shuffle.partitions200 起按数据量 × 2 核数调默认 200 对小数据过大大数据过小4.3 一个容易被忽略的验证History Server 日志落盘很多人跑完任务就不管了等到要复盘时发现 History Server 里空空如也。原因通常是spark.eventLog.dir写错了或者目录权限不对。HDFS 上的/spark-history目录必须允许所有跑任务的用户写入CM 建这个目录时属主是spark而你跑任务的用户可能是别的就会静默失败。验证方法hdfs dfs -ls /spark-history hdfs dfs -touchz /spark-history/test_write第二条能成功才算权限 OK。这一步我强烈建议在交付前做因为 event log 是后续调优唯一的证据来源没有它性能问题基本靠猜。5. 常见问题排查速查表这套环境我装过不下十次问题形态高度集中。下面按现象 → 根因 → 处理整理成表遇到问题先查表能省掉大量翻日志的时间。表格后面补几条表格装不下、需要展开讲的。现象大概率根因处理动作向导检查主机失败报 SSH 错误免密未配或无 sudo 权限手动ssh 节点 sudo -n true验证parcel 一直正在下载.sha文件缺失或内容错误核对文件名与 SHA1 值DataNode 起不来日志无明确报错SELinux 未彻底关闭或 THP 未关getenforce查状态重启确认Agent 心跳正常但角色启动失败Java 路径不一致检查每台机器的 Java 主目录配置executor 被 kill提示超内存overhead 不足或超配开启调大memoryOverhead保持超配关闭卡在 REST application submissionRM 地址解析失败检查yarn-site.xml主机名配置Spark SQL 报 Metastore 连接失败Hive 元数据库凭证或驱动问题检查 JDBC jar 与hive-site.xmlHistory Server 无记录eventLog 目录权限或路径错用 touchz 验证目录可写NoSuchMethodError: com.google.common手动塞 jar 导致 Guava 版本冲突移除非官方 jar恢复 parcel 原生状态shuffle fetch 大量失败shuffle service 未启用YARN aux-services 加spark_shuffle5.1 三个值得展开的坑第一个主机名大小写问题。CM 在注册主机时用的是 Agent 上报的 FQDN如果你在/etc/hosts里写的是大写比如CDH-MASTER-01而hostname -f返回小写就会出现同一台机器被识别成两个主机然后向导里节点数不对、角色分配混乱。处理办法是统一全小写改完重启 Agent。第二个ZooKeeper 的 dataDir 权限。ZooKeeper 启动失败最常见的原因是dataDir属主不对尤其你在 root 下手工建过目录之后服务用zookeeper用户跑就写不进去。日志里是一句很含蓄的Unable to create data directory其实就在等你改权限。chown -R zookeeper:zookeeper /var/lib/zookeeper即可。第三个YARN 的 vcores 超配判断。你设了yarn.nodemanager.resource.cpu-vcores14但yarn.nodemanager.resource.cpu-vcores和yarn.scheduler.maximum-allocation-vcores是两回事后者是单 container 上限。如果后者小于你的spark.executor.cores提交时直接报Invalid resource request错误信息里只提最大值不提你请求了多少很容易看漏。5.2 排查顺序的经验总结出问题时我的一般顺序是先看 CM 界面上的角色日志入口 → 再看原生日志文件 → 最后看 OS 层dmesg、/var/log/messages。CM 聚合了大部分 Java 异常但 OOM Killer 干掉进程这种事它看不到只在dmesg里有痕迹。曾经有个 NodeManager 每隔几小时就重启一次CM 里只显示角色已停止最后在dmesg里看到Out of memory: Kill process原来是机器上跑了个内存泄漏的小脚本。另外不要迷信重启能解决。重启确实能让角色起来但如果根因是配置错误它会在几小时后再次挂掉而你已经忘了之前改过什么。我的习惯是每次改动都在changes.md里记一行时间、改了什么、为什么、影响哪些节点。这个习惯在三个月后回看时价值远超你当时的想象。6. 我在这套环境上踩过的真实教训装完这套东西之后真正让我印象深刻的不是某个具体命令而是几个当时觉得无所谓、后来付出代价的决定。一个是副本数。我一开始图省事保持默认 3两 DataNode 的集群里一直是 under-replicatedHDFS 的写入路径会不断尝试复制到第三个节点然后失败写入延迟比正常情况高出将近一倍。改成 2 之后同样的导入任务从 40 分钟降到 22 分钟。这件事让我养成了一个习惯装完集群第一件事是核对所有和节点数相关的参数包括副本数、dfs.datanode.failed.volumes.tolerated、YARN 的 nodemanager 数量感知。另一个是测试环境别开 Kerberos。我在一个小集群上为了模拟生产把 Kerberos 也开了结果 Spark 的每个提交都要处理 ticket 刷新、delegation token 续期一个没配好就报GSS initiate failed。后来我把安全认证拆成独立的第二阶段先保证非安全模式下全链路通畅再叠安全层出问题能立刻判断是哪一层。这个顺序很重要一次只引入一个变量。还有个小技巧值得分享把常用验证命令打包成一个smoke-test.sh放在 master 上内容就是前面 4.1 的三个用例加 HDFS 读写检查。每次改完配置就执行一次三分钟出结果。这套东西在你要给别的同事交接环境时尤其有用它能替你回答这集群到底好没好这个问题。最后提一句扩容场景。新增节点时除了走一遍基础环境最容易漏的是Python 版本和 JDK 小版本要和现有节点完全一致以及新节点的/etc/hosts要同步。我吃过一次亏新节点装的是更新版本的 PythonPySpark 任务被调度过去就报 pickle 不兼容而另外两个节点跑得好好的导致问题是间歇性的排查方向一度跑偏到数据倾斜上。从那以后扩容前先跑一次版本比对脚本成了我的固定动作。
阅读完成 · 觉得有帮助?