mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
4985 字
14 分钟
认识 Kubernetes:前世今生
2021-10-08

一、云计算商业模式的发展#

要回答「Kubernetes 是什么」这个问题,需要从云计算的起源说起。云计算的发展是「技术」与「商业化」两条主线的演进:前者提供虚拟化、分布式等技术支撑,后者提供产品形态和计费模式。

商业化方面,2006 年亚马逊推出 EC2/S3 树立了 IaaS 标杆;2009 年 Heroku 提出 12 要素和「以应用为中心」的 PaaS 理念;2010 年 OpenStack 和 Cloud Foundry 推动云计算走向开源;2013 年后 Docker 和 K8s 开源使「容器云」走入大众视野,催生了「多云」概念。

技术方面,2003 年谷歌三驾马车论文(GFS/MapReduce/BigTable)奠定了分布式存储和计算的基础;2006 年 KVM 进入 Linux 内核;2008 年 LXC 容器发布;2013 年 Docker 用镜像保证不可变性,利用 Namespace/cgroup/联合文件系统形成最初的容器标准;2014 年谷歌以 Borg 为基础发布 Kubernetes,次年与 Linux 基金会成立 CNCF。

云计算本质上是一种提供计算和存储资源的租赁式服务,根据资源托管层次分为 IaaS、PaaS、SaaS 等。

  1. : Popek & Goldberg 拟化理论

  2. : IBM RAID 专利

  3. : Google 三驾马车论文

  4. : KVM 进入 Linux 内核

  5. : LXC 发布

  6. : Docker 开源

  7. : Kubernetes 开源

  8. : Borg 论文公开 + CNCF 成立

  9. : Omega 论文发表

  10. : 生态爆发

  11. : 多云 / 边缘计算 / eBPF

二、从 Borg 到 Kubernetes:谷歌集群管理的传承#

Kubernetes 并非凭空诞生,它深深植根于谷歌十多年的大规模集群管理经验。要理解 Kubernetes 的设计哲学,需要回到谷歌内部系统演进的完整脉络。

2.1 Borg:大规模集群管理的起点#

Borg 是谷歌内部最早的大规模集群管理系统,早在 2003~2004 年间就开始投入使用,管理着谷歌全球数据中心的数十万台机器。以下关于 Borg 的数据均来自 2015 年公开的 Borg 论文,在此之前 Borg 的细节长期处于内部保密状态。Borg 的核心设计理念包括:

  • 统一的资源池:将所有工作负载(在线服务、离线批处理)混合部署在同一集群中,通过优先级和抢占机制实现资源复用。在线服务白天资源需求高,批处理任务夜晚资源需求高,混合部署使整体利用率大幅提升。Borg 论文数据显示,谷歌集群的平均 CPU 利用率达到 60%~70%,远超同期传统数据中心的 10%~20%。
  • 声明式期望状态:用户提交 Job 时声明期望的副本数和资源需求,Borg 负责持续驱动实际状态向期望状态收敛。如果一个 Task 失败,Borg 会自动在集群中寻找合适的机器重新启动它。
  • Borglet 与 Borgmaster:每个节点上运行 Borglet 代理,负责本地的 Task 启停和状态上报;Borgmaster 作为中央控制器,由主进程和 Scheduler 组成。主进程处理所有 RPC 请求并持久化状态到 Paxos 存储,Scheduler 负责将 Task 分配到满足约束的机器上。

Borg 面向的用户主要是谷歌内部的工程师,API 设计偏向底层,直接暴露了 Task、Job、Allocation 等概念。这种设计虽然灵活,但也带来了使用上的复杂性。

2.2 Omega:下一代架构的探索#

Omega 是 Borg 的后继系统探索。Omega 论文于 2016 年发表,其改进在于去中心化的调度架构

  • Borg 采用集中式调度器,所有调度决策由 Borgmaster 的 Scheduler 统一做出,调度效率受限于单点吞吐。
  • Omega 引入了基于事务的共享状态存储(Transaction-based Shared State),多个调度器可以并发地读取集群状态并提交调度决策,通过乐观并发控制(OCC)解决冲突。这使 Omega 能够支持异构的调度策略,不同类型的工作负载可以使用最适合自己的调度算法。

Omega 的这一设计思想直接影响了 Kubernetes 中 Scheduler 的可扩展架构(Scheduler Framework / Multiple Schedulers),以及基于 etcd 的事务性资源操作模型。

2.3 Kubernetes:开源的 Borg 传承#

2014 年 6 月,谷歌在 DockerCon 上首次开源 Kubernetes。Kubernetes 从 Borg/Omega 中继承了核心设计理念,同时做了重要的简化和改进:

设计理念BorgOmegaKubernetes
调度架构集中式 Scheduler共享状态 + 多调度器单调度器 + Framework 扩展
配置接口BCL(Borg Configuration Language)类 BCLYAML/JSON 声明式 API
容器隔离自研容器方案自研容器方案Docker / containerd / CRI-O
服务发现Borg Name Service(BNS)BNSService + DNS + CoreDNS
存储抽象挂载到 Task 的 Volume类似 BorgPV/PVC + CSI 插件
期望状态声明式声明式声明式(核心设计)
标签选择不支持不支持Label + Selector(创新)

Kubernetes 最重要的创新之一是 Label + Selector 机制。Borg 中 Job 和 Task 是强绑定的层级关系,而 Kubernetes 通过 Label 实现了松耦合的资源关联。任何对象都可以打上任意的 Label,通过 Selector 按条件筛选,这使得 Service、Deployment、NetworkPolicy 等高层概念可以灵活地组合和查询底层 Pod。

graph TB subgraph "谷歌集群管理系统演进" B["Borg<br/>2003~2013<br/>集中式调度<br/>BCL 配置"] O["Omega<br/>论文 2016<br/>共享状态调度<br/>事务性操作"] K["Kubernetes<br/>2014~至今<br/>开源<br/>声明式 API"] end B -->|"架构经验"| O B -->|"设计理念"| K O -->|"调度思想"| K subgraph "Kubernetes 从 Borg 继承的核心设计" D1["声明式期望状态"] D2["控制器循环 Reconcile"] D3["Label/Selector 松耦合"] D4["Pod 最小调度单元"] end K --> D1 K --> D2 K --> D3 K --> D4 style B fill:#fff3e0 style O fill:#e8f5e9 style K fill:#e1f5fe

Kubernetes 还简化了 Borg 中过于底层的设计。例如,Borg 的 Allocation 概念(预留资源槽位)在 K8s 中被合并到 Pod 的资源请求(requests/limits)中;Borg 的 Task 级别重启策略在 K8s 中被抽象为 Pod 的 restartPolicy;Borg 的优先级和抢占机制在 K8s 中经历较长演进,从 v1.8 alpha 引入 PodPriority,到 v1.11 升级 beta,最终在 v1.14(2019 年 3 月)正式 GA。

三、Kubernetes 版本演进与关键里程碑#

从 2014 年开源至今,Kubernetes 已发布 30 多个大版本。以下按阶段梳理关键里程碑:

时期版本关键特性
早期探索v1.0–v1.5核心 API(Pod/Service)、Deployment、StatefulSet、DaemonSet、PDB
功能成熟v1.6–v1.11RBAC GA、CRD GA、Admission Webhook、IPVS 模式、PodPriority
生态扩展v1.12–v1.18CSI GA、Scheduling Framework、Volume Snapshot、API 版本清理、IngressClass GA
稳定迭代v1.19–v1.23发布周期改为每年 3 次、Dockershim 弃用声明、Server-side Apply GA

其中 v1.20 的 Dockershim 弃用声明 是最重要的转折点:Kubernetes 宣布将在后续版本移除对 Docker Engine 的直接支持(仅影响作为容器运行时,不影响 Docker 构建的镜像),社区引发广泛讨论。最终在 v1.24 正式移除 Dockershim,这一决定推动了 containerd/CRI-O 成为默认运行时。

graph LR subgraph "v1.0-v1.5 早期探索" A1["v1.0 核心 API"] A2["v1.2 Deployment"] A3["v1.3 StatefulSet"] A4["v1.4 DaemonSet"] end subgraph "v1.6-v1.11 功能成熟" B1["v1.7 CRD Beta"] B2["v1.9 CRD GA"] B3["v1.10 IPVS"] B4["v1.11 PodPriority"] end subgraph "v1.12-v1.18 生态扩展" C1["v1.13 CSI GA"] C2["v1.14 Preemption GA"] C3["v1.16 API 清理"] C4["v1.18 Scheduling FW"] end subgraph "v1.19-v1.23 稳定迭代" D1["v1.20 Dockershim 弃用"] D2["v1.22 API 清理"] end A1 --> A2 --> A3 --> A4 B1 --> B2 --> B3 --> B4 C1 --> C2 --> C3 --> C4 D1 --> D2 style A1 fill:#e8f5e9 style B2 fill:#e1f5fe style C1 fill:#fff3e0 style D1 fill:#fce4ec

CNCF 生态里程碑#

CNCF(云原生计算基金会)自 2015 年成立以来推动了 Kubernetes 生态的发展:2016 年 K8s 成为第一个毕业项目;2017 年 Prometheus 毕业、containerd 孵化;2018 年 CoreDNS 替代 kube-dns 成为默认 DNS 方案、Helm 毕业;2021 年 Cilium 进入孵化,eBPF 开始在 K8s 网络和可观测性领域展露头角。

四、为什么需要 Kubernetes#

4.1 K8s 的部署优势#

Kubernetes 以 Borg 十多年大规模集群管理经验为基础,以容器化为底座,为存储和网络提供了统一接口,内置多种控制器应对各种部署和维护场景。

部署方式经历了三个阶段:传统部署直接在物理服务器上运行应用,无法定义资源边界,易出现资源争用;虚拟化部署用 VM 实现隔离,但每个 VM 包含完整操作系统,资源开销大;容器部署共享宿主 OS,轻量且可跨云移植。容器流行的核心在于三点:

  • 环境一致性:镜像打包应用及其全部依赖,在开发机、测试环境、云上运行方式完全相同
  • 隔离与资源控制:Namespace 实现进程/网络/文件系统隔离,cgroup 限制 CPU/内存
  • 快速交付:镜像不可变,构建一次反复部署,回滚只需切回旧镜像标签

但容器本身不解决运维问题。一个容器挂了谁来重启?几百个容器怎么调度到合适的机器?滚动更新怎么做?这些才是 Kubernetes 要解决的问题。Kubernetes 提供了一个声明式的、自愈的分布式系统运行框架,你描述期望状态,它负责驱动集群向期望状态收敛。具体能力包括:

  • 服务发现与负载均衡:Service 为 Pod 提供稳定的 ClusterIP 和 DNS 名称,自动分发流量
  • 存储编排:通过 PV/PVC/CSI 将存储系统挂载到 Pod,存储生命周期独立于 Pod
  • 自动部署与回滚:Deployment 控制器以受控速率执行滚动更新,出问题时一键回滚
  • 自我修复:控制器持续监控 Pod 状态,Pod 崩溃自动重建,节点故障自动驱逐并重新调度
  • 密钥与配置管理:ConfigMap 和 Secret 将配置从镜像中剥离,运行时注入

4.2 Kubernetes 与其他编排系统的对比#

Kubernetes 并非唯一的容器编排系统,但它最终赢得了「容器编排之战」。理解它胜出的原因,需要对比同期的主要竞争者:

维度KubernetesDocker SwarmApache Mesos/MarathonNomad
架构声明式、面向终态声明式声明式(Marathon)/ 命令式(Mesos)声明式
扩展性CRD + Operator + Webhook有限Framework 机制Task Driver
容器运行时CRI(Docker/containerd/CRI-O)Docker 仅限Docker/containerd多种(Docker/QEMU/Java)
服务发现Service + CoreDNS内置 DNSMesos-DNS/ConsulConsul 集成
网络模型CNI 插件Overlay 网络CNI/DCOS OverlayCNI
存储模型PV/PVC/CSIVolume 插件外部存储CSI
多负载支持Pod/Deployment/StatefulSet/Job/DaemonSetService/StackDocker 容器/自定义执行器多种 Task Driver
社区规模最大(CNCF)较小中等(Mesosphere)较小(HashiCorp)

Kubernetes 胜出的核心原因:

  1. 声明式 API + 控制器模式:这一源自 Borg 的设计理念使 K8s 天然支持自愈和期望状态收敛,运维复杂度远低于命令式系统。
  2. 可扩展性:CRD + Operator 模式使 K8s 从一个编排器扩展为通用的工作负载平台。任何有状态应用都可以通过 Operator 接入 K8s 管理。
  3. 开放生态:CNCF 中立的治理结构吸引了大量厂商和开发者,形成了强大的网络效应。Google、Red Hat、微软、华为等大厂的共同投入使 K8s 的迭代速度远超竞争对手。
  4. 时机:2015 年 Docker 刚刚开始普及容器化,市场需要一个开源的、中立的编排方案。Swarm 太封闭,Mesos 太复杂,Nomad 太小众,K8s 刚好占据了最佳位置。

五、可变基础设施和不可变基础设施#

基础设施可以理解为服务器、虚拟机或容器。传统运维采用可变基础设施:通过 SSH 登录服务器,手动安装包、修改配置、打补丁。随着变更积累,环境变得脆弱敏感,出现软件包循环依赖、参数配置不一致、版本漂移等问题,故障时也难以快速构建新的服务副本。

不可变基础设施则相反:部署完成后成为只读状态,任何更新或修改都通过用新实例替换旧实例完成。这带来更一致、更可靠、更可预测的行为。借助容器技术可以自动化地构建出不可变的、可版本化管理的、可一致性交付的应用服务体系。Kubernetes 中的不可变基础设施就是 Pod。

1
SSH 登录服务器
2
手动安装依赖
3
修改配置文件
4
打补丁/升级
5
环境漂移

不可变基础设施:

1
编写 Dockerfile
2
构建镜像
3
推送镜像仓库
4
创建新 Pod 替换旧 Pod
5
环境一致

5.1 不可变基础设施的实践原则#

  • 镜像即交付物:应用及其全部依赖打包为容器镜像,镜像一旦构建不可修改
  • 配置与代码分离:通过 ConfigMap 和 Secret 将配置从镜像中剥离,运行时注入,同一镜像可复用于不同环境
  • 版本化一切:镜像有 Tag,配置有 Git Commit,部署有 Release,任何变更可追溯、可回滚
  • 替换而非修改:Pod 需要更新时,K8s 不会在原容器上原地修改,而是创建新 Pod、销毁旧 Pod,这是 Deployment 滚动更新的核心机制

六、Kubernetes 是怎样工作的#

Kubernetes 采用经典的 C/S 架构,由控制平面(Control Plane)和工作节点(Worker Node)组成。所有组件通过 API Server 进行通信,etcd 作为唯一的持久化存储,这是一种高度解耦的设计。

graph TB subgraph "控制平面 Control Plane" API["API Server<br/>集群统一入口"] ETCD["etcd<br/>分布式存储"] SCHED["Scheduler<br/>Pod 调度"] CM["Controller Manager<br/>控制器集合"] end subgraph "工作节点 Worker Node" KL["Kubelet<br/>节点代理"] KP["Kube-proxy<br/>网络代理"] CR["Container Runtime<br/>containerd / CRI-O"] P1["Pod 1"] P2["Pod 2"] P3["Pod 3"] end subgraph "客户端" KCTL["kubectl"] end KCTL -->|"REST API"| API API -->|"读写状态"| ETCD API -->|"调度请求"| SCHED API -->|"监听变化"| CM SCHED -->|"绑定 Pod → Node"| API CM -->|"创建/更新资源"| API API -->|"下发 Pod 规格"| KL KL -->|"启动容器"| CR CR --> P1 CR --> P2 CR --> P3 KL --- KP style API fill:#e1f5fe style ETCD fill:#fff3e0 style SCHED fill:#e8f5e9 style CM fill:#f3e5f5

主节点部分:

  • etcd:持久化存储整个集群的状态。etcd 是一个分布式键值存储,基于 Raft 协议保证一致性。Kubernetes 的所有资源定义(Pod、Service、ConfigMap 等)都存储在 etcd 中。etcd 的性能和可靠性直接决定了集群的可用性,生产环境建议部署 3 或 5 个节点的 etcd 集群。
  • API Server:提供资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制。所有组件(kubectl、Scheduler、Controller Manager、Kubelet)都通过 API Server 进行交互,绝不直接访问 etcd。API Server 支持多种认证方式(TLS 证书、Token、OIDC 等)和授权模式(RBAC、ABAC、Node 等),还支持 Admission Webhook 进行自定义校验和变更。
  • Controller Manager:负责维护集群的状态,比如故障检测、自动扩展、滚动更新等。Controller Manager 内部运行着多个控制器:Deployment Controller、ReplicaSet Controller、StatefulSet Controller、DaemonSet Controller、Job Controller、Endpoint Controller、Service Account Controller 等。每个控制器通过「期望状态 → 实际状态」的 Reconcile 循环不断驱动集群向期望状态收敛。
  • Scheduler:负责资源调度,按照预定的调度策略将 Pod 调度到相应的机器上。v1.19+ 默认调度器基于 Scheduling Framework 实现,调度过程分为 Filter(过滤不满足条件的节点)、Score(对满足条件的节点打分)、Bind(绑定 Pod 到得分最高的节点)三个阶段。每个阶段都可以通过插件扩展。

工作节点部分:

  • Kubelet:从 API Server 获取 Pod 的配置,负责维护容器的生命周期以及 Volume(CVI)和网络(CNI)的管理。Kubelet 定期向 API Server 上报节点状态(NodeStatus),并通过 CAdvisor 采集容器的资源使用指标。Kubelet 还实现了 Pod 的生命周期管理(Pod Lifecycle Event Generator,PLEG),通过容器运行时接口(CRI)与 containerd 交互。
  • Container Runtime(v1.24 移除 Dockershim 前 Docker 是最常见选择,之后 containerd 成为默认):负责镜像管理以及 Pod 和容器的真正运行(CRI)。CRI(Container Runtime Interface)将 Kubelet 与具体的运行时实现解耦,目前支持的运行时包括 containerd、CRI-O 和 Mirantis Container Runtime。
  • Kube-proxy:负责为 Service 提供集群内部的服务发现和负载均衡。Kube-proxy 支持三种模式:userspace(已废弃)、iptables(默认,v1.2+)和 IPVS(v1.8+,大规模场景推荐)。

客户端部分:

  • kubectl:与 API Server 通信,将命令发送到主节点的命令行工具。kubectl 支持命令式命令(kubectl run)、命令式对象配置(kubectl apply -f)和声明式对象配置(kubectl apply -k)三种操作模式。

组件之间的关系为:

Kubernetes 中调度工作负载的最小单位为 Pod。Pod 实际上就是一组协同工作的容器组合,它们运行在同一个工作节点(Node)上,共享网络/PID/IPC/UTS 命名空间,并通过卷共享存储资源。在 Pod 之上是各种控制器,它们以不同的方式控制 Pod 的运行行为:

  • 通过 Deployment 可以维护无状态的多副本集程序。
  • 通过 Job/CronJob 可以维护定时批处理任务。
  • 通过 DaemonSet 针对不同类型的工作节点运行 Pod,提供系统级的后台任务。
  • 通过 StatefulSet 可以运行名称固定且拥有独立生命周期的存储,是一种更可靠和稳定的模拟虚拟机的机制。

6.1 一个 Pod 从创建到运行的工作流#

理解 Kubernetes 的工作方式,最好的方式是跟踪一个 Pod 从提交到运行的完整流程:

sequenceDiagram participant U as 用户/kubectl participant A as API Server participant E as etcd participant S as Scheduler participant CM as Controller Manager participant K as Kubelet participant C as Container Runtime U->>A: kubectl apply -f pod.yaml A->>A: 认证 / 授权 / 准入控制 A->>E: 持久化 Pod 定义 A-->>U: 返回提交成功 Note over S: Scheduler Watch 到未调度 Pod S->>A: 绑定 Pod 到 Node(更新 Pod.spec.nodeName) A->>E: 持久化绑定结果 Note over K: Kubelet Watch 到本节点新 Pod K->>A: 更新 Pod 状态为 Pending K->>C: CRI: Pull Image C-->>K: 镜像拉取完成 K->>C: CRI: Create & Start Container K->>A: 更新 Pod 状态为 Running Note over K: 探针检测容器就绪 K->>A: 更新 Pod Condition: Ready=True A-->>U: Pod 可用
1
用户通过 kubectl apply 提交 Pod YAML 到 API Server。
2
API Server 经过认证(谁在操作)、授权(有没有权限)、准入控制(操作是否合规)三层校验后,将资源写入 etcd。此时 Pod 的 spec.nodeName 为空,状态为 Pending。
3
Scheduler 通过 Watch 机制感知到未调度的 Pod,执行调度算法:先过滤(Filter)出满足约束的节点,再打分(Score)选出最优节点,最后将绑定结果写回 API Server。
4
目标节点上的 Kubelet 通过 Watch 感知到新分配的 Pod,调用 CRI 接口拉取镜像、创建并启动容器。
5
Kubelet 持续上报 Pod 状态到 API Server,容器通过就绪探针(Readiness Probe)检查后,Pod 进入 Ready 状态,可以接收 Service 流量。

这个流程体现了 Kubernetes 的基本设计模式:声明式 API + 控制器循环。用户只需声明「我想要什么」,Kubernetes 的各个组件自动协同驱动实际状态向期望状态收敛。

参考资料

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

认识 Kubernetes:前世今生
https://blog.souloss.cn/posts/kubernetes/k8s-past-and-present/
作者
Souloss
发布于
2021-10-08
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时