AI 工作负载持续吞噬云端算力后,Spark 流水线面临的故障不再只有代码异常和数据倾斜,还包括一个更基础的问题:目标区域可能暂时拿不出你指定的虚拟机。如果集群只能使用某个固定机型,一次容量短缺就可能演变成集群创建失败、任务延迟,甚至业务 SLA 违约。
Google Cloud Managed Service for Apache Spark 提供的灵活虚拟机机制,允许集群按照优先级尝试多个机器系列和存储配置。其核心不是寻找一台“永远有货”的机器,而是消除对单一硬件型号的依赖。
固定机型为什么会成为单点故障
假设生产任务固定使用 n2d-standard-16,并且只能部署在一个繁忙区域。即使 Spark 作业本身没有问题,只要对应机器系列的可用容量不足,集群就无法按时启动。
这种风险会影响整个集群拓扑:
- Master 节点申请失败,集群根本无法建立。
- Primary Worker 不足,核心计算能力无法就绪。
- Secondary Worker 或 Spot Worker 不足,突发负载无法扩容。
- 指定磁盘类型与新机器系列不兼容,候选实例仍然无法创建。
灵活虚拟机通过“候选集合 + 排名”替代单一机型约束。平台先尝试 Rank 0;容量不足时,再依次尝试 Rank 1、Rank 2。不同角色可以拥有各自的候选列表,从而避免只给 Worker 配置回退、却让 Master 继续成为瓶颈。
如何设计有意义的优先级
排名不能只是随意罗列机型。一个更实用的原则是:同一层放置性能和成本接近的替代项,后续层级再逐渐扩大硬件差异。
以 16 vCPU 左右的生产集群为例,可以采用下面的层次:
| 优先级 | 候选机器系列 | 建议存储 | 定位 |
|---|---|---|---|
| Rank 0 | n2d-standard-16、n2-standard-16 |
Standard PD 或受支持的本地 SSD | 首选配置,至少提供两个机器系列 |
| Rank 1 | n4-standard-16、n4d-standard-16 |
Hyperdisk Balanced | 使用较新的机器系列扩大容量池 |
| Rank 2 | c4-standard-16、c3-standard-22 |
Hyperdisk Balanced | 接受不同 CPU 平台或核数 |
| Rank 3 | e2-standard-16 |
Standard PD 或兼容磁盘 | 以成功启动为优先的兜底选项 |
Rank 0 最好至少包含两个机器系列。否则,即使定义了后续回退,常规创建仍然会先受到单一容量池的限制。
存储也必须跟随机型变化。N4、C4 等较新的系列通常需要结合 Hyperdisk 使用;如果候选计算实例支持多种系列,但磁盘配置仍被锁定在不兼容类型上,所谓的灵活性就会被存储层抵消。对于多数分布式 Spark 作业,可以先从 Hyperdisk Balanced 的默认 IOPS 和吞吐量开始,再根据基准测试调整。
可直接改造的集群创建命令
下面的命令展示了如何为 Primary Worker 和 Master 设置分级候选项。运行前需要登录 gcloud、设置项目,并将区域、集群名和网络配置改成自己的环境。
#!/usr/bin/env bash
set -euo pipefail
CLUSTER_NAME="spark-flexible-prod"
REGION="us-east1"
# 不指定固定 zone,让区域级调度拥有更多选择空间。
gcloud dataproc clusters create "${CLUSTER_NAME}" \
--region="${REGION}" \
--num-workers=10 \
--worker-instance-selection='{"machineTypes":["n2d-standard-16","n2-standard-16"],"rank":0,"diskConfig":{"bootDiskType":"pd-standard","bootDiskSizeGb":400}}' \
--worker-instance-selection='{"machineTypes":["n4-standard-16","n4d-standard-16"],"rank":1,"diskConfig":{"bootDiskType":"hyperdisk-balanced","bootDiskSizeGb":400}}' \
--worker-instance-selection='{"machineTypes":["c4-standard-16","c3-standard-22"],"rank":2,"diskConfig":{"bootDiskType":"hyperdisk-balanced","bootDiskSizeGb":400}}' \
--worker-instance-selection='{"machineTypes":["e2-standard-16"],"rank":3,"diskConfig":{"bootDiskType":"pd-ssd","bootDiskSizeGb":400}}' \
--master-instance-selection='{"machineTypes":["n4-standard-16","n4d-standard-16"],"rank":0,"diskConfig":{"bootDiskType":"hyperdisk-balanced","bootDiskSizeGb":400}}'
这段命令可以作为起点,但不应未经验证直接搬进生产环境。需要重点检查:
- 每个机器系列是否在目标区域可用。
- 项目是否拥有对应 CPU、实例和磁盘配额。
- 当前
gcloud与托管 Spark/Dataproc 版本是否支持这些参数。 - 企业网络、服务账号、加密密钥和初始化脚本是否需要额外参数。
- Secondary Worker、Spot Worker 也应根据实际集群模型配置候选项。
如果现有任务仍以 n1-standard-16 为基准,可以将 N1 与 N2 放在较高优先级,再把 N2D、N4/N4D 和 E2 作为逐级回退。这既保留旧任务的运行特征,也为迁移到新架构留下缓冲空间。
算力可用不等于性能完全一致
不同机器代际、CPU 平台和磁盘类型之间可能存在明显差异。Spark 尤其容易受以下因素影响:
- Shuffle 密集型任务对磁盘吞吐和网络带宽敏感。
- 大量小文件处理可能更依赖 IOPS 和元数据操作。
- CPU 密集型 UDF 会受到处理器代际和单核性能影响。
- Executor 数量、核数和内存比例不变时,换机型后未必仍是最佳配置。
因此,应使用真实作业而不是简单的 CPU 压测进行验证。可以记录每个候选系列的总运行时间、Shuffle Spill、失败重试、磁盘吞吐和作业成本,然后为回退层设置可接受的 SLA 边界。灵活虚拟机的目标是让任务先运行起来,但不能假设所有候选项的性能完全等价。
成本策略也需要同步调整。传统按资源绑定的承诺使用折扣往往限定机器系列,会与跨系列调度产生冲突。需要跨机器系列或区域保留成本弹性时,可以评估 Compute flexible CUD,同时确认其覆盖范围与实际调度策略一致。
不要只依赖机型回退
灵活虚拟机解决的是“申请哪种资源”,更高的端到端可用性还需要多层策略配合:
- 启用 AutoZone 思路:避免把任务硬编码到单一可用区,让平台根据当前容量选择区域内的合适 Zone。
- 优先使用较小规格:4、8 或 16 核实例通常比超大实例更容易从按需资源池中获得。相应地调整 YARN Container 与 Executor 布局。
- 使用自动扩缩容:不要为了峰值一次性申请庞大集群。设置合理的最大实例数,让集群随负载和容量逐步扩展。
- 允许部分集群启动:定义可接受的最少 Primary Worker 数量,在资源紧张时先启动任务,再由自动扩容补齐节点。具体配置项应以当前服务版本为准。
- 准备跨区域回退:高需求区域可能持续紧张。关键流水线应预先验证备用区域的数据、网络、配额和合规条件,而不是故障时临时迁移。
上线前检查清单
采用灵活虚拟机时,可以按以下顺序落地:
- Rank 0 至少配置两个性能相近的机器系列。
- 为 Master、Primary Worker 和 Secondary Worker 分别消除单点依赖。
- 核对所有候选机型及 Hyperdisk、PD 的区域配额。
- 用真实 Spark 作业测试每个回退层的性能与成本。
- 验证自动扩缩容、最小 Worker 数和跨区域运行能力。
- 为不同机器系列建立监控标签,确认实际使用了哪个回退项。
- 检查承诺使用折扣是否会限制跨系列调度。
真正可靠的 Spark 平台,不应把可用性押在某一个 VM 型号上。将计算、存储、可用区和区域都设计成可替换资源,才能让容量短缺从一次生产事故,降级为平台自动处理的常规调度事件。