由于kubelet 因为有很多共享状态,所以彻底按照单一职责进行拆分。于是现在总结一下kubelet中关于共享状态的类型、并且选择的原因。
Kubernetes Kubelet 共享状态模式的选择策略
从您提供的 Kubelet 代码中,可以看到 Kubernetes 使用了多种共享状态管理模式。以下是这些模式的选择策略和适用场景:
共享状态模式及其选择标准
1. 集中式管理器模式
适用场景:
- 特定领域的复杂状态管理(如 Pod 状态、容器生命周期)
- 需要提供丰富 API 操作状态的场景
- 当状态有自己的生命周期和业务逻辑
选择依据:
go
klet.statusManager = status.NewManager(klet.kubeClient, klet.podManager, klet, kubeDeps.PodStartupLatencyTracker, klet.getRootDir())
klet.volumeManager = volumemanager.NewVolumeManager(...)
klet.probeManager = prober.NewManager(...)
当状态需要专门的业务逻辑处理、有独立的生命周期,且会被多个组件访问时,使用专门的管理器封装状态和相关操作。
2. 结构体字段 + 互斥锁模式
适用场景:
- 简单的共享状态需要原子更新
- 状态没有复杂的业务逻辑
- 直接附属于所有者对象的状态
选择依据:
go
// 保护 machineInfo 字段的互斥锁
machineInfoLock sync.RWMutex
machineInfo *cadvisorapi.MachineInfo
// 用于资源调整
podResizeMutex sync.Mutex
当状态比较简单,主要是数据结构的更新,不需要复杂的管理逻辑时,直接使用互斥锁保护字段更为简单高效。
3. 原子操作模式
适用场景:
- 简单的单一值状态
- 高并发读取场景
- 无需复杂的更新逻辑
选择依据:
go
// 使用 atomic.Value 存储同步循环监控时间戳
syncLoopMonitor atomic.Value
对于简单的、高频访问的单一值状态,使用原子操作可以提供更高的性能,避免锁的开销。
4. 缓存机制模式
适用场景:
- 需要高效查询的数据
- 从外部源获取的数据
- 读多写少的场景
选择依据:
go
// Pod 缓存
klet.podCache = kubecontainer.NewCache()
// 运行时缓存
runtimeCache, err := kubecontainer.NewRuntimeCache(klet.containerRuntime, runtimeCacheRefreshPeriod)
当需要频繁查询但不频繁更新的数据,使用缓存可以提高性能,减少对底层数据源的访问。
5. 事件通道模式
适用场景:
- 异步处理场景
- 组件间解耦
- 事件驱动的系统
选择依据:
go
// Pod 生命周期事件通道
eventChannel := make(chan *pleg.PodLifecycleEvent, plegChannelCapacity)
// syncLoop 中处理来自多个通道的事件
func (kl *Kubelet) syncLoopIteration(ctx context.Context, configCh <-chan kubetypes.PodUpdate, ... ) { ... }
通道模式适用于状态变更需要触发异步处理或通知其他组件的场景,有助于实现松耦合的事件驱动架构。
选择策略
在选择共享状态管理方式时,可考虑以下因素:
-
状态复杂度:
- 简单状态(单值)→ 原子操作或结构体字段+锁
- 中等复杂度 → 结构体字段+锁或缓存
- 高复杂度(多字段、复杂关系)→ 专用管理器
-
访问模式:
- 读多写少 → 缓存或原子操作(如
syncLoopMonitor
) - 写多读少 → 考虑通道模式(如事件通知)
- 读写平衡 → 结构体字段+锁
- 读多写少 → 缓存或原子操作(如
-
并发需求:
- 高并发读 → 原子操作或读写锁
- 需要事务性更新 → 互斥锁或专用管理器
-
生命周期管理:
- 有独立生命周期 → 专用管理器(如
volumeManager
) - 与宿主对象同生命周期 → 结构体字段+锁
- 有独立生命周期 → 专用管理器(如
-
业务逻辑复杂度:
- 状态有复杂业务规则 → 专用管理器
- 简单数据存取 → 结构体字段+锁或原子操作
实践建议
-
从简单开始:最初可以使用结构体字段+锁的简单方式
-
观察使用模式:随着系统发展,观察状态的使用方式
-
根据需要重构:当某个状态的管理变得复杂时,考虑提取为专门的管理器
-
保持一致性:同类型的状态应使用相同的管理方式,增强代码可理解性
-
考虑测试性:选择便于测试的状态管理方式,如通过接口封装管理器
Kubelet 的代码展示了如何在大型系统中合理选择状态管理模式,平衡了简单性、性能和可维护性的需求。根据您的具体场景选择适当的模式,可以显著提高代码质量和系统稳定性。