OpenAI 将 Kubernetes 扩展至 7,500 个节点
OpenAI 已将单个 Kubernetes 集群扩展至 7,500 个节点,以使机器学习研究团队能够在不修改代码的情况下扩展工作负载。该基础设施支持大规模作业,其中单个 pod 通常占用整个节点,以通过 NVLink 和 GPUDirect 实现硬件效率最大化。
工作负载特征
OpenAI 的 Kubernetes 工作负载与典型的企业级应用显著不同。其主要关注点是大规模机器学习作业,这些作业优先考虑硬件资源访问,而非装箱(bin-packing)或碎片化。
- 资源利用率: Pod 通常占用整个节点,以允许 GPU 通过 NVLink 直接通信,并通过 GPUDirect 通过 NIC 直接通信,从而减少了对基于 NUMA 或 CPU 争用的复杂调度需求。
- 作业性质: 作业通常运行 MPI (Message Passing Interface),使其具有“半有状态”特性。如果 MPI 通信器中的一个 pod 死亡,整个作业将停止并从最后一个检查点(checkpoint)重新开始。
- 网络模式: 极少依赖 Kubernetes 负载均衡、HTTPS 流量或服务发现。Pod 通过使用 MPI over SSH 的方式,直接通过 pod IP 地址进行通信。
- 存储: 系统主要依赖 blob 存储进行数据集流式传输和检查点保存,仅在需要 POSIX 语义时才使用 PersistentVolumes。
网络优化
为了支持 7,500 个节点和约 200,000 个并发 IP 地址,OpenAI 放弃了 Flannel,转而为 Azure VMSS (Virtual Machine Scale Sets) 使用原生 pod 网络和相关的 CNI 插件。
高吞吐量网络
使用基于别名(alias-based)的 IP 地址分配,而非基于路由的网络,避免了有效路由数量的限制并提供了主机级网络吞吐量。这种方法消除了封装,简化了设置并避免了与 MTU 相关的分组碎片化问题。
网络监控
OpenAI 使用 iptables mangle 规则将数据包标记为内部或互联网方向。这些计数器通过开源的 iptables-exporter 进行跟踪,并集成到 Prometheus 中,允许研究人员可视化网络瓶颈。
集群隔离
集群采用 hub-and-spoke(中心辐射型)模型。虽然研究人员可以通过 hub 访问单个集群(spokes),但集群之间无法相互通信,从而确保了故障隔离。
API Server 和 etcd 稳定性
为了在大规模环境下维持集群健康,OpenAI 在集群之外的专用节点上运行 API server 和 etcd。
- 部署: 最大的集群使用五个 API server 和五个 etcd 节点来分担负载。
- 资源使用: API server 的内存使用量随集群规模线性增长,对于 7,500 个节点,堆内存(heap)最高可达 70GB。
- EndpointSlices: 采用 EndpointSlices(在 Kubernetes 1.17 中引入)将 Endpoints 的 WATCHes 负载降低了 1,000 倍,防止了在添加或删除节点时出现 $N^2$ 带宽峰值。
- 扩展策略: 为了避免 API server 过载,OpenAI 避免在 API 交互中使用 DaemonSets,并在自动扩缩容事件期间平滑地添加新节点。
使用 Prometheus 和 Grafana 进行监控
OpenAI 使用 Prometheus 进行时间序列指标,并使用 Grafana 进行可视化,但在扩展方面遇到了显著的障碍。
- 内存泄漏:
/api/v1/seriesAPI 中的一个 bug 导致 Prometheus 在 Grafana 查询直方图指标时,由于无限制的内存消耗而崩溃。OpenAI 对 Prometheus 进行了补丁,以强制执行使用 Context 的超时控制。 - WAL Replay 性能: 由于 Write-Ahead-Log (WAL) 重放,Prometheus 的启动时间被延迟了数小时。通过设置
GOMAXPROCS=24缓解了这一问题,减少了高核数服务器上的 CPU 争用。 - 指标过滤: 为了管理数据量,使用 Prometheus 规则来从摄取过程中“丢弃”细粒度或未使用的指标。
节点健康状况与 GPU 验证
通过结合被动和主动健康检查,使用自动化手段来检测并移除表现异常的节点。
被动健康检查
系统通过 dcgm-exporter 和 NVML Device Query API 监控网络可达性、磁盘健康状况以及 GPU 错误(例如 Uncorrectable ECC 错误)。故障节点会被自动标记为 cordoned;严重故障会触发 pod 驱逐和最终的 VM 终止。
主动 GPU 测试
由于某些 GPU 问题不会触发错误代码,OpenAI 使用了一种“预检”(preflight)系统。新节点加入集群时会带有 taint 和 label;一个 DaemonSet 会在移除 taint 之前运行详尽的 GPU 测试,以允许通用工作负载。
资源管理与调度
OpenAI 实现了自定义机制来处理不同研究团队之间的资源分配和 gang scheduling。
- Team Taints:
team-resource-manager服务使用 taints (openai.com/team=teamname:NoSchedule) 和 admission webhooks 来为研究团队分配特定的节点容量,同时允许低优先级 pod 运行在未使用的容量上。 - Resource Balloons: 为了防止 cluster-autoscaler 移除闲置节点(这会增加启动延迟和 API server 压力),OpenAI 使用了“气球”(balloon)Deployments。这些低优先级 pod 占用空间,但当实际工作到达时,会立即被驱逐。
- Gang Scheduling: 为了防止多个实验各自占用集群容量的一半导致死锁,OpenAI 使用了 Coscheduling 插件(在 Kubernetes 1.18 中引入)以确保在训练开始前,StatefulSet 中的所有 pod 都已完成调度。
剩余挑战
OpenAI 识别出了两个主要的主要未解决问题:
- 指标存储: Prometheus TSDB 引擎在压缩时速度较慢,且容易出现“样本过多”错误。OpenAI 正在迁移到另一种兼容 Prometheus 的存储和查询引擎。
- 流量整形: 研究人员使用的总互联网带宽可能会使外部数据集和软件仓库压力过大,因此需要更好的 pod 网络流量整形技术。