土法炼钢兴趣小组的算法知识备份

【系统架构设计百科】Kubernetes 架构深度解析

文章导航

分类入口
architecture
标签入口
#Kubernetes#k8s#apiserver#etcd#controller#operator#CRD

目录

一、从 Borg 到 Kubernetes:声明式基础设施的诞生

2014 年 6 月,Google 将其内部容器编排系统 Borg 的设计思想提炼并开源,发布了 Kubernetes(简称 K8s)项目。Borg 在 Google 内部运行了超过十年,管理着数百万个容器实例,支撑了搜索、Gmail、YouTube 等核心服务。根据 Google 2015 年发表的论文《Large-scale cluster management at Google with Borg》,Borg 集群的典型规模为单集群 10000 台以上机器,每周调度超过 200 亿个任务实例。Kubernetes 继承了 Borg 的声明式 API、控制器模式、标签选择器等核心设计理念,同时在可扩展性和开放性方面做了根本性改进。

Kubernetes 解决的核心问题是:如何在大规模分布式环境中,以声明式(Declarative)的方式管理应用的部署、扩缩容、服务发现和自愈。与传统的命令式运维方式不同,用户只需声明期望的终态(Desired State),系统负责持续将实际状态(Actual State)收敛到期望状态。这一设计哲学贯穿了 Kubernetes 架构的每一个组件。

本文将从架构全景出发,深入分析 kube-apiserver 为什么是整个集群的”单点”、etcd 的 watch 机制如何驱动声明式 API、控制器模式的工作原理、调度器的决策流程、以及如何通过 CRD 和 Operator 模式扩展 Kubernetes 的能力边界。

二、Kubernetes 整体架构:控制面与数据面

2.1 架构全景

Kubernetes 集群由控制面(Control Plane)和数据面(Data Plane)两部分组成。控制面负责集群的全局决策与状态管理,数据面负责实际运行工作负载。

graph TB
    subgraph 控制面["控制面(Control Plane)"]
        APIServer["kube-apiserver<br/>API 网关 / 认证鉴权 / 准入控制"]
        etcd["etcd<br/>分布式键值存储"]
        Scheduler["kube-scheduler<br/>调度决策"]
        CM["kube-controller-manager<br/>控制器集合"]
        CCM["cloud-controller-manager<br/>云平台适配"]
    end

    subgraph 数据面Node1["数据面 - Node 1"]
        kubelet1["kubelet<br/>Pod 生命周期管理"]
        kproxy1["kube-proxy<br/>网络规则管理"]
        CRI1["容器运行时<br/>containerd / CRI-O"]
        Pod1A["Pod A"]
        Pod1B["Pod B"]
    end

    subgraph 数据面Node2["数据面 - Node 2"]
        kubelet2["kubelet"]
        kproxy2["kube-proxy"]
        CRI2["容器运行时"]
        Pod2A["Pod C"]
        Pod2B["Pod D"]
    end

    APIServer -->|"读写状态"| etcd
    Scheduler -->|"watch 未调度 Pod"| APIServer
    CM -->|"watch 资源变化"| APIServer
    CCM -->|"watch Node/Service"| APIServer

    kubelet1 -->|"上报状态 / watch Pod"| APIServer
    kubelet2 -->|"上报状态 / watch Pod"| APIServer
    kproxy1 -->|"watch Service/Endpoints"| APIServer
    kproxy2 -->|"watch Service/Endpoints"| APIServer

    kubelet1 --> CRI1
    CRI1 --> Pod1A
    CRI1 --> Pod1B
    kubelet2 --> CRI2
    CRI2 --> Pod2A
    CRI2 --> Pod2B

2.2 控制面组件职责

组件 职责 无状态/有状态 高可用方式
kube-apiserver 集群唯一入口,提供 RESTful API,负责认证、鉴权、准入控制、API 聚合 无状态 多副本 + 负载均衡
etcd 存储所有集群状态,提供 watch 通知机制 有状态 Raft 共识,奇数节点(3/5/7)
kube-scheduler 为未调度的 Pod 选择最优 Node 无状态 Leader Election
kube-controller-manager 运行所有内置控制器(Deployment、ReplicaSet、Job 等) 无状态 Leader Election
cloud-controller-manager 对接云厂商 API(负载均衡、存储卷、节点生命周期) 无状态 Leader Election

2.3 数据面组件职责

每个工作节点(Worker Node)上运行以下组件:

2.4 核心设计原则

Kubernetes 架构遵循以下设计原则:

  1. 声明式优于命令式:用户声明期望状态,系统自动收敛。
  2. 控制面与数据面分离:控制逻辑与工作负载运行互不干扰。
  3. 面向终态的设计:所有控制器只关心”当前状态与期望状态的差异”。
  4. 松耦合通信:所有组件通过 API Server 间接通信,不直接互联。
  5. 可扩展性优先:CRD、Admission Webhook、调度框架等扩展点贯穿全栈。

三、kube-apiserver 深度解析

3.1 为什么 apiserver 是”单点”

kube-apiserver 是整个集群的唯一通信枢纽。所有组件——kubelet、kube-scheduler、kube-controller-manager、kubectl、自定义控制器——都只与 apiserver 通信,不直接互联。这一设计有以下深层原因:

  1. 单一数据源(Single Source of Truth):所有状态变更必须经过 apiserver 写入 etcd,确保数据一致性。
  2. 统一认证鉴权:所有请求在同一处完成身份认证和权限校验。
  3. 审计与可观测性:所有 API 调用在同一处记录审计日志。
  4. 乐观并发控制:通过 resourceVersion 实现基于 etcd 的乐观锁。

虽然 apiserver 可以水平扩展为多副本,但从逻辑架构角度看,它仍然是所有组件的”单点依赖”——任何组件失去与 apiserver 的连接,都会停止正常工作。

3.2 请求处理链路

一个典型的 API 请求(如 kubectl apply -f deployment.yaml)经历以下处理阶段:

客户端请求
  │
  ▼
┌─────────────────────────────────┐
│ 1. 认证(Authentication)        │  → 确认请求者身份
│    - X.509 客户端证书            │
│    - Bearer Token                │
│    - OIDC / Webhook Token Review │
├─────────────────────────────────┤
│ 2. 鉴权(Authorization)        │  → 确认请求者权限
│    - RBAC                        │
│    - ABAC                        │
│    - Webhook                     │
│    - Node Authorization          │
├─────────────────────────────────┤
│ 3. 准入控制(Admission Control) │  → 变更/验证请求内容
│    - MutatingAdmissionWebhook    │  → 可修改请求
│    - ValidatingAdmissionWebhook  │  → 只能拒绝请求
├─────────────────────────────────┤
│ 4. 持久化                        │  → 写入 etcd
├─────────────────────────────────┤
│ 5. 通知                          │  → 通过 watch 推送变更事件
└─────────────────────────────────┘

3.3 认证机制

Kubernetes 支持多种认证方式,可以同时启用:

# kube-apiserver 认证相关启动参数
apiVersion: v1
kind: Pod
metadata:
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-apiserver
    - --client-ca-file=/etc/kubernetes/pki/ca.crt
    - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
    - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
    - --service-account-key-file=/etc/kubernetes/pki/sa.pub
    - --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
    - --service-account-issuer=https://kubernetes.default.svc
    - --oidc-issuer-url=https://accounts.google.com
    - --oidc-client-id=my-k8s-cluster
    - --oidc-username-claim=email
    - --authorization-mode=Node,RBAC
    image: registry.k8s.io/kube-apiserver:v1.30.0

认证模块采用链式处理:任一认证器成功即通过,全部失败则拒绝(返回 401)。

3.4 RBAC 鉴权模型

RBAC(Role-Based Access Control)是生产环境中最常用的鉴权模式。其核心模型包含四种资源:

# Role:定义命名空间内的权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get"]

---
# RoleBinding:将 Role 绑定到用户/组/ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: production
subjects:
- kind: ServiceAccount
  name: monitoring-agent
  namespace: monitoring
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

ClusterRole 和 ClusterRoleBinding 提供集群级别的权限控制,适用于跨命名空间资源(如 Node、PersistentVolume)和非资源端点(如 /healthz)。

3.5 准入控制

准入控制器(Admission Controller)在认证鉴权之后、持久化之前执行,分为两类:

执行顺序为:先执行所有 Mutating Webhook,再执行所有 Validating Webhook。这保证了验证逻辑看到的是最终修改后的对象。

3.6 API 聚合层

API 聚合(API Aggregation)允许在不修改核心代码的情况下扩展 Kubernetes API。通过注册 APIService 资源,将自定义 API 组的请求代理到用户部署的扩展 API 服务器。metrics-server 就是一个典型的聚合 API 实现:

apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
  name: v1beta1.metrics.k8s.io
spec:
  service:
    name: metrics-server
    namespace: kube-system
  group: metrics.k8s.io
  version: v1beta1
  groupPriorityMinimum: 100
  versionPriority: 100
  insecureSkipTLSVerify: true

四、etcd 在 Kubernetes 中的角色

4.1 etcd 概述

etcd 是一个基于 Raft 共识算法的分布式键值存储系统,为 Kubernetes 提供所有集群状态的持久化存储。Kubernetes 中的每一个资源对象——Pod、Service、Deployment、ConfigMap——最终都以键值对的形式存储在 etcd 中。

etcd 在 Kubernetes 中的数据模型:

/registry/pods/default/nginx-7d8b49557c-abc12
/registry/deployments/default/nginx
/registry/services/specs/default/kubernetes
/registry/configmaps/kube-system/coredns
/registry/secrets/default/my-secret

4.2 Watch 机制

etcd 的 watch 机制是 Kubernetes 声明式 API 的核心驱动力。所有控制器、kubelet、kube-proxy 都通过 watch API Server 的资源变更来触发自身的处理逻辑,而不是定期轮询。

sequenceDiagram
    participant User as 用户(kubectl)
    participant API as kube-apiserver
    participant etcd as etcd
    participant Informer as Controller Informer
    participant Controller as Controller Reconcile

    User->>API: kubectl apply -f deployment.yaml
    API->>API: 认证 → 鉴权 → 准入控制
    API->>etcd: Put /registry/deployments/default/nginx
    etcd-->>API: OK(revision=1025)

    Note over etcd,Informer: Watch 连接(长连接 gRPC Stream)
    etcd-->>API: WatchEvent(ADDED, revision=1025)
    API-->>Informer: WatchEvent(ADDED)
    Informer->>Informer: 更新本地缓存(Store)
    Informer->>Controller: 入队 workqueue(key="default/nginx")
    Controller->>Controller: Reconcile(读缓存,计算差异)
    Controller->>API: 创建 ReplicaSet
    API->>etcd: Put /registry/replicasets/default/nginx-7d8b49557c

Watch 机制的关键特性:

  1. 事件驱动:基于 gRPC 长连接的服务端推送,延迟通常在毫秒级。
  2. 有序性保证:事件按 etcd revision 严格有序。
  3. 断线重连:客户端通过 resourceVersion 实现断点续传,不会丢失事件。
  4. 压缩与回收:etcd 定期压缩历史版本,过旧的 resourceVersion 会触发 “410 Gone” 错误,客户端需回退到全量 List 重建缓存。

4.3 MVCC 与一致性保障

etcd 使用 MVCC(Multi-Version Concurrency Control)实现并发控制。每次写操作都会生成一个新的 revision,旧版本被保留直到压缩。

Kubernetes 利用 etcd 的 MVCC 实现乐观并发控制:

apiVersion: v1
kind: ConfigMap
metadata:
  name: my-config
  namespace: default
  resourceVersion: "12345"  # 对应 etcd 的 mod_revision
data:
  key: value

当两个客户端同时修改同一个对象时: 1. 客户端 A 读取对象,resourceVersion=12345。 2. 客户端 B 读取对象,resourceVersion=12345。 3. 客户端 A 提交更新,携带 resourceVersion=12345,成功,新 resourceVersion=12346。 4. 客户端 B 提交更新,携带 resourceVersion=12345,etcd 发现已被修改,返回冲突错误(HTTP 409 Conflict)。

4.4 为什么 etcd 是瓶颈

在大规模集群中,etcd 常常成为整个系统的性能瓶颈:

  1. 写放大:Raft 协议要求每次写操作在多数节点上持久化 WAL 日志,再应用到 BoltDB。一次逻辑写入会转化为多次磁盘 I/O。
  2. Watch 连接数:大集群中可能有数千个 watch 连接,每次写操作需要通知所有相关 watcher。
  3. 数据库大小限制:etcd 默认数据库大小上限为 2 GB(可调至 8 GB),超过后拒绝写入。
  4. 序列化开销:所有对象在 apiserver 和 etcd 之间以 Protobuf 序列化传输,大对象(如大型 ConfigMap)带来显著开销。
  5. 磁盘延迟敏感:etcd 对磁盘延迟极度敏感,官方建议使用 SSD 且 P99 写延迟不超过 10ms。

生产环境中的优化措施:

优化方向 具体措施 效果
磁盘 使用 NVMe SSD,分离 WAL 与数据目录 降低 P99 写延迟 50% 以上
网络 etcd 节点间延迟控制在 1ms 以内 降低 Raft 共识延迟
数据规模 定期压缩与碎片整理(etcdctl defrag 控制数据库大小
事件分离 使用独立的 etcd 集群存储 Event 资源 减少主 etcd 写入压力
Watch 优化 apiserver 开启 WatchCache(默认开启) 减少 etcd watch 连接数
限流 配置 apiserver 的 --max-requests-inflight--max-mutating-requests-inflight 保护 etcd 不被打满

五、控制器模式:Reconciliation Loop

5.1 控制器的核心思想

控制器模式是 Kubernetes 最重要的设计模式。每个控制器持续执行以下循环:

for {
    desired := 获取期望状态(从 API Server 的资源对象中读取)
    actual  := 获取实际状态(从集群实际情况中观察)
    diff    := 计算差异(desired - actual)
    执行动作使 actual 趋向 desired
}

这个循环被称为 Reconciliation Loop(调谐循环)。控制器不关心事件的类型(创建、更新、删除),只关心”当前状态与期望状态是否一致”。这种面向终态的设计使系统天然具备自愈能力。

5.2 Informer 机制

Informer 是 client-go 库提供的核心组件,解决了控制器与 API Server 之间高效通信的问题。

graph LR
    subgraph Informer["SharedInformer"]
        Reflector["Reflector<br/>List + Watch"]
        DeltaFIFO["DeltaFIFO<br/>增量事件队列"]
        Indexer["Indexer / Store<br/>本地缓存"]
    end

    APIServer["kube-apiserver"]
    EventHandler["ResourceEventHandler<br/>OnAdd / OnUpdate / OnDelete"]
    WorkQueue["WorkQueue<br/>限速队列"]
    Worker["Worker Goroutine<br/>Reconcile 逻辑"]

    APIServer -->|"List(首次全量)"| Reflector
    APIServer -->|"Watch(增量事件流)"| Reflector
    Reflector -->|"事件入队"| DeltaFIFO
    DeltaFIFO -->|"Pop 事件"| Indexer
    DeltaFIFO -->|"通知"| EventHandler
    EventHandler -->|"提取 key 入队"| WorkQueue
    WorkQueue -->|"取出 key"| Worker
    Worker -->|"读取缓存"| Indexer
    Worker -->|"写操作"| APIServer

Informer 的关键设计决策:

  1. SharedInformerFactory:同一进程内对同一资源类型的多个控制器共享一个 Informer 实例,避免对 API Server 发起重复的 List/Watch 请求。
  2. DeltaFIFO:增量事件队列,保存对象的变更增量(Added、Updated、Deleted),而非完整对象。
  3. Indexer:本地内存缓存,支持按 namespace、标签等维度索引查询,控制器的读操作直接命中本地缓存,极大降低 API Server 负载。
  4. WorkQueue:限速工作队列,支持去重、延迟重入、指数退避重试。

5.3 控制器代码示例

以下是一个简化的 Deployment 控制器 Reconcile 逻辑伪代码:

func (c *DeploymentController) syncDeployment(key string) error {
    namespace, name, err := cache.SplitMetaNamespaceKey(key)
    if err != nil {
        return err
    }

    // 从 Informer 本地缓存读取 Deployment 对象
    deployment, err := c.deploymentLister.Deployments(namespace).Get(name)
    if errors.IsNotFound(err) {
        // 对象已删除,无需处理
        return nil
    }
    if err != nil {
        return err
    }

    // 获取该 Deployment 管理的所有 ReplicaSet
    rsList, err := c.getReplicaSetsForDeployment(deployment)
    if err != nil {
        return err
    }

    // 计算期望状态与实际状态的差异
    scalingEvent := false
    if deployment.Spec.Replicas != nil {
        desiredReplicas := *deployment.Spec.Replicas
        currentReplicas := countAvailableReplicas(rsList)

        if currentReplicas < desiredReplicas {
            // 需要扩容
            err = c.scaleUp(deployment, rsList, desiredReplicas)
            scalingEvent = true
        } else if currentReplicas > desiredReplicas {
            // 需要缩容
            err = c.scaleDown(deployment, rsList, desiredReplicas)
            scalingEvent = true
        }
    }

    // 检查是否需要滚动更新
    if !scalingEvent && needsRollout(deployment, rsList) {
        return c.rolloutUpdate(deployment, rsList)
    }

    // 更新 Deployment 状态
    return c.updateDeploymentStatus(deployment, rsList)
}

5.4 内置控制器一览

kube-controller-manager 内嵌了数十个控制器,以下是核心控制器:

控制器 职责 监听资源 管理资源
DeploymentController 管理无状态应用部署与滚动更新 Deployment, ReplicaSet ReplicaSet
ReplicaSetController 维持 Pod 副本数 ReplicaSet, Pod Pod
StatefulSetController 管理有状态应用,保证有序部署与持久化身份 StatefulSet, Pod Pod, PVC
DaemonSetController 确保每个节点运行一个 Pod 副本 DaemonSet, Node, Pod Pod
JobController 管理一次性批处理任务 Job, Pod Pod
CronJobController 按 cron 表达式创建 Job CronJob Job
EndpointSliceController 维护 Service 的后端端点列表 Service, Pod, Node EndpointSlice
NodeLifecycleController 监控 Node 健康状态,驱逐不健康节点上的 Pod Node, Pod Pod(Taint/Eviction)
GarbageCollector 级联删除拥有者被删除的资源 所有资源 所有资源
NamespaceController 清理被删除命名空间中的所有资源 Namespace 命名空间内所有资源

六、kube-scheduler 调度流程

6.1 调度流程概览

kube-scheduler 负责为每个新创建且未绑定节点的 Pod 选择最优的运行节点。调度流程分为三个主要阶段:

新 Pod(spec.nodeName 为空)
  │
  ▼
┌──────────────────────────┐
│ 阶段一:预选(Filtering) │  → 过滤不满足条件的节点
│   - NodeResourcesFit     │  → 节点资源是否充足
│   - NodeAffinity         │  → 节点亲和性是否匹配
│   - PodTopologySpread    │  → 拓扑分布约束
│   - TaintToleration      │  → 污点是否被容忍
│   - NodePorts            │  → 端口是否冲突
│   可行节点集合 → N 个候选  │
├──────────────────────────┤
│ 阶段二:优选(Scoring)   │  → 对候选节点打分
│   - NodeResourcesFit     │  → 资源利用率均衡
│   - InterPodAffinity     │  → Pod 间亲和/反亲和
│   - ImageLocality        │  → 镜像是否已缓存
│   - PodTopologySpread    │  → 拓扑均匀分布
│   每个节点得分 0-100       │
├──────────────────────────┤
│ 阶段三:绑定(Binding)   │  → 将 Pod 绑定到最高分节点
│   - 设置 spec.nodeName   │
│   - 通知 kubelet 启动 Pod │
└──────────────────────────┘

6.2 调度框架扩展点

Kubernetes 1.19 引入的调度框架(Scheduling Framework)定义了完整的扩展点链路:

扩展点 阶段 功能 调用时机
PreEnqueue 入队前 决定 Pod 是否可以进入调度队列 Pod 创建时
QueueSort 排序 决定调度队列中 Pod 的优先级 队列排序时
PreFilter 预过滤 计算过滤阶段需要的前置信息 过滤前
Filter 过滤 判断节点是否满足 Pod 的约束 对每个节点
PostFilter 后过滤 没有可行节点时的处理(如抢占) 过滤后无结果
PreScore 预打分 计算打分阶段需要的前置信息 打分前
Score 打分 对候选节点打分 对每个候选节点
NormalizeScore 归一化 将分数归一化到 0-100 打分后
Reserve 预留 在绑定前预留资源 选定节点后
Permit 许可 允许/拒绝/等待 Pod 绑定 预留后
PreBind 预绑定 绑定前的准备(如挂载 PV) 许可后
Bind 绑定 执行实际绑定 预绑定后
PostBind 后绑定 绑定成功后的清理/通知 绑定后

6.3 调度约束示例

apiVersion: v1
kind: Pod
metadata:
  name: web-server
  labels:
    app: web
spec:
  # 节点亲和性:优先调度到有 SSD 的节点
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: node.kubernetes.io/instance-type
            operator: In
            values:
            - m5.xlarge
            - m5.2xlarge
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: disk-type
            operator: In
            values:
            - ssd
    # Pod 反亲和性:同一应用的 Pod 分散到不同节点
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - web
        topologyKey: kubernetes.io/hostname
  # 拓扑分布约束:跨可用区均匀分布
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: web
  # 容忍 GPU 节点的污点
  tolerations:
  - key: nvidia.com/gpu
    operator: Exists
    effect: NoSchedule
  containers:
  - name: web
    image: nginx:1.25
    resources:
      requests:
        cpu: "500m"
        memory: "512Mi"
      limits:
        cpu: "1000m"
        memory: "1Gi"

七、kubelet 的职责

7.1 Pod 生命周期管理

kubelet 是每个节点上最核心的组件,负责 Pod 从创建到销毁的全部生命周期管理:

  1. Pod 同步:通过 watch API Server 接收分配到本节点的 Pod 定义,与本地容器运行时的实际状态进行对比。
  2. 容器创建:调用 CRI 接口创建容器,包括拉取镜像、创建 Sandbox、启动容器。
  3. 健康检查:执行 livenessProbe(存活探针)、readinessProbe(就绪探针)和 startupProbe(启动探针)。
  4. 资源管理:通过 cgroups 实施 CPU、内存等资源限制,执行驱逐策略(Eviction)。
  5. 状态上报:定期向 API Server 上报节点状态(NodeStatus)和 Pod 状态(PodStatus)。

7.2 CRI、CNI、CSI 接口

kubelet 通过三个标准接口实现与底层基础设施的解耦:

kubelet
  ├── CRI(Container Runtime Interface)
  │     ├── containerd
  │     ├── CRI-O
  │     └── 其他符合 CRI 规范的运行时
  ├── CNI(Container Network Interface)
  │     ├── Calico(BGP/eBPF 模式)
  │     ├── Cilium(eBPF 原生)
  │     ├── Flannel(VXLAN/host-gw)
  │     └── 其他 CNI 插件
  └── CSI(Container Storage Interface)
        ├── EBS CSI Driver
        ├── Ceph RBD CSI
        ├── NFS CSI
        └── 其他 CSI 驱动

CRI 接口的核心 gRPC 方法:

// RuntimeService 定义了容器运行时的核心操作
type RuntimeService interface {
    // Pod Sandbox 管理
    RunPodSandbox(config *PodSandboxConfig) (string, error)
    StopPodSandbox(podSandboxId string) error
    RemovePodSandbox(podSandboxId string) error

    // 容器管理
    CreateContainer(podSandboxId string, config *ContainerConfig,
        sandboxConfig *PodSandboxConfig) (string, error)
    StartContainer(containerId string) error
    StopContainer(containerId string, timeout int64) error
    RemoveContainer(containerId string) error

    // 容器状态
    ContainerStatus(containerId string) (*ContainerStatus, error)
    ListContainers(filter *ContainerFilter) ([]*Container, error)

    // 命令执行
    ExecSync(containerId string, cmd []string, timeout int64) (
        stdout []byte, stderr []byte, err error)
}

7.3 节点资源管理与驱逐

kubelet 通过驱逐管理器(Eviction Manager)监控节点资源使用情况,当资源低于阈值时触发 Pod 驱逐:

资源信号 默认驱逐阈值 驱逐优先级
memory.available 100Mi BestEffort → Burstable → Guaranteed
nodefs.available 10% 按磁盘使用量排序
nodefs.inodesFree 5% 按 inode 使用量排序
imagefs.available 15% 先回收未使用镜像
pid.available 按内核限制 按 PID 使用量排序

八、CRD 与 Operator 模式

8.1 CRD 的本质

CRD(Custom Resource Definition)允许用户在不修改 Kubernetes 源码的情况下扩展 API,注册自定义资源类型。CRD 注册后,Kubernetes API Server 自动为其提供完整的 RESTful CRUD 端点、watch 机制和 kubectl 支持。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.db.example.com
spec:
  group: db.example.com
  versions:
  - name: v1alpha1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            required: ["engine", "version", "replicas"]
            properties:
              engine:
                type: string
                enum: ["postgresql", "mysql", "mongodb"]
              version:
                type: string
              replicas:
                type: integer
                minimum: 1
                maximum: 7
              storageSize:
                type: string
                pattern: "^[0-9]+(Gi|Ti)$"
              backup:
                type: object
                properties:
                  enabled:
                    type: boolean
                  schedule:
                    type: string
                  retentionDays:
                    type: integer
          status:
            type: object
            properties:
              phase:
                type: string
              readyReplicas:
                type: integer
              endpoint:
                type: string
              lastBackup:
                type: string
                format: date-time
    subresources:
      status: {}
    additionalPrinterColumns:
    - name: Engine
      type: string
      jsonPath: .spec.engine
    - name: Version
      type: string
      jsonPath: .spec.version
    - name: Replicas
      type: integer
      jsonPath: .spec.replicas
    - name: Phase
      type: string
      jsonPath: .status.phase
    - name: Age
      type: date
      jsonPath: .metadata.creationTimestamp
  scope: Namespaced
  names:
    plural: databases
    singular: database
    kind: Database
    shortNames:
    - db

注册 CRD 后,用户即可像操作原生资源一样使用自定义资源:

apiVersion: db.example.com/v1alpha1
kind: Database
metadata:
  name: order-db
  namespace: production
spec:
  engine: postgresql
  version: "16.2"
  replicas: 3
  storageSize: "100Gi"
  backup:
    enabled: true
    schedule: "0 2 * * *"
    retentionDays: 30

8.2 Operator 模式

Operator 将领域运维知识编码为软件,使用 CRD 定义领域模型,使用自定义控制器实现自动化运维逻辑。一个 Operator 本质上就是”CRD + 自定义控制器”。

以下是一个数据库 Operator 的 Reconcile 循环实现:

package controllers

import (
    "context"
    "fmt"
    "time"

    appsv1 "k8s.io/api/apps/v1"
    corev1 "k8s.io/api/core/v1"
    "k8s.io/apimachinery/pkg/api/errors"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/runtime"
    ctrl "sigs.k8s.io/controller-runtime"
    "sigs.k8s.io/controller-runtime/pkg/client"
    "sigs.k8s.io/controller-runtime/pkg/log"

    dbv1alpha1 "github.com/example/db-operator/api/v1alpha1"
)

type DatabaseReconciler struct {
    client.Client
    Scheme *runtime.Scheme
}

// Reconcile 是 Operator 的核心逻辑:确保实际状态与期望状态一致
func (r *DatabaseReconciler) Reconcile(
    ctx context.Context, req ctrl.Request,
) (ctrl.Result, error) {
    logger := log.FromContext(ctx)

    // 第一步:获取 Database CR
    var database dbv1alpha1.Database
    if err := r.Get(ctx, req.NamespacedName, &database); err != nil {
        if errors.IsNotFound(err) {
            logger.Info("Database 资源已被删除")
            return ctrl.Result{}, nil
        }
        return ctrl.Result{}, err
    }

    // 第二步:确保 StatefulSet 存在且规格正确
    sts := r.buildStatefulSet(&database)
    var existingSts appsv1.StatefulSet
    err := r.Get(ctx, client.ObjectKeyFromObject(sts), &existingSts)

    if errors.IsNotFound(err) {
        logger.Info("创建 StatefulSet",
            "name", sts.Name, "replicas", *sts.Spec.Replicas)
        if err := r.Create(ctx, sts); err != nil {
            return ctrl.Result{}, err
        }
        database.Status.Phase = "Creating"
        r.Status().Update(ctx, &database)
        return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
    } else if err != nil {
        return ctrl.Result{}, err
    }

    // 第三步:检查是否需要扩缩容
    if *existingSts.Spec.Replicas != int32(database.Spec.Replicas) {
        logger.Info("更新副本数",
            "from", *existingSts.Spec.Replicas,
            "to", database.Spec.Replicas)
        replicas := int32(database.Spec.Replicas)
        existingSts.Spec.Replicas = &replicas
        if err := r.Update(ctx, &existingSts); err != nil {
            return ctrl.Result{}, err
        }
        database.Status.Phase = "Scaling"
        r.Status().Update(ctx, &database)
        return ctrl.Result{RequeueAfter: 15 * time.Second}, nil
    }

    // 第四步:确保 Service 存在
    svc := r.buildService(&database)
    var existingSvc corev1.Service
    if err := r.Get(ctx, client.ObjectKeyFromObject(svc),
        &existingSvc); errors.IsNotFound(err) {
        if err := r.Create(ctx, svc); err != nil {
            return ctrl.Result{}, err
        }
    }

    // 第五步:更新状态
    database.Status.ReadyReplicas = int(existingSts.Status.ReadyReplicas)
    database.Status.Endpoint = fmt.Sprintf(
        "%s.%s.svc.cluster.local:5432",
        svc.Name, svc.Namespace)

    if existingSts.Status.ReadyReplicas == *existingSts.Spec.Replicas {
        database.Status.Phase = "Running"
    } else {
        database.Status.Phase = "Progressing"
    }

    r.Status().Update(ctx, &database)
    return ctrl.Result{RequeueAfter: 30 * time.Second}, nil
}

func (r *DatabaseReconciler) buildStatefulSet(
    db *dbv1alpha1.Database,
) *appsv1.StatefulSet {
    replicas := int32(db.Spec.Replicas)
    labels := map[string]string{
        "app":                    db.Name,
        "db.example.com/engine": db.Spec.Engine,
    }

    return &appsv1.StatefulSet{
        ObjectMeta: metav1.ObjectMeta{
            Name:      db.Name,
            Namespace: db.Namespace,
            OwnerReferences: []metav1.OwnerReference{
                *metav1.NewControllerRef(db,
                    dbv1alpha1.GroupVersion.WithKind("Database")),
            },
        },
        Spec: appsv1.StatefulSetSpec{
            Replicas:    &replicas,
            ServiceName: db.Name,
            Selector: &metav1.LabelSelector{
                MatchLabels: labels,
            },
            Template: corev1.PodTemplateSpec{
                ObjectMeta: metav1.ObjectMeta{Labels: labels},
                Spec: corev1.PodSpec{
                    Containers: []corev1.Container{{
                        Name:  "database",
                        Image: fmt.Sprintf("%s:%s",
                            db.Spec.Engine, db.Spec.Version),
                        Ports: []corev1.ContainerPort{{
                            ContainerPort: 5432,
                        }},
                    }},
                },
            },
        },
    }
}

// SetupWithManager 注册控制器并声明监听资源
func (r *DatabaseReconciler) SetupWithManager(
    mgr ctrl.Manager,
) error {
    return ctrl.NewControllerManagedBy(mgr).
        For(&dbv1alpha1.Database{}).
        Owns(&appsv1.StatefulSet{}).
        Owns(&corev1.Service{}).
        Complete(r)
}

8.3 Operator 成熟度模型

Operator 社区定义了五级成熟度模型:

级别 名称 能力描述
Level 1 基本安装(Basic Install) 自动部署应用,通过 CRD 配置参数
Level 2 无缝升级(Seamless Upgrades) 支持应用版本升级与配置变更
Level 3 全生命周期管理(Full Lifecycle) 备份、恢复、故障转移
Level 4 深度监控(Deep Insights) 指标暴露、日志聚合、告警集成
Level 5 自动驾驶(Auto Pilot) 自动扩缩容、自动调优、异常自愈

九、多集群联邦

9.1 为什么需要多集群

当集群规模超过一定阈值或业务具有跨地域要求时,单集群方案面临以下限制:

  1. etcd 性能天花板:单集群超过 5000 节点时,etcd 的读写延迟显著增加。
  2. 故障爆炸半径:单集群故障影响所有业务。
  3. 合规要求:数据主权法规(如 GDPR)要求数据存储在特定地域。
  4. 多云策略:避免供应商锁定,分散风险。
  5. 网络延迟:跨地域单集群的 etcd Raft 共识延迟不可接受。

9.2 多集群方案对比

方案 架构模式 核心能力 适用场景 局限性
KubeFed v2 中心化联邦控制面 资源分发、跨集群调度策略 同质集群联邦 社区维护停滞,复杂度高
Liqo 去中心化 Peering 虚拟节点映射,Pod 跨集群透明迁移 边缘-云协同 网络要求高,生态较新
Admiralty 虚拟调度器 多集群 Pod 调度代理 批处理跨集群调度 功能聚焦调度层
Karmada CNCF 沙箱项目,兼容原生 API 资源模板 + 策略 + 覆写 大规模多集群管理 需要独立控制面
Cluster API 声明式集群生命周期管理 集群创建/升级/删除 集群即服务(Cluster as a Service) 不处理工作负载分发
ArgoCD + ApplicationSet GitOps 驱动多集群 Git 仓库为唯一事实源 多集群应用分发 不处理跨集群网络

9.3 多集群网络

跨集群网络互通是多集群架构的核心挑战。主流方案包括:

十、工程案例:Spotify 的大规模 Kubernetes 实践

10.1 背景

Spotify 自 2019 年开始将其基础设施全面迁移到 Kubernetes。到 2023 年,Spotify 在全球范围内运行超过 200 个 Kubernetes 集群,服务于 6000 多个微服务。以下数据来自 Spotify 在 KubeCon 2023 的公开演讲和技术博客。

10.2 集群规模数据

指标 数值
集群总数 200+
总节点数 约 20000
总 Pod 数 约 500000
微服务数量 6000+
日均部署次数 10000+
最大单集群节点数 约 3000
平均 Pod 密度 约 25 Pods/Node
工程师人数 约 4000

10.3 架构决策

集群拆分策略:Spotify 选择多集群而非单个大集群。每个”小队”(Squad,Spotify 的组织单元)可以拥有自己的命名空间,但集群按功能和地域划分:

平台即产品:Spotify 构建了内部平台 Backstage(后来开源),为开发者提供统一的服务目录、部署流水线和基础设施自助服务。开发者无需直接操作 kubectl,而是通过 Backstage 的 UI 或声明式配置文件管理服务。

自定义 Operator:Spotify 开发了多个自定义 Operator 来管理特定领域的运维自动化:

10.4 遇到的挑战与解决方案

挑战一:etcd 性能瓶颈

当单集群节点数超过 2000 时,etcd 的写入延迟从平均 5ms 上升到 30ms 以上,偶发超过 100ms。Spotify 采取的措施包括:

挑战二:Pod 调度延迟

在高峰期,新 Pod 的调度延迟(从创建到绑定节点)可达 15 秒以上。优化措施:

挑战三:大规模配置管理

6000 个微服务的配置管理极具挑战性。Spotify 采用 GitOps 方式,所有集群配置存储在 Git 仓库中,通过 ArgoCD 同步到集群。每次配置变更都会触发 CI/CD 流水线中的策略检查(OPA/Gatekeeper),拒绝不符合规范的配置。

10.5 关键指标

迁移到 Kubernetes 后,Spotify 报告了以下改进:

十一、K8s 架构决策 Trade-off 分析

11.1 部署方式对比

维度 自建 K8s(kubeadm/kubespray) 托管 K8s(EKS/GKE/AKS) 轻量 K8s(k3s/microK8s)
控制面管理 完全自主,需维护 etcd、apiserver 等 云厂商托管,用户无需关注 内嵌 SQLite/DQLite 替代 etcd
运维复杂度 高:证书轮换、版本升级、etcd 备份 低:一键升级,自动备份 极低:单二进制安装
成本 基础设施成本 + 人力运维成本 控制面费用(约 $70-150/月)+ 节点费用 极低,适合边缘场景
最大集群规模 视运维能力而定,通常 5000 节点以内 EKS/GKE 支持 15000 节点 建议 500 节点以内
可定制性 完全可控:可修改任何组件参数 有限:部分参数由云厂商锁定 有限:精简了部分功能
API 兼容性 完整的上游 Kubernetes API 完整兼容 + 云厂商扩展 完整兼容(k3s 通过 CNCF 一致性认证)
etcd 可靠性 自行保障:备份、监控、恢复 云厂商保障 SLA(通常 99.95%) 使用 SQLite,不适合多节点高可用
版本更新速度 最快:上游发布即可升级 延迟 1-3 个月 紧跟上游,通常延迟 1-2 周
适用场景 大型企业私有云、强合规要求 大多数生产工作负载 边缘计算、IoT、开发测试、CI/CD
典型用户 金融机构、电信运营商 互联网公司、中小企业 边缘节点、树莓派、本地开发

11.2 关键架构决策分析

决策一:中心化 API Server 还是去中心化通信?

Kubernetes 选择了中心化 API Server 方案。所有组件间通信都经过 apiserver 中转,而非点对点通信。这一决策的 trade-off:

决策二:etcd 还是其他存储后端?

Kubernetes 深度依赖 etcd 的 watch 机制和强一致性。社区曾讨论过支持其他后端(如 PostgreSQL、CockroachDB),k3s 项目使用 SQLite/DQLite 作为替代方案。

决策三:声明式 API 还是命令式 API?

声明式 API 是 Kubernetes 最核心的设计决策:

决策四:控制器单进程还是多进程?

kube-controller-manager 将数十个控制器打包在一个进程中:

11.3 网络方案对比

维度 Calico Cilium Flannel
数据面 iptables 或 eBPF eBPF 原生 VXLAN 或 host-gw
网络策略 支持(NetworkPolicy + 扩展策略) 支持(L3/L4/L7 策略) 不支持
性能 高(BGP 模式下无封装开销) 最高(eBPF 绕过 iptables) 中等
可观测性 中等 优秀(Hubble 提供 L7 可视化) 基础
复杂度 中等 较高
多集群 支持(Calico Federation) 支持(ClusterMesh) 不支持
适用场景 通用生产环境 高性能/安全敏感环境 小规模/简单场景

十二、生产环境最佳实践

12.1 资源管理

每个容器都应设置资源请求(requests)和限制(limits):

resources:
  requests:
    cpu: "250m"      # 调度依据:确保节点有足够资源
    memory: "256Mi"  # 调度依据:确保节点有足够内存
  limits:
    cpu: "1000m"     # 硬限制:超过则被限流(throttle)
    memory: "512Mi"  # 硬限制:超过则被 OOM Kill

QoS(Quality of Service)等级由 requests 和 limits 的配置决定:

QoS 等级 配置条件 驱逐优先级
Guaranteed 所有容器的所有资源都设置了 requests = limits 最低(最后被驱逐)
Burstable 至少一个容器设置了 requests,但不满足 Guaranteed 条件 中等
BestEffort 没有任何容器设置 requests 或 limits 最高(最先被驱逐)

12.2 健康检查配置

containers:
- name: app
  livenessProbe:       # 存活探针:失败则重启容器
    httpGet:
      path: /healthz
      port: 8080
    initialDelaySeconds: 15
    periodSeconds: 10
    failureThreshold: 3
    timeoutSeconds: 5
  readinessProbe:      # 就绪探针:失败则从 Service 端点移除
    httpGet:
      path: /ready
      port: 8080
    initialDelaySeconds: 5
    periodSeconds: 5
    failureThreshold: 3
  startupProbe:        # 启动探针:启动期间保护慢启动应用
    httpGet:
      path: /healthz
      port: 8080
    initialDelaySeconds: 0
    periodSeconds: 5
    failureThreshold: 30  # 允许 150 秒启动时间

12.3 Pod 中断预算

PodDisruptionBudget(PDB)保障在自愿中断(节点维护、滚动升级)期间的服务可用性:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-pdb
spec:
  minAvailable: 2        # 至少保持 2 个 Pod 可用
  # 或使用 maxUnavailable: 1  # 最多 1 个 Pod 不可用
  selector:
    matchLabels:
      app: web

12.4 安全加固

生产环境的安全加固清单:

  1. Pod 安全标准(Pod Security Standards):使用 Restricted 级别限制容器权限。
  2. 网络策略(NetworkPolicy):默认拒绝所有入站流量,按需开放。
  3. RBAC 最小权限:遵循最小权限原则,避免使用 cluster-admin。
  4. 镜像安全:只允许从受信仓库拉取镜像,启用镜像签名验证。
  5. Secret 加密:启用 etcd 静态加密(EncryptionConfiguration),使用外部密钥管理系统(如 Vault)。
  6. 审计日志:启用 API 审计日志,记录所有写操作。
# 网络策略:默认拒绝所有入站,只允许同 namespace 的流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-same-namespace
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector: {}

十三、总结与展望

Kubernetes 架构的核心设计理念可以归纳为三个关键词:声明式控制器驱动可扩展

声明式 API 使得系统天然具备幂等性和自愈能力,用户只需描述期望状态,系统负责持续收敛。这一设计使得 GitOps、基础设施即代码等现代运维范式成为可能。

控制器模式 将所有状态管理逻辑抽象为”观察-比较-行动”的统一循环。kube-apiserver 作为中心枢纽,etcd 的 watch 机制作为事件驱动引擎,二者共同构成了整个系统的”神经中枢”。kube-apiserver 之所以是集群的”单点”,是因为它承载了认证、鉴权、准入控制、审计日志、watch 通知等所有横切关注点;etcd 的 watch 机制之所以能驱动声明式 API,是因为它提供了毫秒级的、有序的、可靠的变更通知机制,使控制器能够及时响应状态变化。

可扩展性 通过 CRD、Operator、Admission Webhook、调度框架等扩展点,使 Kubernetes 从容器编排平台演化为通用的分布式系统控制面。这种”平台的平台”定位是 Kubernetes 生态繁荣的根本原因。

未来的演进方向包括:Gateway API 逐步取代 Ingress 成为标准流量入口、Sidecar 容器的原生支持(KEP-753)、基于 eBPF 的网络和可观测性方案成为主流、以及多集群管理从工具碎片化走向标准化。

参考资料

  1. Abhishek Verma, Luis Pedrosa, Madhukar Korupolu, David Oppenheimer, Eric Tune, John Wilkes. “Large-scale cluster management at Google with Borg.” Proceedings of the European Conference on Computer Systems (EuroSys), ACM, 2015.
  2. Brendan Burns, Brian Grant, David Oppenheimer, Eric Brewer, John Wilkes. “Borg, Omega, and Kubernetes.” Communications of the ACM, Vol. 59, No. 5, May 2016.
  3. Kubernetes Official Documentation. “Kubernetes Components.” https://kubernetes.io/docs/concepts/overview/components/
  4. etcd Official Documentation. “etcd architecture.” https://etcd.io/docs/
  5. Kubernetes Official Documentation. “Controlling Access to the Kubernetes API.” https://kubernetes.io/docs/concepts/security/controlling-access/
  6. Kubernetes Official Documentation. “Scheduling Framework.” https://kubernetes.io/docs/concepts/scheduling-eviction/scheduling-framework/
  7. Kubernetes Official Documentation. “Custom Resources.” https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/
  8. Spotify Engineering. “How We Use Kubernetes at Spotify.” KubeCon North America 2023.
  9. Spotify Engineering Blog. “Backstage: An Open Platform for Building Developer Portals.” https://backstage.io/
  10. CNCF. “Operator Framework.” https://operatorframework.io/
  11. k3s Official Documentation. “Architecture.” https://docs.k3s.io/architecture
  12. Cilium Documentation. “ClusterMesh - Multi Cluster Networking.” https://docs.cilium.io/en/latest/network/clustermesh/

上一篇:容器架构

下一篇:Serverless 架构

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-13 · architecture

【系统架构设计百科】配置管理架构:从配置文件到配置中心

配置应该放在代码里、环境变量里、还是配置中心里?本文从 12-Factor App 的配置理念出发,拆解配置的分层模型,深入分析 Apollo、Nacos、etcd 三大配置中心的架构与取舍,讨论动态配置灰度发布、配置加密与审计、Feature Flags 等工程实践。

2026-04-13 · architecture

【系统架构设计百科】架构质量属性:不只是"高可用高性能"

需求评审时写下的'高可用、高性能、高并发',到了架构设计阶段几乎无法落地——因为它们不是可执行的需求。本文从 SEI/CMU 的质量属性理论出发,用 stimulus-response 场景模型把模糊需求变成可量化、可验证的架构约束,并拆解属性之间的冲突与联动关系。

2026-04-13 · architecture

【系统架构设计百科】告警策略:如何避免"狼来了"

大多数团队的告警系统都在制造噪声而不是传递信号。阈值告警看似直观,实则产生大量误报和漏报,值班工程师在凌晨三点被叫醒,却发现只是一次无害的毛刺。本文从告警疲劳的工业数据出发,拆解基于 SLO 的多窗口燃烧率告警算法,深入 Alertmanager 的路由、抑制与分组机制,结合 PagerDuty 的告警疲劳研究和真实工程案例,给出一套可落地的告警策略设计方法。

2026-04-13 · architecture

【系统架构设计百科】复杂性管理:架构的核心战场

系统复杂性是架构腐化的根源——本文从 Brooks 的本质复杂性与偶然复杂性划分出发,结合认知负荷理论与 Parnas 的信息隐藏原则,系统阐述复杂性的来源、度量与控制手段,并给出可操作的架构策略


By .