Files
Qiniu/technical/k8s/k8s-02-architecture.md
T

696 lines
28 KiB
Markdown
Raw Normal View History

2026-07-07 15:55:47 +08:00
---
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