乐趣区

关于运维:OpenAI-Kubernetes-相关博文读后笔记

一、概述

最近 ChatGPT 和其公司 OpenAI 特地火:ChatGPT 3, ChatGPT 3.5, New Bing, ChatGPT 4…

怀着学习的心态,这几天拜访了 OpenAI 的博客, 上边对于 AI 的内容,的确隔行如隔山,齐全看不明确。😂

然而翻看过程中,惊喜发现有 2 篇与 Kubernetes 应用相干的文章:

  • 2018 年 1 月:Scaling Kubernetes to 2,500 nodes (openai.com)
  • 2021 年 1 月:Scaling Kubernetes to 7,500 nodes (openai.com)

这不碰到老本行了嘛,学习下~

以下为读后笔记,也退出了本人的思考:针对 OpenAI 现状,如何进一步优化监控、镜像拉取、容器编排相干架构。

二、读后笔记

2.1 Dota 2 的 OpenAI 是跑在 Kubernetes 上的

💪💪💪

Dota2 游戏镜像大概是 17GB😛

2.2 OpenAI 如何应用 Kubernetes

2.2.1 用处

Kubernetes 在 OpenAI 次要用于深度学习,次要应用的是 Kubernetes Job.

2.2.2 抉择 Kubernetes 起因

Kubernetes 提供了

  • 疾速的迭代周期
  • 正当的可扩展性
  • 规范样板

2.2.3 Kubernetes 集群规模

  • 集群运行在 AZure 上
  • 2018 年博文,集群 Node 规模为 2500 多
  • 2021 年博文,集群 Node 规模为 7500 多

2.2.4 Kubernetes 超大规模应用过程中遇到的问题

  • etcd
  • Kube masters
  • Docker 镜像拉取
  • 网络
  • KubeDNS
  • 机器的 ARP 缓存

2.2.5 Kubernetes 配套工具

  • 监控:

    • DataDog(商业监控)
    • Prometheus + Grafana
  • 日志:

    • fluentd
  • 网络:

    • 刚开始是 Flannel
    • 前面是 Azure 的 VMSSes CNI 插件

2.3 Kubernetes 超大规模发现的问题及解决

2.3.1 Etcd

2.3.1.1 问题形容

集群 500 节点后,刚开始是 kubectl 应用卡;另外通过 DataDog 监控发现磁盘写入提早飙升至数百 ms,只管每台机器都应用 30,5 IOPS 的 P000 SSD。

前面解决之后,加到 1000 节点,发现 etcd 提早再次变高;发现 kube-apiservers 从 etcd 读取的速率超过 500MB/s

另一个 1,000 个节点后的故障是达到了 etcd 的硬存储限度(默认为 2GB),这导致它进行承受写入。

2.3.1.2 解决方案
  • 将每个节点的 etcd 目录挪动到本地长期磁盘,该长期磁盘是间接连贯到实例的 SSD,而不是网络连接的 SSD。切换到本地磁盘使写入提早达到 200us,etcd 变得衰弱!(依据咱们之前的 Azure 应用教训,其网络 SSD 的确性能不太行😂)
  • 1000 节点时呈现的问题,在于

    • 启用了审计日志
    • 启用了 Prometheus 对 APIServer 的监控
    • 这导致呈现了许多迟缓的查问和对事件的 LIST API 的适度调用
    • 根本原因:默认设置 fluentd 和 Datadog 的监控过程是从集群中的每个节点查问 API 服务器。” 咱们 ” 只是简略地更改了这些过程,使其轮询不那么激进,并且 apiserver 上的负载再次变得稳固
  • 将 Kubernetes Events 存储在一个独自的 etcd 集群中,这样事件创立中的峰值不会影响主 etcd 实例的性能。配置如前面的代码段
  • 咱们用标记 --quota-backend-bytes 减少了最大 etcd 大小。
--etcd-servers-overrides=/events#https://0.example.com:2381;­https://1.example.com:2381;­https://2.example.com:2381

最初,etcd 和 APIServer 都运行在专用节点上。防止相互影响。
在 7500 节点时,有 5 个 etcd 节点。

2.3.2 API Server

在 7500 节点时,有 5 个 API 服务器,并且每个 API 服务器应用的堆内存高达 70GB.

2.3.3 Docker 镜像拉取

2.3.3.1 问题形容

Dota 容器会在一段时间内处于 pending 状态——但对于其余容器也是如此。

解决之后,发现有报错:rpc error: code = 2 desc = net/http: request canceled, 表明因为不足进度,镜像拉取已被勾销。

还有个问题,OpenAI 的 Kubernetes 组件镜像是默认从 gcr.io 拉取的,然而 gcr.io 可能失败或超出配额(机器用的 NAT 公网 IP 是同一个,很容易超出配额).

2.3.3.2 解决方案

kubelet 有一个 --serialize-image-pulls 默认为 true 的标记,示意 Dota 镜像拉取阻塞了所有其余镜像。

--serialize-image-pulls 改为 false; 将 Docker 根目录挪动到了实例附加的 SSD(而不是网络 SSD)

针对第二个问题,大镜像须要太长时间的 pull 和提取,或者当有大量积压的镜像须要拉取时。为了解决这个问题,咱们将 kubelet 的 --image-pull-progress-deadline 标记设置为 30 分钟,并将 Docker 守护过程的 max-concurrent-downloads 选项设置为 10。第二个选项没有放慢大镜像的提取速度,但容许镜像队列并行拉取。

为了解决这个 gcp.io 失败的问题,” 咱们 ” 通过应用 docker image save -o /opt/preloaded_docker_images.tardocker image load -i /opt/preloaded_docker_images.tar,在 Kubernetes worker 的机器镜像中预装了这些 Docker 镜像。为了进步性能,咱们对常见的 OpenAI 外部镜像如 Dota 镜像的白名单做了同样的解决。

2.3.3.3 🤔笔者思考

对于 OpenAI 碰到的 Docker 镜像拉取的问题,都是十分典型的大规模 Kubernetes 会碰到的问题,其实有更好的解决方案:P2P 镜像解决方案,典型就是 DragonFly.

DragonFly 提供高效、稳固、平安的基于 P2P 技术的文件散发和镜像减速零碎,并且是云原生架构中镜像减速畛域的规范解决方案以及最佳实际。其最大的劣势就是:

  • 基于 P2P 的文件散发:通过利用 P2P 技术进行文件传输,它能最大限度地利用每个对等节点(Peer)的带宽资源,以进步下载效率,并节俭大量跨机房带宽,尤其是低廉的跨境带宽。
  • 预热:P2P 减速可预热两种类型数据 image 和 file, 用户能够在控制台操作或者间接调用 api 进行预热。

2.3.4 网络

Flannel 在这种超大规模场景下必定是撑不住的,刚开始 OpenAI 采纳了非常简单暴力的解决方案(也适宜他们的应用场景): pod 配置应用 HostNetwork:

...
hostNetwork: true
...
dnsPolicy: ClusterFirstWithHostNet

前面是改为应用 Azure 的 VMSSes CNI 插件。

2.3.4.1 🤔笔者思考

其实 OpenAI 对 Kubernetes 的刚需是:容器编排,网络性能不是刚需,OpenAI 不必 Kubernetes CNI 也能够的。前面会延长讨论一下。

2.3.5 ARP 缓存

另外一个可能常常会疏忽的点是 ARP 缓存问题。

2.3.5.1 问题形容

有一天,一位工程师报告说,他们的 Redis 服务器的 nc -v 须要 30 多秒能力打印出连贯曾经建设。咱们追踪到这个问题是由内核的 ARP 栈引起的。对 Redis pod 主机的初步考察显示,网络出了重大的问题:任何端口的通信都要挂上好几秒,而且无奈通过本地 dnsmasq 守护过程解析 DNS 名称,dig 只是打印了一条神秘的失败信息:socket.c:1915: internal_send: 127.0.0.1#53: Invalid argument。dmesg 日志的信息量更大:neighbor table overflow! 这意味着 ARP 缓存的空间曾经用完。ARP 是用来将网络地址(如 IPv4 地址)映射到物理地址(如 MAC 地址)的。

2.3.5.2 解决方案

/etc/sysctl.conf 中设置选项:

net.ipv4.neigh.default.gc_thresh1 = 80000
net.ipv4.neigh.default.gc_thresh2 = 90000
net.ipv4.neigh.default.gc_thresh3 = 100000

在 Kubernetes 集群中调整这些选项尤其重要,因为每个 pod 都有本人的 IP 地址,会耗费 ARP 缓存的空间。

2.3.6 Prometheus 和 Grafana

因为大量的采集和查问,Prometheus 和 Grafana OOM 的频率也不低。

2.3.6.1 解决方案
  • Grafana: 实质上还是 Prometheus 的高基数问题,我之前介绍过,见这里:

    • Prometheus 性能调优 – 什么是高基数问题以及如何解决?- 东风微鸣技术博客 (ewhisper.cn)
  • Prometheus: 重启后,WAL replay 是个问题,在 Prometheus 收集新指标和服务查问之前,通常须要破费数小时能力 replay 所有 WAL 日志。

    • 应用 GOMAXPROCS=24 配置,在 WAL replay 期间,Prometheus 试图应用所有的内核,对于领有大量内核的服务器来说,这种抢夺会扼杀所有的性能。
2.3.6.2 🤔笔者思考
  1. Prometheus 近期版本性能会好很多,及时降级到最新版本会对性能问题大有帮忙。比方:高基数,大内存,cpu 耗费较多等都有肯定水平优化。
  2. Prometheus 在这么大规模集群状况下,倡议创立多个 node role 为 monitoring 的高配机器(也挂本地 SSD), 供 Prometheus 专用。
  3. 或者更近一步,能够抉择兼容 Prometheus 的其余计划,如:VictoriaMetrics(适宜存储为块存储场景)和 Grafana Labs 公布的 Mimir(适宜存储为对象存储场景).

2.4 OpenAI Kubernetes 的应用场景及举荐的替换计划

这里详细描述一下 OpenAI Kubernetes 的应用场景:

  • 次要用到的资源类型是 Job
  • 对于 OpenAI 的许多工作负载,单个 Pod 占据了整个节点
  • OpenAI 以后的集群具备残缺的平分带宽,不会思考任何机架或网络拓扑
  • OpenAI 不太依赖 Kubernetes 负载平衡,HTTPS 流量很少,不须要 A/B 测试,蓝 / 绿或金丝雀
  • Pod 通过 SSH 应用 MPI 在其 Pod IP 地址上间接互相通信 (hostNetwork),而不是通过 service endpoint 进行通信
  • 服务发现是无限的
  • 有一些 PersistentVolume,但 blob 存储的可伸缩性要高得多
  • 防止应用 Overlay 网络,因为会影响网络性能

2.4.1 🤔笔者思考

看完 Scaling Kubernetes to 7,500 nodes (openai.com) 这篇,其实会发现 OpenAI 对 Kubernetes 的应用和一般 IT 公司差别还是比拟大的。

OpenAI 最次要用的是:Kubernetes 的 容器编排, 特地是对 Job 的调度能力。

其余 Kubernetes 性能,用的很少或简直没有,如:

  • 存储 CSI 用的很少,次要应用的是 blob 存储
  • 网络 CNI 用了,但 Pod 次要用的是 hostNetwork
  • DNS 用的也不多
  • Service 用的很少,Pod 次要用的是 hostNetwork
  • Ingress(即上文说的 Kubernetes 负载平衡)用的很少,因为 Kubernetes 集群次要用于试验(当初随着 ChatGPT 的大规模应用可能会用的比之前多一些)
  • Kubernetes 的一些高级公布策略,如 A/B 测试,蓝 / 绿或金丝雀也不须要

所以我集体认为(观点仅供参考), Kubernetes 对于 OpenAI 来说,还是有些过于简单和性能冗余的。

OpenAI 真正须要的,是一个纯正的 容器编排 解决方案,特地是对 Job 的调度能力。

所以我感觉啊,不思考用户规模,不思考 Kubernetes 是容器编排畛域的事实上的规范的话,HashiCorp 的 Nomad 反而是更适合的解决方案。以下是具体理由:

  • Nomad 是一个易于应用、灵便和高性能的工作负载调度器,能够部署混合的微服务、批处理 容器化 和非容器化利用。
  • GPU 反对:Nomad 为 GPU 工作负载(如机器学习(ML)和人工智能(AI))提供内置反对。Nomad 应用设施插件来自动检测和利用来自硬件设施(如 GPU、FPGA 和 TPU)的资源。
  • 通过验证的可扩展性:Nomad 乐观地并发,可进步吞吐量并缩小工作负载的提早。Nomad 已被证实能够在理论生产环境中扩大到 10K+ 节点的集群。
  • 简略性:Nomad 作为单个过程运行,内部依赖性为零。运维人员能够轻松配置、治理和扩大 Nomad。开发人员能够轻松 定义并运行应用程序。
  • Nomad 的其中 2 个 调度器: Batch 和 System Batch, 十分符合 OpenAI 的应用场景。
  • Nomad 并不附带服务发现及网络的相干性能,只是在 1.3 版本当前, 减少了内置的本地服务发现(SD),使得 Consul 或其余第三方工具非必要。Nomad 的服务发现并不是要取代这些工具。相同,它是一个替代品,能够更容易地测试和部署更简略的架构。

2.5 横向扩大小技巧

另外,惊喜的发现 OpenAI 的博文中居然提到了 Kubernetes 横向扩大的小技巧,OpenAI 将其称为:CPU & GPU balloons.

笔者这里具体向大家介绍一下:

2.5.1 横向扩大的工夫悖论

首先,OpenAI 的横向扩大需要波及 2 个层面:

  • Kubernetes 集群层面的 Node 扩大,通过 Cluster AutoScaler 实现(这里 OpenAI 用的是本人开发的,个别各个私有云都提供对应的 Cluster AutoScaler 插件), 为的是疾速横向扩大 Node
  • Pod 层面的 横向扩大 (HPA), 为的是疾速横向 Pod 的数量。

然而,在流量或业务量飙升的状况下,Node(也就是云虚拟机)的扩大并不像 Pod 那么迅速,个别是须要几分钟的初始化和启动的工夫,进而影响到 Pod 的横向扩大,导致无奈及时响应业务飙升的需要。

2.5.2 解决方案概述

为此,解决方案就是 CPU & GPU balloons, 具体如下:

在新 Node 上创立新 Pod 所需的工夫由四个次要因素决定:

  • HPA(Horizontal Pod Autoscaler) 响应工夫。
  • Cluster Autoscaler 响应工夫。
  • Node 配置工夫
  • Pod 创立工夫

这里次要耗时是 Node 配置的工夫,这次要取决于云提供商。

一个新的计算资源在 3 到 5 分钟 内实现配置是很规范的。

在新 Node 上创立新 Pod 所需的工夫预估须要 7 min 左右。

如果你须要一个新的 Node,你如何调整主动缩放以缩小 7 分钟的缩放工夫?

大部分工夫花在了 Node 配置上,因为你不能扭转云供应商提供资源的工夫,所以就须要一个变通办法:

即:被动创立节点(超配),这样当你须要它们时,它们曾经被配置好了。

始终确保有一个备用节点可用

  1. 创立一个节点并将其留空(其实是搁置一个 balloon pod 来占用该节点)。
  2. 如果空节点中有 Pod(非 balloon pod) 就会创立另一个空节点。

这里,能够运行具备足够 requests 的 deployment 来保留整个节点。

能够将此 pod 视为占位符 — 它旨在保留空间,而不是应用任何资源。

一旦创立了一个真正的 Pod,您就能够逐出占位符并部署 Pod。

具体怎么实现呢?

2.5.3 具体实现

  • 具备 requests 的 Pod
  • Pod 部署优先级 (PodPriorityClass) 和抢占

如果您的节点实例是 2 vCPU 和 8GB 内存,那么 Pod 的可用空间应该为 1.73 vCPU 和 ~5.9GB 内存 (OS, kubelet, kubeproxy 等须要预留肯定资源),以使得 Pod 独占该 Node。

具体资源需要能够如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: overprovisioning
spec:
  replicas: 1
  selector:
    matchLabels:
      run: overprovisioning
  template:
    metadata:
      labels:
        run: overprovisioning
    spec:
      containers:
        - name: pause
          image: registry.k8s.io/pause
          resources:
            requests:
              cpu: '1739m'
              memory: '5.9G'

而后,要确保在创立真正的 Pod 后立刻逐出该 Pod,须要应用 优先级和抢占。

Pod 优先级示意 Pod 绝对于其余 Pod 的重要性。

当无奈调度 Pod 时,调度程序会尝试抢占(逐出)优先级较低的 Pod 来调度挂起的(优先级较高的)Pod。

能够应用 PodPriorityClass 在集群中配置 Pod 优先级:

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: overprovisioning
value: -1
globalDefault: false
description: 'Priority class used by overprovisioning.'

因为 Pod 的默认优先级是 0,而超配的 PriorityClass 的值是 -1,所以当集群的空间耗尽时,这个 Pod 会被首先驱赶。

之前的 Deployment 能够调整为:(spec 中 减少 priorityClassNameoverprovisioning)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: overprovisioning
spec:
  replicas: 1
  selector:
    matchLabels:
      run: overprovisioning
  template:
    metadata:
      labels:
        run: overprovisioning
    spec:
      priorityClassName: overprovisioning
      containers:
        - name: reserve-resources
          image: registry.k8s.io/pause
          resources:
            requests:
              cpu: '1739m'
              memory: '5.9G'

当集群中没有足够的资源时,占位的 pod 会被抢占,新的 pod 会取代它们的地位。

因为占位 pod 变得不可调度,它迫使 Cluster Autoscaler 向集群增加更多节点。

🎉🎉🎉

三、总结

本文通过学习 OpenAI 博客中 Kubernetes 相干博文,学到了很多超大规模 Kubernetes 集群下的调优技巧。

在这里梳理了 OpenAI 遇到的性能问题及解决方案,同时就横向扩大小技巧(占位🎈) 进行了具体阐明。

同时,联合笔者的教训,也做出一些延长思考:

  • 在 Kubernetes 集群中,

    • Metrics 监控:举荐应用 VictoriaMetrics 和 Grafana Labs 公布的 Mimir 替换 Prometheus
    • 镜像工具:举荐应用 DragonFly, 利用 P2P 和预热性能缓解镜像拉取问题
  • 在容器 / 批处理编排调度解决方案中,能够尝试抉择 HashiCorp 的 Nomad 替换 Kubernetes.

以上。

参考文档

  • Scaling Kubernetes to 2,500 nodes (openai.com)
  • Scaling Kubernetes to 7,500 nodes (openai.com)
  • Prometheus 性能调优 – 什么是高基数问题以及如何解决?- 东风微鸣技术博客 (ewhisper.cn)
  • 大规模 IoT 边缘容器集群治理的几种架构 -2-HashiCorp 解决方案 Nomad – 东风微鸣技术博客 (ewhisper.cn)
  • Architecting Kubernetes clusters — choosing the best autoscaling strategy (learnk8s.io)
  • Grafana 系列文章(一):基于 Grafana 的全栈可察看性 Demo(包含 Mimir) – 东风微鸣技术博客 (ewhisper.cn)

三人行, 必有我师; 常识共享, 天下为公. 本文由东风微鸣技术博客 EWhisper.cn 编写.

退出移动版