大规模基础设施¶
构建服务数百万用户的系统需要的远不止一台 server。本文涵盖可扩展性模式、分布式系统基础、microservice、数据流水线、数据库扩展、搜索与向量系统、可观测性、可靠性工程以及 CI/CD
- 每秒处理 1 个请求的 model 可以在笔记本电脑上运行。以 99.9% 可用性每秒处理 100,000 个请求则需要分布式系统、自动化故障切换和精心设计的数据流水线。本文涵盖弥合这一差距的模式。
可扩展性¶
-
垂直扩展(纵向扩展):换用更大的机器。更多 CPU、更多 RAM、更大的 GPU。简单,但有硬上限(可用最大机器)且存在单点故障。
-
水平扩展(横向扩展):增加更多机器。每台机器承担一部分流量。没有单机限制,但需要:load balancing(第 01 文)、数据分区以及处理分布式状态。
-
无状态服务默认可水平扩展。在 load balancer 后面增加更多实例即可。在启动时加载权重并独立处理请求的 model inference server 是无状态的——任何实例都能处理任意请求。
-
有状态服务(数据库、KV-cache、feature store)更难扩展。状态必须在多台机器间分区(sharding,第 01 文),并进行复制以实现容错。
-
可扩展性方程:对于有 \(n\) 台 server 的系统:
- 理想情况:吞吐量线性扩展(\(n\) 台 server → \(n\) 倍吞吐量)。
- 实际情况:协调、load balancing 和数据传输的开销导致吞吐量呈亚线性扩展。Amdahl 定律(第 13 章)适用:串行部分(共享状态、协调)限制了加速比。
分布式系统¶
-
分布式系统是一组相互协作以提供服务的机器。核心挑战:
-
网络分区:机器并不总能相互通信。网线被切断、交换机故障、数据中心断电。系统必须能处理部分故障。
-
时钟偏差:机器有不同的时钟。"机器 1 上事件 A 发生于 10:00:01"和"机器 2 上事件 B 发生于 10:00:01"并不意味着它们同时发生。逻辑时钟(Lamport 时间戳、向量时钟)在不依赖物理时钟的情况下建立事件顺序。
-
共识:多台机器如何就某个值(例如谁是 leader)达成一致?Raft 是标准共识算法。节点集群选出一个 leader。leader 处理所有写入。若 leader 失败,剩余节点选出新 leader。需要多数票(5 个节点中的 3 个)才能运行,因此能容忍 \(\lfloor(n-1)/2\rfloor\) 个故障。
-
分布式锁:确保只有一台机器执行某项关键操作。Redlock(基于 Redis)在多个 Redis 实例上获取锁。若多数实例授予锁,则锁被获取。用途:防止重复部署 model,确保只有一个训练任务写入检查点。
Microservice¶
- Microservice 将系统分解为小型、可独立部署的服务。每个服务拥有一个领域:
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ API Gateway │→ │ Feature Svc │→ │ Feature DB │
└─────────────┘ └──────────────┘ └─────────────┘
│
├────────→ ┌──────────────┐ ┌─────────────┐
│ │ Model Svc │→ │ Model Store │
│ └──────────────┘ └─────────────┘
│
└────────→ ┌──────────────┐ ┌─────────────┐
│ Logging Svc │→ │ Log Store │
└──────────────┘ └─────────────┘
-
优势:独立部署(更新 model 服务无需改动 feature 服务)、独立扩缩(根据请求负载扩缩 model server,根据 feature store 读取速率扩缩 feature server)、技术自由(model 服务用 Python,feature 服务用 Go)。
-
劣势:网络开销(每次服务调用都是一次网络往返)、复杂性(调试横跨多个服务)、数据一致性(不支持跨服务事务)。
-
服务发现:API gateway 如何找到 model 服务?选项:基于 DNS(每个服务注册一个 DNS 名)、K8s services(内置)或服务注册中心(Consul、Eureka)。
-
Saga 模式:对于跨多个服务的操作(创建用户 + 分配资源 + 发送欢迎邮件),使用 saga:一系列本地事务,若任意步骤失败则执行补偿操作。
数据流水线¶
- ML 系统消耗大量数据。数据流水线负责移动、转换和提供这些数据:
批处理¶
-
定期(每小时、每天)处理大量数据。
-
MapReduce:最初的批处理范式。Map(独立转换每条记录)→ Shuffle(按 key 分组)→ Reduce(按组聚合)。概念简单,但实现冗长。
-
Apache Spark:现代批处理引擎。内存处理(对迭代算法比 MapReduce 快 100 倍)。支持 SQL、DataFrame 和 ML 流水线。大规模 feature 工程的标准工具。
-
示例:为推荐系统计算用户 feature。输入:过去 30 天的 10 亿条用户活动事件。输出:1 亿条用户 feature 向量。作为 Spark 任务每天运行,输出到 feature store。
流处理¶
-
实时处理到达的数据(亚秒级延迟)。
-
Apache Flink:领先的流处理引擎。恰好一次处理、事件时间处理(按事件发生时间而非到达时间处理)、窗口(滚动窗口、滑动窗口、会话窗口)。
-
Kafka Streams:内置在 Kafka 中的轻量级流处理。适用于无需部署独立集群的简单转换(过滤、聚合)。
-
示例:实时欺诈检测。每笔信用卡交易都是 Kafka 事件。Flink 任务计算运行统计数据(交易频率、位置变化),并在 100ms 内标记异常。
Lambda 架构¶
-
结合批处理和流处理。批处理层提供准确、全面的结果(但有延迟)。速度层提供近似的实时结果。服务层合并两者。
-
实践中,许多团队现在使用 Kappa 架构:仅用流处理,以流作为真相来源。流可以重放(Kafka 保留事件),因此可以通过重放流来模拟批处理。
ML 训练基础设施¶
- 训练一个前沿 model(100B+ 参数)是大规模基础设施问题:数千块 GPU 运行数月,消耗兆瓦级电力,产生 PB 级数据,耗资数千万美元。基础设施决定训练是否能成功。
GPU 集群¶
- 训练集群是通过高速网络连接的 GPU server 集合。核心组件:
-
GPU server(节点):每台 server 有 4-8 块 GPU。典型配置:8 × H100 GPU、2 × AMD EPYC CPU、2 TB RAM、30 TB NVMe SSD。节点内的 GPU 通过 NVLink 连接(H100 上每块 GPU 900 GB/s),比 PCIe 快 30 倍。
-
集群规模:小型训练集群有 64-256 块 GPU(8-32 个节点)。前沿 model 训练集群有 4,000-32,000 块 GPU(500-4000 个节点)。Meta 的 Llama 3 使用了 16,384 块 H100 GPU。Google 在拥有 8,000+ 芯片的 TPU pod 上训练。
-
粗略估算:训练一个 70B model 需要约 $200 万算力成本。训练一个 400B+ 前沿 model 需要约 $5,000 万到 $1 亿。集群硬件本身约 $5 亿到 $10 亿(H100 每块 $30K × 16,000 块 = $4.8 亿)。
网络拓扑¶
-
GPU 节点间的网络是最关键的基础设施组件。若 GPU 交换梯度的速度不够快,它们会空闲等待通信完成。
-
InfiniBand 是 GPU 集群网络的标准。NVIDIA 的 Quantum-2 InfiniBand 每端口提供 400 Gb/s。每个节点通常有 8 个 InfiniBand 端口(每块 GPU 一个),提供每节点 400 GB/s 的总对分带宽。
-
RDMA(Remote Direct Memory Access):InfiniBand 支持 RDMA,可在不同节点的 GPU 内存之间直接传输数据,无需 CPU 参与。这将延迟从约 100μs(TCP)降低到约 1μs,对高效的梯度 all-reduce(第 6 章)至关重要。
-
网络拓扑很重要:胖树(Clos 网络)提供全对分带宽(任意 GPU 可以全速与其他 GPU 通信)。更便宜的拓扑(轨道优化、3D 环面)带宽较少但成本较低。拓扑必须与并行策略匹配:
- 数据并行:所有 GPU 上的 all-reduce → 需要高对分带宽(胖树)。
- 张量并行:节点内通信 → NVLink 处理(无需网络)。
- 流水线并行:相邻流水线阶段间通信 → 只需特定节点对间的带宽(轨道优化即可)。
-
以太网替代:RoCE v2(基于融合以太网的 RDMA)在标准以太网基础设施上提供 RDMA。比 InfiniBand 便宜,但延迟更高且拥塞更多。Google 在部分 TPU pod 网络中使用 RoCE。Ultra Ethernet 联盟正在为 AI 工作负载开发无损以太网。
训练存储¶
-
训练需要三层存储:
-
数据集存储:训练语料库(1-100 TB 文本,或 PB 级多模态数据)。存储在分布式文件系统或对象存储中。必须支持高吞吐量顺序读取(数据加载器大批量读取数据)。Lustre 和 GPFS 是常用的 HPC 文件系统;cloud 替代品包括 FSx for Lustre(AWS)和 Filestore(GCP)。
-
检查点存储:定期保存的训练状态(model 权重 + 优化器状态 + 调度器状态)。混合精度下含 Adam 优化器的 70B model:每个检查点约 560 GB(70B × 4 字节 × 2 用于优化器)。3 个月训练每小时保存一次 = 约 2000 个检查点 = 1.1 PB。实践中只保留最新的 N 个检查点,旧的删除。保存速度必须足够快,以免显著减慢训练。
-
日志和指标:实验追踪数据(loss 曲线、学习率计划、梯度范数)。数据量相对较小,但必须实时写入。W&B、MLflow 或 TensorBoard 处理这些。
-
-
存储瓶颈:一个 16,000 GPU 的集群需要持续以约 100 GB/s 读取训练数据。若文件系统无法维持这一吞吐量,GPU 就会空闲等待数据。数据流水线优化(预取、缓存、使用 WebDataset 或 Mosaic Streaming 的格式优化)至关重要。
任务调度¶
-
GPU 集群服务于多个团队和项目。任务调度器为训练任务分配 GPU:
-
SLURM:标准 HPC 任务调度器。用户提交任务时指定 GPU 数量、内存和时间限制。SLURM 分配资源并管理队列。支持基于优先级的调度、抢占以及团队间的公平分配。
-
带 GPU 调度的 Kubernetes(第 18 章第 02 文):cloud 原生方法。K8s GPU device plugin 将 GPU 作为可调度资源暴露。Volcano 和 Run:ai 添加 ML 专用调度功能:团队调度(一次性为任务分配所有 GPU,而非逐个分配)、优先级队列和 GPU 时间共享。
-
调度挑战:
- 碎片化:有 1000 块 GPU 的集群可能有 200 块空闲,但分散在 50 个节点上(每节点 4 块空闲)。即使总量足够,需要 128 块连续 GPU 的任务也无法运行。碎片整理(迁移任务以整合空闲 GPU)或拓扑感知调度(分配连接良好的 GPU)可解决此问题。
- 优先级与抢占:紧急实验应抢占低优先级任务。但抢占已运行 2 天的训练任务会浪费算力。调度器必须在优先级和效率间取得平衡。
- 公平分配:各团队应随时间获得其分配的算力份额,即使某个团队提交的任务超过其份额。
容错¶
-
在数千块 GPU 运行数月的规模下,硬件故障不是例外——而是常态。16,000 GPU 集群的平均故障间隔时间以小时计,而非月计。
-
常见故障:GPU 内存错误(ECC 可纠正和不可纠正)、NVLink 故障(节点内 GPU 间通信)、InfiniBand 链路故障(节点间通信)、节点崩溃(内核恐慌、电源故障)和存储故障(磁盘或控制器故障)。
-
检查点是主要防御手段。每 N 步保存完整训练状态(model、优化器、数据加载器位置)。故障时:识别故障节点,替换或移除,从最新检查点重启训练。故障的代价是最后一个检查点到故障时刻之间的算力损失。
-
检查点频率权衡:频繁检查点(每 10 分钟)故障时损失更少算力,但会降低训练速度(保存 560 GB 需要时间)。不频繁检查点(每 2 小时)速度更快,但故障时最多损失 2 小时算力。大多数团队每 20-60 分钟设置一次检查点。
-
弹性训练:现代框架(PyTorch Elastic、DeepSpeed)支持无需重启即可调整训练规模。若 500 个节点中有 2 个故障,训练继续在 498 个节点上运行。故障节点被替换后,训练自动将其纳入。
-
健康监控:持续监控所有 GPU(温度、内存错误、计算吞吐量)、网络链路(丢包率、延迟)和存储(吞吐量、错误率)。异常时自动告警。部分集群会定期运行 GPU 健康检查(短暂的计算测试),在硬件完全故障前主动识别退化硬件。
-
规模效应:训练 Meta 的 Llama 3(16,384 块 H100,54 天)经历了约 466 次任务中断。有效训练时间仅为实际时钟时间的约 90%——10% 的时间损失于故障和恢复。将有效训练率维持在 90%(而非 50% 或 70%)的基础设施,是能训练前沿 model 的组织与不能的组织之间的核心差距。
成本与效率¶
- 训练基础设施成本主要由 GPU 小时数决定:
| 组件 | 占总成本比例 |
|---|---|
| GPU 算力 | 70-80% |
| 网络(InfiniBand) | 10-15% |
| 存储 | 5-10% |
| 冷却与电力 | 5-10% |
-
GPU 利用率(Model FLOPs Utilisation,MFU)衡量 GPU 理论峰值性能中实际用于有效计算的比例。H100 峰值为 989 TFLOPS(FP8)。达到 40-50% MFU 是良好水平;50-60% 是优秀水平。差距来自:通信开销(all-reduce、流水线气泡)、内存带宽限制以及检查点和数据加载期间的空闲时间。
-
提升 MFU:重叠计算与通信(第 6 章)、使用高效 attention(Flash Attention,第 16 章)、优化数据加载(防止 GPU 饥饿)、减少检查点开销(异步检查点,先保存到快速 NVMe,再后台复制到持久存储)。
-
自建 vs 购买:小规模(<256 GPU)时,cloud 更便宜(无前期成本,按小时付费)。大规模(>1000 GPU,持续使用超过 6 个月)时,拥有硬件更便宜(3 年内 TCO 约低 2-3 倍)。大多数 AI 公司采用混合方式:自建集群用于持续训练,cloud 用于突发容量和实验。
数据库扩展¶
-
读副本:将读查询路由到主数据库的副本。主数据库处理写入,副本处理读取。由于大多数工作负载以读为主(95%+ 的操作是读取),这样可以随副本数量线性扩展读吞吐量。
-
分区(sharding,来自第 01 文):将数据分散到多个数据库。每个分区独立运行,支持并行读写。挑战是跨分区查询(需要 join 来自不同分片的数据)。
-
连接池:数据库的连接容量有限。连接池(PostgreSQL 的 PgBouncer)在请求间复用连接,防止数百个服务实例各自连接时耗尽连接数。
搜索与向量系统¶
文本搜索¶
-
倒排索引:文本搜索的基础。对每个词,存储包含该词的文档列表。查询时对各查询词的列表取交集。Elasticsearch 是标准选择:分布式、实时,支持全文搜索、聚合和地理空间查询。
-
BM25:标准文本检索评分函数。通过词频、逆文档频率和文档长度归一化对文档评分。简单但有效——对关键词查询仍与神经方法相当。
向量搜索¶
-
向量数据库存储 embedding(高维向量),支持快速近似最近邻(ANN)搜索。给定查询 embedding,找出 \(k\) 个最相似的存储 embedding。
-
FAISS(Facebook AI Similarity Search):ANN 搜索库(非数据库)。支持多种索引类型:
- Flat:精确搜索,\(O(n)\)。用于小数据集或作为基准。
- IVF(倒排文件):将向量划分为簇,仅搜索最近的簇。每次查询 \(O(n/k)\)。
- HNSW(分层可导航小世界):基于图。构建分层图,从粗到细导航。速度极快且精度高,是大多数应用的默认选择。
- 乘积量化(PQ):将向量压缩为紧凑编码,用于内存高效搜索。以精度换内存。
-
托管向量数据库:Pinecone、Weaviate、Milvus、Qdrant。它们处理 FAISS 不具备的扩展、复制和实时更新。
-
对于 RAG(Retrieval-Augmented Generation):用户查询 → 用文本编码器 embedding → 在向量数据库中搜索相关文档 → 将检索到的文档添加到 LLM prompt 前。检索质量直接决定 LLM 响应的质量。
可观测性¶
- 可观测性是从系统的外部输出理解内部运行状态的能力。三大支柱:
日志¶
-
结构化日志(JSON)可搜索且可解析。非结构化日志("ERROR: something failed")则不行。始终记录:时间戳、服务名称、请求 ID(用于跨服务追踪)、严重级别以及相关上下文。
-
ELK 栈(Elasticsearch、Logstash、Kibana):标准日志流水线。Logstash 收集和转换日志,Elasticsearch 建立索引,Kibana 可视化和搜索。
指标¶
-
指标是随时间变化的数值测量:请求速率、错误率、延迟百分位数、GPU 利用率、队列深度。Prometheus 从服务抓取指标;Grafana 在带告警功能的仪表板中可视化它们。
-
RED 方法(针对服务):Rate(请求数/秒)、Errors(错误率)、Duration(延迟)。为每个服务监控这些指标。
-
USE 方法(针对资源):Utilisation(使用率%)、Saturation(队列深度)、Errors(错误)。为每种资源(CPU、GPU、内存、磁盘、网络)监控这些指标。
链路追踪¶
-
分布式追踪跟踪单个请求跨多个服务的路径。用户请求到达 API gateway → feature 服务 → model 服务 → 后处理。一条 trace 记录每一跳的时序,显示延迟花费在哪里。
-
OpenTelemetry:trace、指标和日志的开放标准。只需一次代码插桩,即可导出到任意后端(Jaeger、Zipkin、Datadog)。
可靠性¶
-
SLO(Service Level Objective):目标可靠性。"99.9% 的请求在 <200ms 内完成。"这给出了具体的错误预算:0.1% 的请求(每月约 43 分钟)可以缓慢或失败。
-
SLI(Service Level Indicator):衡量指标。"过去 5 分钟的第 99 百分位延迟。"
-
SLA(Service Level Agreement):有后果的合同承诺。"若可用性低于 99.95%,客户获得抵扣。"
-
错误预算:若 SLO 是 99.9% 而实际是 99.99%,则有预算进行高风险变更(部署新 model、迁移数据库)。若实际是 99.85%,则冻结所有变更并专注于可靠性。错误预算将可靠性从抽象目标变为可度量的资源。
-
混沌工程:故意注入故障(关闭 server、添加网络延迟、损坏数据)以测试系统是否能正确处理。Netflix 的 Chaos Monkey 随机终止生产实例。若系统保持运行,则具有弹性。若崩溃,你在用户发现之前找到了 bug。
CI/CD¶
-
持续集成:自动构建和测试每次代码变更。每次推送触发:代码检查、类型检查、单元测试、集成测试。任意测试失败则拒绝变更。这在代码到达生产前捕获 bug。
-
持续部署:自动部署通过 CI 的变更。部署策略:
-
蓝绿(Blue-green):运行两个相同的环境(蓝 = 当前,绿 = 新)。即时从蓝切换到绿。若绿失败,立即切回蓝(即时回滚)。
-
金丝雀(Canary):将一小部分流量(1-5%)路由到新版本。监控错误。若指标良好则逐渐增加流量。这限制了糟糕部署的影响范围。
-
功能开关(Feature flags):部署新代码但隐藏在开关后面。为部分用户启用(内部测试人员,然后 beta 用户,然后所有人)。将部署(代码上线)与发布(用户看到功能)解耦。
-
-
对于 ML:CI/CD 包括 model 专用步骤。model 变更触发:单元测试(形状测试、梯度检查)、在持出集上评估(准确率不应回退)、shadow deployment(新 model 与旧 model 并行运行,比较输出)以及逐步上线(金丝雀从 1% → 100%)。