将 Kubernetes 扩展到 2,500 个节点
OpenAI 通过使用 D15v2 和 NC24 VM 的组合,将其 Kubernetes 基础设施扩展到 Azure 上超过 2,500 个节点,以支持深度学习研究。此扩展工作需要克服状态存储、Pod 调度、容器镜像分发和网络配置方面的重大瓶颈。
优化 etcd 性能和稳定性
随着集群规模超过 500 和 1,000 个节点,Kubernetes 状态存储出现了严重的延迟和容量问题。
减少写入延迟
当集群超过 500 个节点时,由于网络附加 SSD 上的写入延迟高,导致 kubectl 超时。OpenAI 通过将 etcd 目录移动到本地临时磁盘(直接连接到实例的 SSD)来解决此问题,使写入延迟从 2ms 降低到 200us。
管理 API 服务器负载
在 1,000 个节点时,由于 kube-apiservers 从 etcd 读取超过 500MB/s,高提交延迟再次出现。这是由监控进程(Fluentd 和 Datadog)从每个节点对 API 服务器进行激进轮询导致的。通过降低这些进程的轮询频率,恢复了稳定性。
隔离事件和存储配额
为了防止事件创建的峰值影响主集群状态,OpenAI 使用 --etcd-servers-overrides 标志将 Kubernetes Events 存储在单独的 etcd 集群中。此外,他们使用 --quota-backend-bytes 标志将默认的 2GB etcd 存储限制提高,以防止在 etcd 停止接受写入后自动扩缩器终止工作节点而导致的级联故障。
Kube Master 和调度调整
OpenAI 将 Kubernetes 用作批处理调度系统,并采用自定义自动扩缩器来最小化空闲节点的成本。
Bin-Packing 调度策略
为了确保未使用的节点可以被终止且大型 Pod 能够快速调度,OpenAI 将默认的负载分散策略替换为 'MostRequestedPriority' 策略。这鼓励将 Pod 按 Bin-Packing 方式装入较少的节点,而不是均匀分布。
KubeDNS 可靠性
Bin-Packing 策略导致一些机器上运行了 10+ 份 KubeDNS 副本,超过了 Azure VM 外部域名查询限制(约 200 QPS)。OpenAI 通过实施 Pod 反亲和性规则来解决此问题,以确保 KubeDNS Pod 分布在不同的主机名上。
加速 Docker 镜像拉取
大型容器镜像(例如用于 Dota 项目的 17GB 镜像)导致 Pod 长时间处于 Pending 状态。
并行拉取和超时调优
默认情况下,kubelet 会序列化镜像拉取(--serialize-image-pulls=true),这意味着一个大型镜像可能会阻塞其他所有镜像。OpenAI 将其设置为 false,并将 Docker 切换到 overlay2 存储驱动程序。他们还将 Docker 根目录移动到实例附加的 SSD。
为了防止因大型镜像提取缓慢而导致的 rpc error: code = 2 desc = net/http: request canceled 错误,他们将 --image-pull-progress-deadline 增加到 30 分钟,并在 Docker 守护进程中将 max-concurrent-downloads 设置为 10。
预加载常用镜像
为了避免在节点通过 NAT 访问 Google Container Registry(gcr.io)时触发每 IP 配额限制,OpenAI 使用 docker image save 和 docker image load 将 pod-infra-container-image 和其他常用内部镜像直接预加载到工作节点机器镜像中。
网络和内核调优
大规模分布式实验揭示了与网络栈相关的显著吞吐量下降和连接问题。
克服 Flannel 开销
OpenAI 观察到使用 Flannel 的 Pod 吞吐量受限约为 2Gbit/s,而裸机之间可达 10-15Gbit/s。为了在高性能工作负载中绕过此开销,用户可以为其 Pod 设置 hostNetwork: true 和 dnsPolicy: ClusterFirstWithHostNet。
解决 ARP 缓存溢出
间歇性 DNS 解析失败和连接挂起是由内核的 ARP 缓存耗尽导致的,因为大型集群中的每个 Pod 都会消耗一个 ARP 条目。这通过 dmesg 中的 neighbor table overflow! 消息被识别出来。OpenAI 通过在 /etc/sysctl.conf 中增加 ARP 缓存阈值来解决此问题:
net.ipv4.neigh.default.gc_thresh1 = 80000
net.ipv4.neigh.default.gc_thresh2 = 90000
net.ipv4.neigh.default.gc_thresh3 = 100000
Sources
相关
- Dispatch
- Dispatch
- Dispatch
- Dispatch
- Dispatch