第7章:分布式系统架构

"我们不是在构建服务器,而是在构建一台行星级的计算机。" - Luiz André Barroso

概述

Google的分布式系统架构代表了21世纪计算机科学最重要的创新之一。从最初的几台服务器到如今横跨全球的数百万台机器,Google不仅解决了前所未有的规模挑战,更重要的是,他们将这些解决方案抽象化、论文化,深刻影响了整个工业界。

本章将深入探讨Google分布式系统的核心组件、设计理念和架构演进,从工程师视角理解这些系统如何协同工作,支撑起每天处理数十亿请求的服务。

7.1 架构演进历程

7.1.1 初创时期 (1998-2002):从车库到机房

硬件哲学的诞生

1998年,Larry Page和Sergey Brin在斯坦福大学的宿舍里组装了第一批服务器。这些服务器使用廉价的商用硬件(COTS - Commercial Off-The-Shelf),奠定了Google独特的硬件哲学基础。

早期架构 (1998-1999)
┌──────────────────────────────────────┐
│          用户浏览器                   │
└────────────────┬─────────────────────┘
                 │HTTP
         ┌───────▼────────┐
         │   Web Server   │
         │  (Apache/自研) │
         └───────┬────────┘
                 │
    ┌────────────┼────────────┐
    ▼            ▼            ▼
┌────────┐  ┌────────┐  ┌────────┐
│Index   │  │Crawler │  │PageRank│
│Server  │  │Server  │  │Server  │
└────────┘  └────────┘  └────────┘
    │            │            │
    └────────────┼────────────┘
                 ▼
         ┌──────────────┐
         │  File System │
         │   (单机)     │
         └──────────────┘

关键决策

  • 使用廉价PC而非昂贵的服务器
  • 软件层面处理硬件故障
  • 横向扩展而非纵向扩展

7.1.2 基础设施奠基 (2003-2006):三驾马车

这个时期,Google发表了三篇影响深远的论文,被称为分布式系统的"三驾马车":

  1. Google File System (GFS) - 2003 - 解决问题:海量数据的可靠存储 - 核心贡献者:Sanjay Ghemawat, Howard Gobioff, Shun-Tak Leung

  2. MapReduce - 2004 - 解决问题:大规模数据并行处理 - 核心贡献者:Jeffrey Dean, Sanjay Ghemawat

  3. Bigtable - 2006 - 解决问题:结构化数据的分布式存储 - 核心贡献者:Fay Chang, Jeffrey Dean, Sanjay Ghemawat等

三驾马车架构关系
        ┌─────────────────────┐
        │     应用层          │
        │ (Search, Gmail等)   │
        └──────────┬──────────┘
                   │
        ┌──────────▼──────────┐
        │    MapReduce        │
        │  (计算框架)         │
        └──────────┬──────────┘
                   │
    ┌──────────────┼──────────────┐
    ▼              ▼              ▼
┌─────────┐  ┌─────────┐  ┌─────────┐
│Bigtable │  │直接访问 │  │其他存储 │
│结构化   │  │  GFS    │  │  系统   │
└────┬────┘  └─────────┘  └─────────┘
     │              │              │
     └──────────────┼──────────────┘
                    ▼
          ┌───────────────────┐
          │       GFS         │
          │ (分布式文件系统)  │
          └───────────────────┘

7.1.3 规模化时代 (2007-2012):全球化架构

Borg系统的诞生

2007年左右,Google内部开始使用Borg系统进行大规模集群管理。虽然Borg论文直到2015年才发表,但这个系统从2007年就开始支撑Google的所有服务。

Borg时代的数据中心架构
┌────────────────────────────────────────┐
│          全球负载均衡器                │
│         (Maglev/GSLB)                  │
└─────────┬──────────────────────────────┘
          │
    ┌─────┼─────┬──────────┬──────────┐
    ▼     ▼     ▼          ▼          ▼
┌───────────────────┐  ┌───────────────────┐
│   美国数据中心    │  │   欧洲数据中心    │
│  ┌─────────────┐  │  │  ┌─────────────┐  │
│  │ Borg Master │  │  │  │ Borg Master │  │
│  └──────┬──────┘  │  │  └──────┬──────┘  │
│         │         │  │         │         │
│  ┌──────▼──────┐  │  │  ┌──────▼──────┐  │
│  │  Borglets   │  │  │  │  Borglets   │  │
│  │ (1000s节点) │  │  │  │ (1000s节点) │  │
│  └─────────────┘  │  │  └─────────────┘  │
└───────────────────┘  └───────────────────┘

Spanner的突破

2012年,Google发表Spanner论文,首次实现了全球分布式的强一致性数据库。

关键创新:

  • TrueTime API:全球时钟同步
  • 多版本并发控制(MVCC)
  • 自动数据分片和迁移

7.1.4 云原生时代 (2013-2020):开源与标准化

Kubernetes的诞生

2014年,Google开源Kubernetes,将Borg的经验带给整个行业。

Kubernetes vs Borg 对比
┌─────────────────────────────────────┐
│            Borg (内部)              │
├─────────────────────────────────────┤
│ - 专有系统,深度定制                │
│ - 单体架构                          │
│ - 与Google基础设施紧密集成          │
│ - 支持数百万容器                    │
└─────────────────────────────────────┘
                ↓ 经验提炼
┌─────────────────────────────────────┐
│         Kubernetes (开源)           │
├─────────────────────────────────────┤
│ - 通用容器编排                      │
│ - 微服务架构                        │
│ - 云厂商中立                        │
│ - 可扩展API                         │
└─────────────────────────────────────┘

7.1.5 AI基础设施时代 (2020-至今)

随着AI工作负载的爆发式增长,Google的分布式系统架构进入新阶段。

新一代架构特征

  • TPU Pod:专用AI计算集群
  • 流式处理:Dataflow
  • 无服务器:Cloud Run
  • 边缘计算:分布式推理
现代Google架构栈 (2024)
┌──────────────────────────────────┐
│         应用层                   │
│  (Search, YouTube, Gmail等)      │
└────────────┬─────────────────────┘
             │
┌────────────▼─────────────────────┐
│         服务网格                 │
│    (Istio/Envoy based)          │
└────────────┬─────────────────────┘
             │
┌────────────▼─────────────────────┐
│      容器编排层                  │
│    (Borg + Kubernetes)          │
└────────────┬─────────────────────┘
             │
┌────────────▼─────────────────────┐
│      统一存储层                  │
│  (Colossus + Spanner + Bigtable)│
└────────────┬─────────────────────┘
             │
┌────────────▼─────────────────────┐
│      硬件抽象层                  │
│   (CPU + GPU + TPU + 网络)      │
└──────────────────────────────────┘

7.2 GFS:分布式文件系统的开创者

7.2.1 设计背景与挑战

2003年,Google面临的存储挑战:

  • 数TB级别的数据存储需求
  • 硬件故障是常态而非异常
  • 文件大小从几KB到几GB不等
  • 大部分是追加写入而非随机写入

设计假设

  1. 系统由廉价的商用组件构成,故障频繁
  2. 存储大文件(通常100MB以上)
  3. 工作负载主要是大规模流式读取和追加写入
  4. 应用和文件系统API可以协同设计

7.2.2 架构设计

GFS架构图
                 ┌──────────────┐
                 │  GFS Client  │
                 └──────┬───────┘
                        │
        ┌───────────────┼───────────────┐
        │               │               │
        ▼               ▼               ▼
┌──────────────┐  控制流 ┌──────────────┐
│  GFS Master  │◄────────┤应用程序      │
│              │         │              │
│ ·命名空间    │         └──────┬───────┘
│ ·元数据      │                │数据流
│ ·chunk位置   │                │
└──────┬───────┘                │
       │                        │
       │控制消息                │
       │                        │
   ┌───┼────────────────────────┼───┐
   ▼   ▼                        ▼   ▼
┌──────────┐  ┌──────────┐  ┌──────────┐
│Chunkserver│  │Chunkserver│  │Chunkserver│
│           │  │           │  │           │
│ ·64MB块   │  │ ·64MB块   │  │ ·64MB块   │
│ ·3副本    │  │ ·3副本    │  │ ·3副本    │
└───────────┘  └───────────┘  └───────────┘
   Linux文件系统    Linux文件系统    Linux文件系统

核心组件

  1. Master节点(单点) - 维护所有文件系统元数据 - 管理chunk租约和垃圾回收 - 处理chunk迁移和负载均衡

  2. Chunkserver(数千个) - 存储64MB的chunk - 每个chunk默认3个副本 - 直接与客户端交互进行数据传输

  3. 客户端库 - 实现文件系统API - 缓存元数据 - 直接与chunkserver通信

7.2.3 关键机制

  1. 租约机制(Lease)
写入流程

1. Client → Master: 请求写入文件
2. Master → Client: 返回主副本和其他副本位置
3. Client → 所有副本: 推送数据(流水线)
4. Client → 主副本: 发送写入请求
5. 主副本 → 其他副本: 转发写入请求
6. 其他副本 → 主副本: 确认完成
7. 主副本 → Client: 返回结果
  1. 一致性模型 - 定义一致(defined):所有客户端看到相同数据 - 一致(consistent):所有客户端看到相同数据,且是某次写入的完整结果 - GFS保证:追加操作是原子的且至少一次

  2. 容错机制

故障处理策略
┌────────────────────────────────┐
│         故障类型               │
├────────────────────────────────┤
│ Chunkserver故障:              │
│ - 心跳检测(60秒超时)         │
│ - 自动重新复制到3副本          │
│                                │
│ Master故障:                   │
│ - 操作日志复制                 │
│ - Shadow Master热备            │
│                                │
│ 数据损坏:                     │
│ - 32位校验和                  │
│ - 后台扫描验证                 │
└────────────────────────────────┘

7.2.4 性能优化

  1. 大块设计(64MB) - 减少元数据开销 - 减少客户端与master交互 - 提高网络利用率

  2. 流水线复制

数据流优化
Client ──────► Replica A ──────► Replica B ──────► Replica C
         最近          次近             最远
    (网络拓扑距离)
  1. 快照机制 - Copy-on-write实现 - 几乎瞬时完成 - 用于备份和实验

7.2.5 从GFS到Colossus

2010年后,Google开发了下一代文件系统Colossus:

| 特性 | GFS | Colossus |

特性 GFS Colossus
Master 单点 分布式(多个元数据服务器)
块大小 64MB固定 1MB可变
客户端缓存 有限 更智能的缓存
规模 PB级 EB级
延迟 毫秒级 微秒级
Colossus架构演进
         GFS (2003)                    Colossus (2010+)
    ┌──────────────┐              ┌─────────────────────┐
    │ Single Master│              │ Distributed Masters │
    └──────┬───────┘              └──────────┬──────────┘
           │                                  │
           │                      ┌───────────┼───────────┐
           │                      ▼           ▼           ▼
    ┌──────▼───────┐       ┌──────────┐ ┌──────────┐ ┌──────────┐
    │ Chunkservers │       │ Curator  │ │ Mixer    │ │ D-Server │
    │   (1000s)    │       │ (元数据) │ │ (负载)   │ │ (数据)   │
    └──────────────┘       └──────────┘ └──────────┘ └──────────┘

7.3 MapReduce:大数据处理的编程范式

7.3.1 诞生背景

2004年,Jeffrey Dean和Sanjay Ghemawat面临的问题:

  • Google需要处理数百TB的数据
  • 程序员花费大量时间处理分布式计算的复杂性
  • 需要一个简单的编程模型隐藏分布式细节

核心洞察: 大多数数据处理可以抽象为两个简单操作:Map和Reduce

7.3.2 编程模型

MapReduce计算模型
输入数据 ──────► Map阶段 ──────► Shuffle阶段 ──────► Reduce阶段 ──────► 输出结果
   │               │                  │                   │               │
   ▼               ▼                  ▼                   ▼               ▼
(K1,V1)  →  map(K1,V1)  →  list(K2,V2)  →  (K2,list(V2))  →  reduce(K2,list(V2))  →  list(K3,V3)

示例:词频统计
"hello world"  →  [("hello",1),("world",1)]  →  ("hello",[1,1])  →  ("hello",2)
"hello google" →  [("hello",1),("google",1)] →  ("world",[1])    →  ("world",1)
                                              →  ("google",[1])   →  ("google",1)

编程接口

// 用户只需实现这两个函数
class Mapper {
  virtual void Map(const string& key, const string& value,
                  OutputCollector* output) = 0;
};

class Reducer {
  virtual void Reduce(const string& key,
                     const Iterator<string>& values,
                     OutputCollector* output) = 0;
};

7.3.3 执行流程

MapReduce执行架构
                    ┌─────────────┐
                    │   Master    │
                    │  (调度器)   │
                    └──────┬──────┘
                           │
        ┌──────────────────┼──────────────────┐
        │                  │                  │
   分配Map任务        分配Reduce任务      监控和容错
        │                  │                  │
        ▼                  ▼                  ▼
┌───────────────────────────────────────────────────┐
│                    Worker节点池                    │
├───────────────────────────────────────────────────┤
│  ┌─────────┐  ┌─────────┐  ┌─────────┐         │
│  │Worker 1 │  │Worker 2 │  │Worker 3 │  ...    │
│  │执行Map  │  │执行Map  │  │执行Reduce│         │
│  └─────────┘  └─────────┘  └─────────┘         │
└───────────────────────────────────────────────────┘
        │                  │                  │
        ▼                  ▼                  ▼
    ┌────────┐        ┌────────┐        ┌────────┐
    │  GFS   │        │  GFS   │        │  GFS   │
    │输入文件│        │中间文件│        │输出文件│
    └────────┘        └────────┘        └────────┘

详细执行步骤

  1. 输入分片 - Master将输入文件分成16-64MB的片 - 创建M个Map任务和R个Reduce任务

  2. Map阶段 - Worker读取输入分片 - 解析出key/value对 - 调用用户Map函数 - 缓冲中间结果到内存

  3. Shuffle阶段 - Map输出按key哈希到R个分区 - 周期性写入本地磁盘 - 位置信息报告给Master

  4. Reduce阶段 - Reducer从Map worker拉取数据 - 按key排序合并 - 调用用户Reduce函数 - 输出写入GFS

7.3.4 容错机制

容错策略
┌─────────────────────────────────────────┐
│            Worker故障                   │
├─────────────────────────────────────────┤
│ Map Worker故障:                        │
│ - Master检测(心跳超时)                │
│ - 重新调度所有Map任务                  │
│ - 通知Reducer新位置                    │
│                                         │
│ Reduce Worker故障:                     │
│ - 重新调度未完成的Reduce任务           │
│ - 从Map Worker重新读取数据             │
└─────────────────────────────────────────┘

┌─────────────────────────────────────────┐
│            Master故障                   │
├─────────────────────────────────────────┤
│ - 周期性写入检查点                      │
│ - 故障后从检查点恢复                    │
│ - 或者直接重新执行整个作业              │
└─────────────────────────────────────────┘

7.3.5 优化技术

  1. 数据本地性
调度优化
优先级1: 本地数据 ──────► Worker和数据在同一机器
优先级2: 同机架   ──────► Worker和数据在同一机架
优先级3: 跨机架   ──────► Worker和数据在不同机架
  1. 备份任务(Backup Tasks) - 检测"掉队者"(stragglers) - 在作业快结束时启动备份执行 - 显著减少总完成时间(可达44%)

  2. Combiner函数

Combiner优化(局部聚合)
Map输出: [("the",1),("the",1),("the",1)]
    ↓ Combiner
本地聚合: [("the",3)]
    ↓
网络传输量减少

7.3.6 性能数据

Google 2004年的基准测试:

| 测试类型 | 数据量 | 机器数 | 总时间 | 吞吐率 |

测试类型 数据量 机器数 总时间 吞吐率
排序 1TB 1800 891秒 1.12GB/s
搜索 1TB 1800 150秒 6.67GB/s

真实应用案例

  • 网页索引:处理20TB数据
  • 日志分析:每天处理数PB日志
  • 机器学习:训练大规模模型

7.3.7 MapReduce的影响

开源实现

  • Apache Hadoop(2006):Yahoo主导
  • Apache Spark(2014):内存计算
  • Apache Flink(2015):流处理
MapReduce生态系统演进
        MapReduce (2004)
               │
    ┌──────────┼──────────┐
    ▼          ▼          ▼
 Hadoop     Spark      Flink
 (2006)     (2014)     (2015)
    │          │          │
批处理    内存计算    流处理

编程范式影响

  1. 函数式编程在大数据处理中的应用
  2. 声明式编程模型的流行
  3. 分布式计算框架的标准化

7.4 Bigtable:NoSQL数据库的先驱

7.4.1 设计动机

2006年,Google面临的结构化数据存储挑战:

  • 需要存储数百TB到PB级别的结构化数据
  • 支持数千台机器的横向扩展
  • 提供比GFS更丰富的数据模型
  • 实现高性能的随机读写访问

核心需求场景

  • 网页索引:存储数十亿网页的属性
  • Google Earth:管理海量地理图像数据
  • 用户数据:Gmail、Google Analytics等

7.4.2 数据模型

Bigtable是一个稀疏的、分布式的、持久化的多维排序映射:

(row:string, column:string, time:int64) → string

示例:网页表

行键: com.cnn.www (反向URL)
列族: contents:, anchor:
时间戳: t1, t2, t3...

┌─────────────────┬──────────────────────┬─────────────────────────┐
     Row Key        contents:html         anchor:cnnsi.com      
├─────────────────┼──────────────────────┼─────────────────────────┤
 com.cnn.www      t6: "<html>..."       t9: "CNN"               
                  t5: "<html>..."       t8: "CNN.com"           
                  t3: "<html>..."                               
├─────────────────┼──────────────────────┼─────────────────────────┤
 com.google.www   t10: "<html>..."      t11: "Google"           
└─────────────────┴──────────────────────┴─────────────────────────┘

数据模型特点

  1. : - 行键是任意字符串(最大64KB) - 按字典序排序存储 - 行的读写是原子的

  2. 列族: - 列键格式:family:qualifier - 列族必须预先创建 - 同一列族的数据物理存储在一起

  3. 时间戳: - 64位整数,微秒精度 - 可自动分配或应用指定 - 用于版本控制和垃圾回收

7.4.3 系统架构

Bigtable架构
                    ┌──────────────┐
                    │   客户端     │
                    └──────┬───────┘
                           │
                    ┌──────▼───────┐
                    │ Bigtable库   │
                    └──────┬───────┘
                           │
        ┌──────────────────┼──────────────────┐
        │                  │                  │
        ▼                  ▼                  ▼
┌──────────────┐    ┌──────────────┐  ┌──────────────┐
│    Master    │    │   Chubby     │  │ Tablet Server│
│              │◄───┤              │  │              │
│ ·分配tablets │    │ ·锁服务     │  │ ·管理tablets │
│ ·负载均衡   │    │ ·根tablet位置│  │ ·处理读写   │
│ ·垃圾回收   │    │ ·模式信息   │  │ ·分裂tablets │
└──────────────┘    └──────────────┘  └──────┬───────┘
                                              │
                                              ▼
                                    ┌──────────────┐
                                    │     GFS      │
                                    │  存储SSTable │
                                    └──────────────┘

关键组件

  1. Master服务器(单点) - 分配tablets到tablet服务器 - 检测tablet服务器的加入和失效 - 负载均衡和垃圾回收 - 处理模式变更(创建表、列族)

  2. Tablet服务器(数百到数千个) - 管理一组tablets(通常10-1000个) - 处理tablets的读写请求 - 在tablets过大时进行分裂

  3. Chubby锁服务 - 确保任何时刻最多一个活跃的master - 存储Bigtable数据的引导位置 - 发现tablet服务器和终结它们 - 存储Bigtable模式信息

7.4.4 Tablet定位

使用三层结构定位tablet:

Tablet定位层次结构
        ┌─────────────────┐
        │    Chubby       │
        │  (Root tablet   │
        │   location)     │
        └────────┬────────┘
                 │
        ┌────────▼────────┐
        │  Root tablet    │ (永不分裂,1个tablet)
        │  METADATA表     │
        └────────┬────────┘
                 │
        ┌────────▼────────┐
        │ Other METADATA  │ (可分裂,~2^34个tablets)
        │    tablets      │
        └────────┬────────┘
                 │
        ┌────────▼────────┐
        │  User tablets   │
        └─────────────────┘

定位计算:

- 128MB METADATA tablet
- 1KB per tablet元数据
- 可索引2^34个tablets
- 每个tablet 128MB
- 总计可存储2^61字节数据

7.4.5 Tablet服务

写入路径

写入流程

1. 写入 ──► memtable (内存,已排序)
2. memtable满 ──► 冻结,创建新memtable
3. 冻结的memtable ──► 转换为SSTable
4. SSTable ──► 写入GFS

内存结构:
┌──────────────────────────────┐
│      Tablet Server内存       │
├──────────────────────────────┤
│  ┌────────────────────────┐  │
│  │   提交日志 (WAL)       │  │
│  └────────────────────────┘  │
│  ┌────────────────────────┐  │
│  │   memtable (活跃)      │  │
│  └────────────────────────┘  │
│  ┌────────────────────────┐  │
│  │   memtable (冻结)      │  │
│  └────────────────────────┘  │
└──────────────────────────────┘

读取路径

读取合并流程

1. 检查授权和格式
2. 读取数据源:
   ├─► memtable (最新)
   ├─► 冻结的memtables
   └─► SSTable文件 (最旧)

3. 合并多个数据源的结果
4. 返回最终数据

7.4.6 压缩(Compaction)

压缩类型

  1. Minor Compaction - memtable → 新SSTable - 减少内存使用 - 缩短恢复时间

  2. Merging Compaction - 多个SSTable + memtable → 新SSTable - 减少SSTable数量 - 提高读取性能

  3. Major Compaction - 所有SSTable → 一个SSTable - 删除已标记删除的数据 - 回收存储空间

压缩过程示意
Minor Compaction:
memtable ──────────► SSTable_new

Merging Compaction:
SSTable_1 ─┐
SSTable_2 ─┼────► SSTable_merged
SSTable_3 ─┘

Major Compaction:
SSTable_1 ─┐
SSTable_2 ─┤
SSTable_3 ─┼────► SSTable_major (无删除标记)
SSTable_4 ─┤
SSTable_5 ─┘

7.4.7 性能优化

  1. 局部性群组(Locality Groups) - 将经常一起访问的列族分组 - 每个局部性群组生成独立的SSTable - 支持高效的列族扫描

  2. 压缩 - 两阶段压缩:Bentley-McIlroy + 快速压缩 - 典型压缩率:10:1 - 空间换时间的权衡

  3. 缓存

两级缓存结构
┌─────────────────────────────┐
│      Scan Cache             │
│  (键值对缓存,高层次)        │
└──────────────┬──────────────┘
               │
┌──────────────▼──────────────┐
│      Block Cache            │
│  (SSTable块缓存,低层次)    │
└─────────────────────────────┘
  1. Bloom过滤器 - 减少不必要的SSTable访问 - 极大提高读取性能 - 空间开销小(每个键几个位)

7.4.8 实际应用案例

Google Analytics (2006年数据)

  • 200个tablet服务器
  • 存储约1PB数据
  • 处理数百万个网站的分析数据
  • 原始点击表:~200TB
  • 摘要表:~20TB

Google Earth

  • 存储卫星图像数据
  • 单表超过70TB
  • 支持高并发的图像服务

个性化搜索

  • 存储用户搜索历史
  • 支持快速的用户查询
  • 数据复制到多个数据中心

7.4.9 Bigtable的影响与演进

开源实现

  • Apache HBase (2008):Hadoop生态系统的列族数据库
  • Apache Cassandra (2008):Facebook开发,结合Bigtable和Dynamo
  • Apache Accumulo (2011):NSA贡献,增强安全特性

对NoSQL运动的影响

Bigtable影响图谱
          Bigtable (2006)
               │
    ┌──────────┼──────────┐
    ▼          ▼          ▼
  HBase    Cassandra   Accumulo
    │          │          │
    ▼          ▼          ▼
 NoSQL运动  宽列存储   分布式KV

Google内部演进:从Bigtable到Spanner

  • Bigtable:最终一致性,单数据中心优化
  • Megastore (2011):跨数据中心,同步复制
  • Spanner (2012):全球分布式,强一致性

7.5 Borg:大规模集群管理的先驱

7.5.1 背景与动机

Borg是Google内部的大规模集群管理系统,从2003年开始开发,2007年全面部署。虽然论文直到2015年才发表,但Borg已经运行了Google几乎所有的服务超过十年。

设计目标

  • 管理数万台机器上的数十万个作业
  • 高资源利用率(60-70%)
  • 高可用性(99.99%)
  • 简化用户的部署和管理

7.5.2 核心概念

工作负载抽象

Borg作业层次结构
┌──────────────────────────────┐
│         Job(作业)          │
│   - 名称、所有者、约束       │
│   - 任务数量                 │
└──────────────┬───────────────┘
               │包含
      ┌────────┼────────┐
      ▼        ▼        ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│  Task 1  │ │  Task 2  │ │  Task N  │
│ - 资源   │ │ - 资源   │ │ - 资源   │
│ - 二进制 │ │ - 二进制 │ │ - 二进制 │
└──────────┘ └──────────┘ └──────────┘

资源模型

  • CPU:以核数计量(可以是分数)
  • 内存:以字节计量
  • 磁盘:空间和I/O带宽
  • 网络:带宽

优先级和配额

优先级层次(从高到低)
┌─────────────────────────────┐
│ Production (Prod)           │ 监控、生产服务
├─────────────────────────────┤
│ Batch                       │ 批处理作业
├─────────────────────────────┤
│ Best-effort (BE)            │ 测试、低优先级
└─────────────────────────────┘

7.5.3 系统架构

Borg系统架构
                    ┌─────────────────┐
                    │   BorgMaster    │
                    │   (主控节点)    │
                    │ ┌─────────────┐ │
                    │ │ Scheduler   │ │
                    │ └─────────────┘ │
                    └────────┬────────┘
                             │
                    ┌────────┼────────┐
                    │        │        │
              控制信息    状态更新   心跳
                    │        │        │
        ┌───────────▼────────▼────────▼───────────┐
        │              Borglets                   │
        │         (每台机器上的代理)              │
        ├──────────────────────────────────────────┤
        │ Machine 1  │ Machine 2  │ ... │Machine N│
        │ ┌────────┐ │ ┌────────┐ │     │┌────────┐│
        │ │Borglet │ │ │Borglet │ │     ││Borglet ││
        │ └────────┘ │ └────────┘ │     │└────────┘│
        │  Tasks     │  Tasks     │     │ Tasks   │
        └──────────────────────────────────────────┘

BorgMaster组件

  1. 主BorgMaster进程(逻辑单点) - 处理客户端RPC(创建、更新、删除作业) - 管理系统中所有对象的状态 - 与Borglets通信

  2. 调度器 - 异步扫描待处理的任务 - 分配任务到机器 - 考虑约束和优先级

  3. Paxos副本(5个) - 保证高可用性 - 存储持久化状态 - 自动故障切换

Borglet功能

  • 启动和停止任务
  • 重启失败的任务
  • 管理本地资源
  • 报告机器状态
  • 提供HTTP服务器用于调试

7.5.4 调度算法

两阶段调度

调度流程
第一阶段:可行性检查
┌─────────────────────────────┐
│ 过滤不满足约束的机器:       │
│ - 资源不足                  │
│ - 属性不匹配                │
│ - 已有冲突任务              │
└──────────────┬──────────────┘
               ▼
第二阶段:评分和选择
┌─────────────────────────────┐
│ 对可行机器评分:             │
│ - 最小化被抢占的任务        │
│ - 优选已有包的机器          │
│ - 分散故障域                │
│ - 负载均衡                  │
└─────────────────────────────┘

优化技术

  1. 等价类(Equivalence Classes) - 将相似的机器分组 - 减少调度计算量

  2. 松弛随机化 - 不总是选择最优机器 - 避免热点和竞争

  3. 优先级抢占 - 高优先级任务可以抢占低优先级 - 被抢占的任务进入待处理队列

7.5.5 资源隔离

Linux容器技术

资源隔离层次
┌─────────────────────────────┐
│      任务(应用代码)        │
├─────────────────────────────┤
│      容器运行时              │
│   (资源限制和隔离)          │
├─────────────────────────────┤
│      cgroups                │
│  - CPU配额                  │
│  - 内存限制                 │
│  - I/O带宽                  │
├─────────────────────────────┤
│      Linux内核              │
└─────────────────────────────┘

隔离机制

  • CPU:使用CFS(完全公平调度器)配额
  • 内存:硬限制,OOM killer
  • 磁盘I/O:带宽限制
  • 网络:流量整形

7.5.6 可用性设计

故障处理

故障检测和恢复
┌────────────────────────────────┐
│ Borglet故障:                  │
│ - 继续运行已有任务             │
│ - 新任务等待恢复               │
│ - 超时后重新调度               │
├────────────────────────────────┤
│ BorgMaster故障:               │
│ - Paxos选举新主                │
│ - 从检查点恢复状态             │
│ - 重新同步Borglets             │
├────────────────────────────────┤
│ 任务故障:                     │
│ - 自动重启(有限次数)         │
│ - 重新调度到其他机器           │
│ - 通知用户                     │
└────────────────────────────────┘

可用性数据(2013年):

  • BorgMaster平均故障间隔:~60天
  • 故障切换时间:~10秒
  • 99.99%的作业不受影响

7.5.7 使用效率

资源利用率提升技术

  1. 混部(Co-location) - 将延迟敏感和批处理任务混合 - 利用负载的时间差异 - 典型利用率:60-70%

  2. 资源回收

资源回收机制
预留资源 ────► 实际使用 ────► 可回收资源
  100%           60%            40%
                                  │
                                  ▼
                            分配给BE任务
  1. 细粒度资源分配 - CPU可以0.1核为单位 - 内存以MB为单位 - 避免资源浪费

7.5.8 从Borg到Kubernetes

经验教训

| 方面 | Borg | Kubernetes |

方面 Borg Kubernetes
API 专有RPC REST API
配置 BCL语言 YAML/JSON
网络 IP-per-machine IP-per-pod
存储 自定义 标准卷抽象
生态 内部 开源社区

Kubernetes继承的核心概念

  1. Pods ← Borg allocs
  2. Services ← Borg服务发现
  3. Labels ← Borg标签
  4. Namespaces ← Borg cells的简化
Borg到Kubernetes的演进
     Borg (2003-2015)              Kubernetes (2014-)
          内部使用                      开源项目
             │                            │
     ┌───────┼───────┐            ┌───────┼───────┐
     ▼       ▼       ▼            ▼       ▼       ▼
   Omega  内部改进  经验积累    Docker   CNCF   云原生
             │                            │
             └──────────┬─────────────────┘
                        ▼
                 行业标准容器编排平台