696 lines
28 KiB
Markdown
696 lines
28 KiB
Markdown
---
|
||
tags: [k8s, architecture, devops]
|
||
create time: 2026-07-07 15:48
|
||
---
|
||
|
||
# Kubernetes 架构深度解析
|
||
|
||
## 概述
|
||
|
||
Kubernetes 采用经典的**控制平面 + 数据平面**分层架构。控制平面(Control Plane)负责全局决策——调度、编排、状态收敛;数据平面(Data Plane)由每个节点上的 Agent 组成,负责 Pod 的实际运行与网络转发。本文从 API Server、etcd、Scheduler、Controller Manager 四大控制平面组件,到 kubelet、kube-proxy、Container Runtime 三大数据平面组件,逐一拆解其内部机制、设计动机与协作关系,最后通过一次完整的 `kubectl apply` 请求流转,串联所有组件的工作链路。
|
||
|
||
---
|
||
|
||
## 控制平面(Control Plane)
|
||
|
||
控制平面是 Kubernetes 集群的"大脑",所有声明式状态的写入、决策与协调均在此完成。
|
||
|
||
### API Server(kube-apiserver)
|
||
|
||
API Server 是整个集群的**唯一入口**(Single Source of Truth)。所有组件——kubectl、kubelet、Controller Manager、Scheduler——都只和 API Server 通信,绝不直接操作 etcd。
|
||
|
||
#### 为什么这样设计?
|
||
|
||
- **统一鉴权**:所有请求经过同一套认证/授权/准入链,避免各组件自行实现安全逻辑。
|
||
- **解耦存储**:API Server 作为 etcd 的代理层,后端存储引擎可替换(理论层面),上层组件无需感知。
|
||
- **可观测性**:集中入口便于审计日志、请求速率限制等横向关注点的统一治理。
|
||
|
||
#### 请求处理链(Request Pipeline)
|
||
|
||
一次 API 请求经过以下阶段:
|
||
|
||
```
|
||
Client Request
|
||
→ Authentication(认证)
|
||
→ Authorization(授权)
|
||
→ Admission Control(准入控制,Mutating → Validating)
|
||
→ Schema Validation(对象校验)
|
||
→ etcd Write(持久化)
|
||
```
|
||
|
||
| 阶段 | 作用 | 常见实现 |
|
||
|------|------|----------|
|
||
| Authentication | 确认"你是谁" | X.509 Client Cert、ServiceAccount Token、OIDC、Webhook Token |
|
||
| Authorization | 确认"你能做什么" | RBAC(主流)、ABAC、Webhook |
|
||
| Mutating Admission | 修改对象(注入 sidecar、设置默认值) | MutatingWebhookConfiguration |
|
||
| Validating Admission | 拒绝非法对象(资源限额、策略校验) | ValidatingAdmissionPolicy、OPA/Gatekeeper |
|
||
| Schema Validation | 校验字段类型与必填项 | 内置 OpenAPI Schema |
|
||
|
||
> [!info] RBAC 的三个核心概念
|
||
> - **Role / ClusterRole**:定义一组权限(动词 + 资源)。
|
||
> - **RoleBinding / ClusterRoleBinding**:将 Role 绑定到 Subject(User、Group、ServiceAccount)。
|
||
> - **聚合 ClusterRole**:通过 Label Selector 合并多个 ClusterRole,扩展权限时无需修改原角色。
|
||
|
||
#### Watch 机制
|
||
|
||
API Server 支持 **Watch**——客户端可以长轮询监听资源变更,而非反复 List 轮询。其实现原理:
|
||
|
||
1. 客户端发送 `GET /api/v1/pods?watch=true&resourceVersion=12345`。
|
||
2. API Server 从 etcd 的 Watch 流中获取变更事件,经序列化后通过 HTTP chunked transfer / HTTP/2 stream 推送给客户端。
|
||
3. 如果客户端的 `resourceVersion` 过旧(已被 API Server compaction),返回 `410 Gone`,客户端需重新 List。
|
||
|
||
> [!tip] Informer 机制
|
||
> Kubernetes client-go 库中的 Informer 封装了 Watch + 本地缓存(Indexer)+ 事件分发(EventHandler)的完整流程。所有 Controller 内部都依赖 Informer 来感知资源变化,避免直接对 API Server 发起大量请求。
|
||
|
||
#### 与 etcd 的交互
|
||
|
||
- **写操作**:API Server 是唯一向 etcd 写入数据的组件。
|
||
- **读操作**:其他组件(Scheduler、Controller Manager)通过 API Server 读取,API Server 内部有 `apiserver cache` 层优化读性能。
|
||
- **一致性保证**:API Server 使用 etcd 的 MVCC(Multi-Version Concurrency Control)实现乐观并发控制,通过 `resourceVersion` 字段做冲突检测。
|
||
|
||
---
|
||
|
||
### etcd
|
||
|
||
etcd 是 Kubernetes 的**唯一持久化存储**,存储了集群的所有状态数据:Pods、Services、ConfigMaps、Secrets、Nodes、Namespaces 等。
|
||
|
||
#### Raft 一致性协议
|
||
|
||
etcd 使用 Raft 协议保证分布式一致性。Raft 将节点分为三种角色:
|
||
|
||
| 角色 | 职责 | 选举条件 |
|
||
|------|------|----------|
|
||
| Leader | 处理所有写请求,日志复制到 Follower | 获得多数节点投票 |
|
||
| Follower | 接收 Leader 日志,被动同步 | 默认状态 |
|
||
| Candidate | 发起选举 | Follower 在 election timeout 内未收到心跳 |
|
||
|
||
**写入流程**:
|
||
|
||
1. 客户端写请求发送至 Leader。
|
||
2. Leader 将日志条目(Log Entry)追加到本地日志。
|
||
3. Leader 将日志复制到所有 Follower。
|
||
4. **多数派(majority)**确认后,Leader 提交(commit)该条目。
|
||
5. Leader 通知 Follower 提交,状态机应用变更。
|
||
|
||
> [!warning] etcd 集群节点数建议
|
||
> 生产环境建议 **3 或 5 个节点**。3 节点容忍 1 个故障,5 节点容忍 2 个故障。偶数节点(如 4)不增加容错能力反而增加复制开销,不推荐。
|
||
|
||
#### Key-Value 结构
|
||
|
||
etcd 以扁平的 Key 空间存储数据,Kubernetes 的资源路径映射为:
|
||
|
||
```
|
||
/registry/pods/default/my-pod
|
||
/registry/deployments/default/my-deploy
|
||
/registry/services/kube-system/kube-dns
|
||
/registry/minions/node-1
|
||
```
|
||
|
||
每个 Value 是对应资源对象的 **Protocol Buffer 编码**。
|
||
|
||
#### Watch 特性
|
||
|
||
etcd 原生支持基于 revision 的 Watch。API Server 的 Watch 机制正是建立在 etcd Watch 之上。etcd 使用 **MVCC** 保留历史版本,支持从任意 revision 回放事件,这也是 `resourceVersion` 机制的底层基础。
|
||
|
||
#### 备份与恢复策略
|
||
|
||
- **定期快照**:`etcdctl snapshot save` 创建一致性快照。
|
||
- **自动 compaction**:通过 `--auto-compaction-retention` 配置历史版本保留时间,防止存储无限增长。
|
||
- **恢复流程**:`etcdctl snapshot restore` 恢复数据到新目录 → 重启 etcd 进程。
|
||
|
||
> [!tip] etcd 的磁盘性能至关重要
|
||
> etcd 对磁盘延迟极其敏感。生产环境应使用 SSD,避免与其他 I/O 密集型应用共享磁盘。可以使用 `fio` 工具检测磁盘 IOPS 和延迟。
|
||
|
||
---
|
||
|
||
### Scheduler(kube-scheduler)
|
||
|
||
Scheduler 负责为每个未绑定 Node 的 Pod 选择最优节点。其核心流程是**过滤(Filter)→ 打分(Score)**两阶段调度。
|
||
|
||
#### 调度流程
|
||
|
||
```
|
||
Pod Created (spec.nodeName 为空)
|
||
→ 1. Filtering(过滤阶段)
|
||
- 移除不满足硬性条件的节点
|
||
- 例:资源不足、NodeSelector 不匹配、Taint 无对应 Toleration
|
||
→ 2. Scoring(打分阶段)
|
||
- 对剩余节点按策略打分
|
||
- 默认策略倾向于:资源均衡分布、亲和性匹配、数据局部性
|
||
→ 3. Binding(绑定阶段)
|
||
- 选择得分最高的节点,写入 Pod.spec.nodeName
|
||
- API Server 通知对应节点的 kubelet
|
||
```
|
||
|
||
> [!info] 为什么是 Filter + Score 两阶段?
|
||
> 过滤阶段是**约束满足问题(CSP)**,用排除法缩小候选集;打分阶段是**优化问题**,在可行解中找最优。两阶段分离使得每一步可以独立扩展和插件化。
|
||
|
||
#### 亲和性与反亲和性
|
||
|
||
| 类型 | 作用域 | 典型场景 |
|
||
|------|--------|----------|
|
||
| `nodeAffinity` | Pod → Node | 将 Pod 调度到特定标签的节点(如 GPU 节点) |
|
||
| `podAffinity` | Pod → Pod | 将相关 Pod 调度到同一拓扑域(如日志收集器靠近应用) |
|
||
| `podAntiAffinity` | Pod → Pod | 将同组 Pod 分散到不同节点(高可用) |
|
||
|
||
亲和性规则分为两种强度:
|
||
- **requiredDuringSchedulingIgnoredDuringExecution**:硬性要求,不满足则不调度。
|
||
- **preferredDuringSchedulingIgnoredDuringExecution**:软性偏好,尽量满足。
|
||
|
||
> [!tip] "IgnoredDuringExecution" 的含义
|
||
> Pod 一旦调度到某节点,即使该节点后来不再满足亲和性条件,也不会被驱逐(除非配置了 `requiredDuringScheduling` 节点亲和)。这是为了避免频繁迁移造成抖动。
|
||
|
||
#### Taint / Toleration
|
||
|
||
- **Taint**:施加在 Node 上的排斥标记,格式 `key=value:effect`。
|
||
- **Toleration**:Pod 上声明的容忍声明,允许 Pod 调度到带对应 Taint 的节点。
|
||
|
||
Effect 有三种:
|
||
|
||
| Effect | 含义 |
|
||
|--------|------|
|
||
| `NoSchedule` | 不允许新 Pod 调度到该节点(已运行的不受影响) |
|
||
| `PreferNoSchedule` | 尽量不调度,但非强制 |
|
||
| `NoExecute` | 新 Pod 不调度 + 已运行且无 Toleration 的 Pod 会被驱逐 |
|
||
|
||
> [!warning] Master 节点的 Taint
|
||
> 生产集群的 Master 节点通常带有 `node-role.kubernetes.io/control-plane:NoExecute` Taint,防止业务 Pod 消耗控制平面资源。如果需要在 Master 运行监控组件,需添加对应 Toleration。
|
||
|
||
#### PriorityClass
|
||
|
||
PriorityClass 定义 Pod 的调度优先级。当集群资源不足时,低优先级 Pod 可能被**抢占(Preemption)**——即 Scheduler 驱逐低优先级 Pod 为高优先级 Pod 腾出空间。
|
||
|
||
```yaml
|
||
apiVersion: scheduling.k8s.io/v1
|
||
kind: PriorityClass
|
||
metadata:
|
||
name: high-priority
|
||
value: 1000000
|
||
globalDefault: false
|
||
description: "用于关键业务 Pod"
|
||
```
|
||
|
||
> [!info] 调度框架(Scheduling Framework)
|
||
> Kubernetes 1.19+ 引入 Scheduling Framework,将调度过程拆分为多个扩展点(QueueSort、PreFilter、Filter、PostFilter、Score、Reserve、Permit、Bind、PostBind),插件可以在任意扩展点介入,极大提升了调度器的可扩展性。
|
||
|
||
---
|
||
|
||
### Controller Manager(kube-controller-manager)
|
||
|
||
Controller Manager 是一组**控制器(Controller)**的集合进程,每个控制器负责一种资源类型的持续协调。
|
||
|
||
#### 控制循环(Reconcile Loop)原理
|
||
|
||
所有控制器遵循同一个模式——**声明式控制循环**:
|
||
|
||
```mermaid
|
||
graph LR
|
||
A[Observe: 观察当前状态] --> B[Diff: 比较期望状态与实际状态]
|
||
B --> C{存在差异?}
|
||
C -->|是| D[Act: 执行操作收敛差异]
|
||
D --> A
|
||
C -->|否| E[Wait: 等待下一次事件触发]
|
||
E --> A
|
||
```
|
||
|
||
这就是 Kubernetes 的核心哲学:**不要告诉系统"怎么做"(命令式),而是告诉它"要什么"(声明式),系统自行收敛到期望状态。**
|
||
|
||
#### 常见控制器详解
|
||
|
||
##### Deployment Controller
|
||
|
||
- 监听 Deployment 对象的变更。
|
||
- 当 `.spec.replicas` 或 `.spec.template` 发生变化时,创建或更新对应的 **ReplicaSet**。
|
||
- 支持 **Rolling Update**(滚动更新):创建新版 ReplicaSet 逐步扩容,同时缩减旧版。
|
||
- 支持 **Rollback**:通过 `kubectl rollout undo` 切换回旧版 ReplicaSet。
|
||
|
||
**Deployment → ReplicaSet → Pod** 的层级关系:
|
||
|
||
```
|
||
Deployment (my-app)
|
||
├── ReplicaSet (my-app-7d8b9f, revision 2) ← 当前版本
|
||
│ ├── Pod-1
|
||
│ ├── Pod-2
|
||
│ └── Pod-3
|
||
└── ReplicaSet (my-app-4a6c2e, revision 1) ← 旧版本(保留用于回滚)
|
||
```
|
||
|
||
##### ReplicaSet Controller
|
||
|
||
- 确保指定数量的 Pod 副本始终运行。
|
||
- 当 Pod 数量 < `replicas` 时创建新 Pod。
|
||
- 当 Pod 数量 > `replicas` 时删除多余 Pod(优先删除处于异常状态的 Pod)。
|
||
- 使用 **Label Selector** 匹配归属的 Pod。
|
||
|
||
##### Node Controller
|
||
|
||
- 监控每个 Node 的健康状态(通过 kubelet 上报的 `NodeStatus`)。
|
||
- 当 Node 的 `Ready` 条件变为 `Unknown`(默认 40 秒未收到心跳)时,标记为 `NodeNotReady`。
|
||
- 经过 `--pod-eviction-timeout`(默认 5 分钟)后,驱逐该节点上的所有 Pod,触发 Scheduler 重新调度。
|
||
|
||
**Node 状态条件**:
|
||
|
||
| Condition | 含义 |
|
||
|-----------|------|
|
||
| `Ready` | kubelet 心跳正常 |
|
||
| `MemoryPressure` | 节点内存不足 |
|
||
| `DiskPressure` | 节点磁盘不足 |
|
||
| `PIDPressure` | 节点进程数过多 |
|
||
| `NetworkUnavailable` | 网络插件未就绪 |
|
||
|
||
##### Job Controller
|
||
|
||
- 管理批处理任务的生命周期。
|
||
- 确保 Job 中的 Pod 成功运行指定次数(`.spec.completions`)。
|
||
- 支持三种模式:单 Pod Job、固定完成次数 Job、工作队列 Job。
|
||
- 通过 `.spec.backoffLimit` 控制失败重试次数。
|
||
- `ttlSecondsAfterFinished` 可在完成后自动清理 Job。
|
||
|
||
> [!tip] 控制器如何避免重复处理?
|
||
> Informer 维护一个本地缓存的 `resourceVersion`,控制器在处理完一个事件后将当前版本记录下来。下次事件如果版本相同则跳过。此外,控制器的 Worker Queue 会对同一资源进行去重合并。
|
||
|
||
---
|
||
|
||
## 数据平面(Data Plane)
|
||
|
||
数据平面由集群中每个工作节点上的组件组成,负责 Pod 的实际创建、运行与网络通信。
|
||
|
||
### kubelet
|
||
|
||
kubelet 是运行在每个节点上的 **Node Agent**,是控制平面与数据平面之间的桥梁。
|
||
|
||
#### Pod 生命周期管理
|
||
|
||
kubelet 监听 API Server 中分配到本节点的 Pod,按照以下流程管理:
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> Pending: API Server 创建 Pod
|
||
Pending --> ContainerCreating: kubelet 接收
|
||
ContainerCreating --> Running: 所有容器启动成功
|
||
Running --> Succeeded: 所有容器正常退出(Exit 0)
|
||
Running --> Failed: 容器异常退出
|
||
Running --> Running: 健康检查通过
|
||
Failed --> Running: 重启策略触发(RestartPolicy)
|
||
Succeeded --> [*]
|
||
Failed --> [*]: 超过重启次数上限
|
||
```
|
||
|
||
**Pod 阶段(Phase)**:
|
||
|
||
| Phase | 含义 |
|
||
|-------|------|
|
||
| `Pending` | 已被 API Server 接受,但容器尚未全部创建 |
|
||
| `Running` | 至少一个容器正在运行 |
|
||
| `Succeeded` | 所有容器正常终止 |
|
||
| `Failed` | 至少一个容器异常终止 |
|
||
| `Unknown` | 无法获取 Pod 状态(通常是节点失联) |
|
||
|
||
#### CRI 调用链
|
||
|
||
kubelet 通过 **CRI(Container Runtime Interface)** 与容器运行时交互。CRI 是基于 gRPC 的标准接口:
|
||
|
||
```
|
||
kubelet
|
||
→ CRI Client(内置)
|
||
→ Runtime Service(管理容器生命周期:RunPodSandbox、CreateContainer、StartContainer、StopContainer)
|
||
→ Image Service(管理镜像:PullImage、ListImages、RemoveImage)
|
||
→ containerd(主流实现)
|
||
```
|
||
|
||
> [!info] 为什么引入 CRI?
|
||
> Kubernetes 早期绑定 Docker,切换运行时需要重新编译 kubelet。CRI 作为抽象层,使得 kubelet 可以对接任何实现了 CRI 接口的运行时(containerd、CRI-O 等),实现了**运行时可插拔**。
|
||
|
||
#### 健康检查执行
|
||
|
||
kubelet 执行三类探针(Probe):
|
||
|
||
| 探针类型 | 作用 | 失败后果 |
|
||
|----------|------|----------|
|
||
| **Liveness Probe** | 检测容器是否存活 | 重启容器 |
|
||
| **Readiness Probe** | 检测容器是否就绪接收流量 | 从 Service Endpoint 中移除 |
|
||
| **Startup Probe** | 检测容器是否启动完成 | 在 Startup Probe 成功前,Liveness/Readiness 不生效 |
|
||
|
||
探针支持三种探测方式:
|
||
|
||
- **httpGet**:发送 HTTP GET 请求,2xx/3xx 视为成功。
|
||
- **tcpSocket**:尝试建立 TCP 连接,连接成功视为成功。
|
||
- **exec**:在容器内执行命令,退出码为 0 视为成功。
|
||
|
||
> [!warning] 没有 Readiness Probe 的后果
|
||
> 如果不配置 Readiness Probe,容器一启动就会被加入 Service Endpoint,可能在应用初始化完成前就收到流量,导致请求失败。生产环境中**强烈建议为每个 Pod 配置 Readiness Probe**。
|
||
|
||
---
|
||
|
||
### kube-proxy
|
||
|
||
kube-proxy 运行在每个节点上,负责实现 **Service** 的网络转发逻辑。
|
||
|
||
#### Service 实现原理
|
||
|
||
Service 是 Kubernetes 提供的**服务发现与负载均衡**抽象。一个 Service 通过 Label Selector 匹配一组后端 Pod,并分配一个稳定的虚拟 IP(ClusterIP)。
|
||
|
||
当客户端访问 `ServiceIP:Port` 时,kube-proxy 负责将流量转发到后端 Pod。
|
||
|
||
#### iptables 模式 vs IPVS 模式
|
||
|
||
| 维度 | iptables 模式 | IPVS 模式 |
|
||
|------|---------------|-----------|
|
||
| **实现方式** | 使用 iptables 规则链做 DNAT | 使用内核 IPVS(IP Virtual Server)模块 |
|
||
| **规则数量** | 随 Service/Endpoint 数量线性增长 | 使用 Hash 表,复杂度 O(1) |
|
||
| **性能** | Service 数量 < 1000 时表现良好 | Service 数量 > 1000 时性能优势明显 |
|
||
| **负载均衡算法** | 随机(概率相等) | 支持多种:rr、lc、dh、sh、sed、nq |
|
||
| **连接追踪** | 依赖 conntrack 模块 | IPVS 自带连接追踪 |
|
||
| **会话保持** | 不支持 | 支持(Client-IP 会话亲和) |
|
||
| **内核要求** | 标准 Linux 内核 | 需要 IPVS 内核模块(`ip_vs`、`ip_vs_rr` 等) |
|
||
| **适用场景** | 中小规模集群 | 大规模集群(Service 数量多) |
|
||
|
||
> [!tip] nftables 模式(Kubernetes 1.29+)
|
||
> Kubernetes 1.29 引入 nftables 代理模式作为 iptables 的替代。nftables 是 iptables 的继任者,提供更好的规则匹配性能和更简洁的语法。未来可能成为默认模式。
|
||
|
||
**iptables 模式的转发链路**:
|
||
|
||
```
|
||
Client → KUBE-SERVICES Chain
|
||
→ KUBE-SVC-XXXX (匹配 Service ClusterIP:Port)
|
||
→ KUBE-SEP-YYYY (DNAT 到后端 Pod IP:Port,随机选择)
|
||
→ Pod
|
||
```
|
||
|
||
**IPVS 模式的转发链路**:
|
||
|
||
```
|
||
Client → IPVS Virtual Server (Service ClusterIP:Port)
|
||
→ IPVS Real Server (后端 Pod IP:Port,按算法选择)
|
||
→ Pod
|
||
```
|
||
|
||
> [!info] ClusterIP、NodePort、LoadBalancer 三种 Service 类型的关系
|
||
> - **ClusterIP**:分配集群内部虚拟 IP,kube-proxy 通过 iptables/IPVS 实现转发。
|
||
> - **NodePort**:在 ClusterIP 基础上,在每个节点开放一个端口(30000-32767),外部可通过 `NodeIP:NodePort` 访问。
|
||
> - **LoadBalancer**:在 NodePort 基础上,调用云厂商 API 创建外部负载均衡器,将流量转发到 NodePort。
|
||
|
||
---
|
||
|
||
### Container Runtime(containerd)
|
||
|
||
containerd 是目前 Kubernetes 最主流的容器运行时,从 Docker 中剥离出来的**工业级容器运行时**。
|
||
|
||
#### containerd 架构
|
||
|
||
```mermaid
|
||
graph TB
|
||
K[kubelet] -->|CRI gRPC| CRI[CRI Plugin]
|
||
CRI --> CT[Container Service]
|
||
CRI --> SS[Sandbox Service]
|
||
CRI --> IS[Image Service]
|
||
CT --> T[Task Manager]
|
||
T --> R[runc / youki]
|
||
IS --> CS[Content Store]
|
||
SS --> NS[Network Sandbox]
|
||
```
|
||
|
||
containerd 的核心模块:
|
||
|
||
| 模块 | 职责 |
|
||
|------|------|
|
||
| **CRI Plugin** | 实现 CRI gRPC 接口,翻译 kubelet 请求 |
|
||
| **Container Service** | 管理容器的元数据(创建、删除、查询) |
|
||
| **Task Manager** | 管理容器进程的生命周期(start、stop、pause) |
|
||
| **Content Store** | 管理镜像层(layer)的存储,支持 content-addressable 存储 |
|
||
| **Snapshotter** | 管理容器文件系统快照(overlayfs、btrfs 等) |
|
||
| **Events** | 发布容器生命周期事件 |
|
||
|
||
#### CRI 接口规范
|
||
|
||
CRI 定义了两组 gRPC Service:
|
||
|
||
1. **RuntimeService**:管理 Pod Sandbox 和容器的生命周期。
|
||
|
||
```protobuf
|
||
service RuntimeService {
|
||
rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse);
|
||
rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse);
|
||
rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse);
|
||
rpc StartContainer(StartContainerRequest) returns (StartContainerResponse);
|
||
rpc StopContainer(StopContainerRequest) returns (StopContainerResponse);
|
||
rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse);
|
||
rpc ListContainers(ListContainersRequest) returns (ListContainersResponse);
|
||
rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse);
|
||
// ...
|
||
}
|
||
```
|
||
|
||
2. **ImageService**:管理镜像的拉取、查询与删除。
|
||
|
||
```protobuf
|
||
service ImageService {
|
||
rpc PullImage(PullImageRequest) returns (PullImageResponse);
|
||
rpc ListImages(ListImagesRequest) returns (ListImagesResponse);
|
||
rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse);
|
||
rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse);
|
||
}
|
||
```
|
||
|
||
> [!info] Pod Sandbox 是什么?
|
||
> Pod Sandbox 是 CRI 中对 Pod 级别资源的抽象,包括网络命名空间、PID 命名空间等共享资源。containerd 中对应的是 `pause` 容器(也叫 infra 容器),它的唯一作用是持有命名空间,业务容器通过加入这些命名空间实现网络共享。
|
||
|
||
---
|
||
|
||
## 请求流转全过程
|
||
|
||
从 `kubectl apply -f deployment.yaml` 到 Pod 变为 `Running` 状态,整个请求经过以下完整路径:
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
actor User as 用户
|
||
participant kubectl as kubectl
|
||
participant API as API Server
|
||
participant etcd as etcd
|
||
participant DC as Deployment Controller
|
||
participant RSC as ReplicaSet Controller
|
||
participant SC as Scheduler
|
||
participant KL as kubelet
|
||
participant CRI as containerd
|
||
participant Pod as Pod
|
||
|
||
User->>kubectl: kubectl apply -f deploy.yaml
|
||
kubectl->>kubectl: 本地 YAML 校验
|
||
kubectl->>API: POST /apis/apps/v1/namespaces/{ns}/deployments
|
||
|
||
Note over API: Authentication (证书认证)
|
||
Note over API: Authorization (RBAC 鉴权)
|
||
Note over API: Admission Control (Mutating + Validating)
|
||
|
||
API->>etcd: 写入 Deployment 对象
|
||
etcd-->>API: OK
|
||
|
||
Note over API: 返回 201 Created 给 kubectl
|
||
API-->>kubectl: Deployment created
|
||
kubectl-->>User: deployment.apps/xxx created
|
||
|
||
Note over DC: Deployment Controller 通过 Watch 感知变更
|
||
|
||
DC->>API: 创建 ReplicaSet
|
||
API->>etcd: 写入 ReplicaSet 对象
|
||
etcd-->>API: OK
|
||
|
||
Note over RSC: ReplicaSet Controller 通过 Watch 感知变更
|
||
|
||
loop 创建 3 个 Pod (replicas=3)
|
||
RSC->>API: 创建 Pod (spec.nodeName 为空)
|
||
API->>etcd: 写入 Pod 对象
|
||
etcd-->>API: OK
|
||
end
|
||
|
||
Note over SC: Scheduler 通过 Watch 感知未调度的 Pod
|
||
|
||
loop 每个 Pod
|
||
SC->>SC: Filter: 过滤不满足条件的节点
|
||
SC->>SC: Score: 对候选节点打分
|
||
SC->>API: Bind Pod to Node
|
||
API->>etcd: 更新 Pod.spec.nodeName
|
||
etcd-->>API: OK
|
||
end
|
||
|
||
Note over KL: kubelet 通过 Watch 感知分配到本节点的 Pod
|
||
|
||
KL->>CRI: RunPodSandbox (创建 pause 容器)
|
||
CRI-->>KL: Sandbox ID
|
||
|
||
KL->>CRI: CreateContainer (创建业务容器)
|
||
CRI-->>KL: Container ID
|
||
|
||
KL->>CRI: StartContainer (启动业务容器)
|
||
CRI->>Pod: 容器进程启动
|
||
CRI-->>KL: OK
|
||
|
||
KL->>API: 更新 Pod Status 为 Running
|
||
API->>etcd: 更新 Pod Status
|
||
etcd-->>API: OK
|
||
```
|
||
|
||
### 关键阶段说明
|
||
|
||
1. **kubectl 阶段**:kubectl 做本地 YAML 解析与校验,然后通过 REST API 发送给 API Server。
|
||
2. **API Server 阶段**:经过完整的认证→授权→准入链后,将对象持久化到 etcd。
|
||
3. **Deployment Controller 阶段**:Watch 到 Deployment 变更,创建对应的 ReplicaSet。
|
||
4. **ReplicaSet Controller 阶段**:Watch 到 ReplicaSet 变更,创建指定数量的 Pod 对象(此时 Pod 尚未绑定节点)。
|
||
5. **Scheduler 阶段**:Watch 到未调度的 Pod,执行 Filter + Score 两阶段调度,将结果写入 `Pod.spec.nodeName`。
|
||
6. **kubelet 阶段**:目标节点的 kubelet Watch 到分配给自己的 Pod,通过 CRI 接口调用 containerd 创建和启动容器。
|
||
7. **状态更新**:kubelet 将 Pod 状态上报给 API Server,完成整个流程。
|
||
|
||
> [!tip] 整个过程大约需要几秒到几十秒
|
||
> 时间取决于镜像大小、节点资源状况、网络速度等因素。可以通过 `kubectl describe pod` 查看每个阶段的时间戳,定位瓶颈所在。
|
||
|
||
---
|
||
|
||
## 高可用架构
|
||
|
||
生产环境需要部署多 Master 节点以消除单点故障。
|
||
|
||
### 多 Master 部署
|
||
|
||
```mermaid
|
||
graph TB
|
||
LB[负载均衡器<br/>如 HAProxy / 云 LB]
|
||
|
||
subgraph Master 集群
|
||
M1[Master 1<br/>API Server + Scheduler + Controller Manager]
|
||
M2[Master 2<br/>API Server + Scheduler + Controller Manager]
|
||
M3[Master 3<br/>API Server + Scheduler + Controller Manager]
|
||
end
|
||
|
||
subgraph etcd 集群
|
||
E1[etcd 1]
|
||
E2[etcd 2]
|
||
E3[etcd 3]
|
||
end
|
||
|
||
subgraph Worker 集群
|
||
W1[Node 1]
|
||
W2[Node 2]
|
||
W3[Node 3]
|
||
end
|
||
|
||
LB --> M1
|
||
LB --> M2
|
||
LB --> M3
|
||
|
||
M1 --- E1
|
||
M2 --- E2
|
||
M3 --- E3
|
||
|
||
E1 --- E2
|
||
E2 --- E3
|
||
|
||
W1 --> LB
|
||
W2 --> LB
|
||
W3 --> LB
|
||
```
|
||
|
||
### 组件高可用机制
|
||
|
||
| 组件 | 高可用机制 | 说明 |
|
||
|------|-----------|------|
|
||
| **API Server** | 多实例 + 负载均衡 | 无状态组件,可水平扩展 |
|
||
| **etcd** | Raft 共识协议 | 3/5 节点集群,容忍 (n-1)/2 故障 |
|
||
| **Scheduler** | Leader Election | 多实例运行,仅 Leader 执行调度 |
|
||
| **Controller Manager** | Leader Election | 多实例运行,仅 Leader 执行控制循环 |
|
||
|
||
> [!info] Leader Election 机制
|
||
> Scheduler 和 Controller Manager 使用 Kubernetes Lease 对象实现 Leader Election。每个实例启动时尝试获取 Lease,获取成功者成为 Leader 并定期续租。Leader 故障后,其他实例在 Lease 过期后重新竞选。
|
||
|
||
### etcd 集群拓扑选择
|
||
|
||
**堆叠式(Stacked)拓扑**:
|
||
|
||
- etcd 与 Master 组件部署在同一节点。
|
||
- 优点:部署简单,节点数少。
|
||
- 缺点:Master 节点故障同时丢失 etcd 成员,可靠性较低。
|
||
|
||
**外部式(External)etcd 拓扑**:
|
||
|
||
- etcd 独立部署在专用节点上。
|
||
- 优点:Master 故障不影响 etcd 集群,可靠性更高。
|
||
- 缺点:节点数更多,运维复杂度增加。
|
||
|
||
| 维度 | 堆叠式 | 外部式 |
|
||
|------|--------|--------|
|
||
| 节点数 | 3 Master = 3 etcd | 3 Master + 3 etcd = 6 |
|
||
| 容错能力 | 1 节点故障 | 1 Master 故障 + 1 etcd 故障 |
|
||
| 部署复杂度 | 低 | 高 |
|
||
| 适用场景 | 测试/小规模生产 | 大规模生产 |
|
||
|
||
### 负载均衡方案
|
||
|
||
- **硬件 LB**:F5 等(传统数据中心)。
|
||
- **软件 LB**:HAProxy + Keepalived(自建集群)、Nginx(四层代理)。
|
||
- **云厂商 LB**:AWS NLB、阿里云 SLB、GCP Load Balancer(托管集群通常内置)。
|
||
|
||
> [!warning] API Server 的负载均衡是四层(TCP),不是七层(HTTP)
|
||
> 因为 API Server 使用 HTTP/2 + gRPC,七层负载均衡可能破坏长连接和流式请求。应使用 L4 负载均衡(如 HAProxy TCP mode、NLB)。
|
||
|
||
---
|
||
|
||
## 常见陷阱与最佳实践
|
||
|
||
### 陷阱
|
||
|
||
1. **etcd 磁盘性能不足导致集群卡顿**
|
||
- etcd 是延迟敏感型应用,磁盘 I/O 慢会导致 Leader 选举频繁、API Server 超时。
|
||
- 解决:使用 SSD,监控 `etcd_disk_wal_fsync_duration_seconds` 指标。
|
||
|
||
2. **RBAC 权限配置不当**
|
||
- 过于宽松的 ClusterRole 绑定带来安全风险;过于严格导致组件无法工作。
|
||
- 解决:遵循最小权限原则,使用 `kubectl auth can-i` 验证权限。
|
||
|
||
3. **未配置 Pod 资源请求(requests/limits)**
|
||
- 没有 requests,Scheduler 无法做出合理的调度决策。
|
||
- 没有 limits,单个 Pod 可能耗尽节点资源,影响其他 Pod。
|
||
- 解决:始终为容器设置 `resources.requests` 和 `resources.limits`。
|
||
|
||
4. **kubelet 证书过期**
|
||
- kubeadm 默认签发的证书有效期为 1 年。
|
||
- 解决:设置证书自动轮换(`--rotate-certificates`),或使用外部 CA 签发长周期证书。
|
||
|
||
5. **使用 `latest` 镜像标签**
|
||
- `latest` 标签不可追溯,回滚时无法确定之前的版本。
|
||
- 解决:始终使用明确的版本标签(如 `v1.2.3`)或镜像摘要(sha256)。
|
||
|
||
### 最佳实践
|
||
|
||
1. **控制平面组件监控**
|
||
- 监控 API Server 的请求延迟、错误率(`apiserver_request_duration_seconds`)。
|
||
- 监控 etcd 的 Leader 切换次数、提案提交延迟。
|
||
- 监控 Scheduler 的调度延迟与 Pending Pod 数量。
|
||
|
||
2. **分离 Master 与 Worker 节点**
|
||
- Master 节点不运行业务 Pod(通过 Taint 机制)。
|
||
- 避免业务负载影响控制平面稳定性。
|
||
|
||
3. **etcd 定期备份**
|
||
- 至少每日一次快照,保留 7 天。
|
||
- 备份存储在独立于集群的存储位置。
|
||
- 定期演练恢复流程。
|
||
|
||
4. **合理设置 Pod Disruption Budget (PDB)**
|
||
- 在节点维护(drain)时,PDB 保证最少可用 Pod 数量。
|
||
- 示例:`minAvailable: 2` 确保滚动维护时至少 2 个 Pod 运行。
|
||
|
||
5. **使用 Pod Priority and Preemption**
|
||
- 关键系统组件(如 CoreDNS、Ingress Controller)设置高优先级。
|
||
- 避免资源紧张时关键组件被驱逐。
|
||
|
||
---
|
||
|
||
## 延伸阅读
|
||
|
||
- [[k8s-01-overview]] — Kubernetes 基础概念概览
|
||
- Kubernetes 官方文档 - Architecture: https://kubernetes.io/docs/concepts/overview/components/
|
||
- etcd 官方文档: https://etcd.io/docs/
|
||
- Kubernetes Scheduler 源码: https://github.com/kubernetes/kubernetes/tree/master/pkg/scheduler
|
||
- containerd 官方文档: https://containerd.io/docs/
|
||
- 《Kubernetes in Action》第二版 — Marko Lukša
|
||
- 《Programming Kubernetes》— Michael Hausenblas, Stefan Schimanski
|