Kubernetes架构深度解析
// 目录 · contents
前言
Kubernetes(简称K8s)已成为容器编排领域的事实标准。要真正掌握Kubernetes,必须深入理解其架构设计。本文将从控制平面、节点组件、Pod生命周期、控制器模式和网络模型五个维度进行全面解析。
整体架构概览
Kubernetes采用Master-Worker架构,由控制平面(Control Plane)和工作节点(Worker Node)组成。
graph TB
subgraph ControlPlane["Control Plane (Master)"]
API["API Server"]
ETCD["etcd"]
SCHED["Scheduler"]
CM["Controller Manager"]
CCM["Cloud Controller Manager"]
end
subgraph WorkerNode1["Worker Node 1"]
KL1["kubelet"]
KP1["kube-proxy"]
CR1["Container Runtime"]
POD1["Pod A"]
POD2["Pod B"]
end
subgraph WorkerNode2["Worker Node 2"]
KL2["kubelet"]
KP2["kube-proxy"]
CR2["Container Runtime"]
POD3["Pod C"]
POD4["Pod D"]
end
API --> ETCD
SCHED --> API
CM --> API
CCM --> API
KL1 --> API
KL2 --> API
KP1 --> API
KP2 --> API
KL1 --> CR1
KL2 --> CR2
CR1 --> POD1
CR1 --> POD2
CR2 --> POD3
CR2 --> POD4
控制平面组件
API Server
API Server是Kubernetes控制平面的前端,所有组件之间的通信都通过它进行。它提供RESTful API接口,是唯一与etcd直接交互的组件。
API Server的核心职责:
- 认证(Authentication):验证请求者身份
- 授权(Authorization):检查请求者权限(RBAC)
- 准入控制(Admission Control):对请求进行校验和修改
- 数据持久化:将资源状态写入etcd
sequenceDiagram
participant Client as kubectl/客户端
participant API as API Server
participant Auth as 认证模块
participant Authz as 授权模块
participant Admit as 准入控制
participant ETCD as etcd
Client->>API: HTTP Request
API->>Auth: 身份验证
Auth-->>API: 验证通过
API->>Authz: RBAC授权检查
Authz-->>API: 授权通过
API->>Admit: Mutating Admission
Admit-->>API: 修改后的资源
API->>Admit: Validating Admission
Admit-->>API: 校验通过
API->>ETCD: 持久化存储
ETCD-->>API: 写入成功
API-->>Client: 返回响应
API Server请求处理的关键代码路径:
1 | |
etcd
etcd是一个高可用的分布式键值存储,是Kubernetes集群的”唯一真相来源(Single Source of Truth)“。所有集群状态数据都存储在etcd中。
etcd的关键特性: - 使用Raft一致性算法保证强一致性 - 支持Watch机制,API Server通过Watch监听状态变化 - 推荐使用奇数节点部署(3或5节点)
1 | |
Scheduler
Scheduler负责将未调度的Pod分配到合适的节点。调度过程分为两个阶段:
flowchart LR
A["待调度Pod"] --> B["预选 Filtering"]
B --> C["优选 Scoring"]
C --> D["绑定 Binding"]
subgraph Filtering["预选阶段"]
F1["NodeResourcesFit"]
F2["NodeAffinity"]
F3["PodTopologySpread"]
F4["TaintToleration"]
end
subgraph Scoring["优选阶段"]
S1["LeastRequestedPriority"]
S2["BalancedResourceAllocation"]
S3["ImageLocality"]
S4["InterPodAffinity"]
end
B -.-> Filtering
C -.-> Scoring
预选(Filtering):过滤掉不满足条件的节点(如资源不足、不匹配nodeSelector)。
优选(Scoring):对剩余节点打分排序,选出最优节点。
1 | |
Controller Manager
Controller Manager运行着一组控制器,每个控制器负责管理特定类型的资源。核心控制器包括:
| 控制器 | 职责 |
|---|---|
| Deployment Controller | 管理Deployment和ReplicaSet |
| ReplicaSet Controller | 确保Pod副本数符合期望 |
| StatefulSet Controller | 管理有状态应用 |
| DaemonSet Controller | 确保每个节点运行指定Pod |
| Job Controller | 管理批处理任务 |
| Node Controller | 监控节点健康状态 |
| Service Account Controller | 管理ServiceAccount |
| Endpoint Controller | 维护Service与Pod的映射 |
控制器遵循声明式的调谐循环(Reconcile Loop)模式:
1 | |
graph LR
A["Observe<br>观察当前状态"] --> B["Diff<br>比较期望状态"]
B --> C["Act<br>执行调谐动作"]
C --> A
style A fill:#4CAF50,color:#fff
style B fill:#FF9800,color:#fff
style C fill:#2196F3,color:#fff
节点组件
kubelet
kubelet是每个节点上的核心代理,负责:
- 从API Server接收PodSpec
- 通过CRI(Container Runtime Interface)管理容器
- 执行健康检查(liveness/readiness/startup probe)
- 上报节点和Pod状态
graph TB
API["API Server"] --> KL["kubelet"]
KL --> CRI["CRI (Container Runtime Interface)"]
KL --> CNI["CNI (Container Network Interface)"]
KL --> CSI["CSI (Container Storage Interface)"]
CRI --> Containerd["containerd"]
CRI --> CriO["CRI-O"]
Containerd --> RunC["runc"]
CriO --> RunC
CNI --> Calico
CNI --> Flannel
CNI --> Cilium
CSI --> LocalPV["Local PV"]
CSI --> NFS
CSI --> Ceph
kubelet健康检查配置示例:
1 | |
kube-proxy
kube-proxy负责实现Service的网络代理和负载均衡。它支持三种代理模式:
graph TB
subgraph IPTables["iptables模式"]
IT1["Service ClusterIP"] --> IT2["iptables规则"]
IT2 --> IT3["Pod Endpoint 1"]
IT2 --> IT4["Pod Endpoint 2"]
IT2 --> IT5["Pod Endpoint 3"]
end
subgraph IPVS["IPVS模式"]
IV1["Service ClusterIP"] --> IV2["IPVS虚拟服务器"]
IV2 --> IV3["Pod Endpoint 1"]
IV2 --> IV4["Pod Endpoint 2"]
IV2 --> IV5["Pod Endpoint 3"]
end
- iptables模式:默认模式,通过iptables规则转发,适合中小规模集群
- IPVS模式:基于内核IPVS模块,性能更好,支持多种负载均衡算法(rr/lc/dh/sh/sed/nq)
- nftables模式:Kubernetes 1.29+引入,替代iptables
1 | |
Pod生命周期
Pod是Kubernetes最小的调度单元,其生命周期状态流转如下:
stateDiagram-v2
[*] --> Pending: 创建Pod
Pending --> Running: 调度成功,容器启动
Running --> Succeeded: 所有容器正常退出(restartPolicy=Never)
Running --> Failed: 容器异常退出
Running --> Unknown: 节点通信失败
Failed --> Running: 容器重启(restartPolicy=Always/OnFailure)
Unknown --> Running: 通信恢复
Unknown --> Failed: 超时判定失败
Succeeded --> [*]
Failed --> [*]: restartPolicy=Never
Init Container
Init Container在应用容器启动前按顺序执行,常用于初始化工作:
1 | |
Pod Hook
Kubernetes提供postStart和preStop钩子来处理容器启停事件:
1 | |
控制器模式
Deployment滚动更新
sequenceDiagram
participant User as 用户
participant DC as Deployment Controller
participant RS1 as ReplicaSet v1
participant RS2 as ReplicaSet v2
User->>DC: 更新镜像版本
DC->>RS2: 创建新ReplicaSet(replicas=0)
loop 滚动更新
DC->>RS2: Scale Up (+1)
Note over RS2: 新Pod就绪
DC->>RS1: Scale Down (-1)
Note over RS1: 旧Pod终止
end
Note over RS1: replicas=0
Note over RS2: replicas=N(期望值)
1 | |
StatefulSet
StatefulSet为有状态应用提供稳定的网络标识和持久化存储:
1 | |
网络模型
Kubernetes网络模型的核心要求:
- Pod内通信:同一Pod内的容器共享网络命名空间,通过localhost通信
- Pod间通信:所有Pod可以直接通信,不需要NAT
- Service通信:通过ClusterIP实现服务发现和负载均衡
- 外部通信:通过NodePort、LoadBalancer或Ingress暴露服务
graph TB
subgraph Node1["节点1 (10.0.1.1)"]
subgraph Pod1["Pod A (10.244.1.2)"]
C1["Container 1<br>:8080"]
C2["Container 2<br>:9090"]
end
subgraph Pod2["Pod B (10.244.1.3)"]
C3["Container 3<br>:8080"]
end
Bridge1["cbr0 (10.244.1.0/24)"]
end
subgraph Node2["节点2 (10.0.1.2)"]
subgraph Pod3["Pod C (10.244.2.2)"]
C4["Container 4<br>:8080"]
end
Bridge2["cbr0 (10.244.2.0/24)"]
end
C1 <-.->|localhost| C2
Pod1 --> Bridge1
Pod2 --> Bridge1
Pod3 --> Bridge2
Bridge1 <-->|"VXLAN/BGP<br>Overlay/Underlay"| Bridge2
实战:使用kubeadm搭建集群
1 | |
总结
Kubernetes的架构设计体现了以下核心理念:
- 声明式API:用户描述期望状态,系统负责实现
- 控制器模式:通过调谐循环不断收敛到期望状态
- 松耦合:各组件通过API Server通信,职责单一
- 可扩展:CRI/CNI/CSI等接口允许灵活替换底层实现
理解这些核心架构概念,是高效使用和运维Kubernetes的基础。后续文章将深入探讨网络模型、资源管理和调度策略等进阶话题。
踩坑记录
团队从 Docker Compose 迁移到 K8s 后第一次大促,Pod 开始出现
OOMKilled。排查发现我们没有给 Java 应用设置
-XX:MaxRAMPercentage,JVM
堆大小默认按宿主机内存计算(128G),而 Pod
limit 只有 2G,堆开到 96G 后直接被 cgroup 杀掉。
修复很简单:在 JVM 参数里加
-XX:MaxRAMPercentage=75.0,让 JVM
感知容器内存限制。但这个坑需要亲自踩一次才能记住——Docker 容器下 JVM
的内存感知问题在 JDK 8u191 之后才得到修复,很多老教程根本没提这个。
第二个坑是 requests 和 limits
设置。刚迁移时图省事,所有服务都不设 requests,结果
Scheduler 把大量 Pod 调度到同一个节点,节点内存耗尽后 kubelet
开始随机驱逐 Pod,触发了一次意外的服务中断。设置合理的
requests 是 K8s 资源管理的基础,不能省。
实测结果
迁移前:12 台 8 核 16G 的 ECS,平均 CPU 利用率 15%,内存 40%(大量浪费)
| 指标 | 迁移前(ECS) | 迁移后(K8s) |
|---|---|---|
| 机器利用率(CPU) | 15% | 62% |
| 机器利用率(内存) | 40% | 78% |
| 滚动发布耗时 | 平均 45 分钟 | 平均 8 分钟 |
| 故障恢复(Pod 自愈) | 手动,平均 10 分钟 | 自动,< 30 秒 |
我的看法
K8s 最大的价值不是弹性伸缩,而是统一了运维标准。迁移之前,每个服务的部署方式都不一样,运维靠口口相传的经验。K8s 强制你用声明式 YAML 描述服务状态,这本身就逼着团队把运维知识沉淀到代码里,可以 review、可以 diff、可以回滚。
弹性伸缩是锦上添花,标准化才是核心价值。如果团队规模小、服务数量少,K8s 的学习和运维成本未必值得,Docker Compose 反而更轻量实用。