3. Kubernetes核心架构:控制平面与工作节点
聊到Kubernetes,很多人第一反应就是“容器编排”。但说白了,它就是一个分布式操作系统。你想想看,操作系统管理进程、内存、磁盘,K8s管理容器、网络、存储。嗯,这个类比很关键,能帮你理解它的设计初衷。
我个人习惯把K8s架构拆成两大部分来看:控制平面和工作节点。控制平面是大脑,工作节点是手脚。两者配合,才能跑起来。
控制平面组件
控制平面就是K8s的“大脑”。它负责决策、调度、状态维护。我见过不少新手,一上来就部署集群,结果控制平面挂了,整个集群都瘫痪。所以,理解这几个组件,是玩转K8s的第一步。
API Server:集群的“门面”
API Server是K8s里唯一直接和etcd打交道的组件。所有操作,无论是kubectl、UI还是内部组件,都得经过它。它本质上就是一个RESTful API网关,负责认证、授权、准入控制。
我在项目中遇到过一个问题:某次集群突然响应变慢,排查半天发现是API Server的QPS被打满了。后来加了限流和缓存,才稳住。所以,API Server的性能瓶颈往往是集群的瓶颈。
核心职责:
- 提供RESTful API接口
- 认证与授权(RBAC、ABAC)
- 准入控制(Mutating & Validating Admission Webhooks)
- 数据持久化到etcd
Scheduler:聪明的“调度员”
Scheduler负责把Pod分配到合适的Node上。它可不是随机分配的,而是有一套复杂的算法。说白了,就是“找最合适的坑”。
调度过程分两步:过滤和打分。过滤掉不满足条件的节点(比如资源不足、端口冲突),然后对剩余节点打分,选最高分的。
我曾经踩过一个坑:默认调度器只考虑CPU和内存,没考虑磁盘IO。结果某个IO密集型的Pod被调度到了共享磁盘的节点上,性能惨不忍睹。后来我加了自定义调度器扩展,才解决。
小技巧:如果你有特殊需求,比如GPU、大内存、低延迟,可以用NodeSelector、NodeAffinity、Taints/Tolerations来干预调度决策。
Controller Manager:集群的“管家”
Controller Manager里跑着一堆控制器。每个控制器负责一种资源的状态维护。比如Deployment控制器、ReplicaSet控制器、Node控制器等等。
它的工作模式很简单:观察 -> 对比 -> 修正。从API Server获取当前状态,对比期望状态,然后执行操作让实际状态趋近期望状态。这就是K8s著名的“声明式”管理。
我记得有一次,集群里的某个Node挂了,但上面的Pod迟迟没被重新调度。查了半天,发现是Node Controller的监控周期太长。调整了--node-monitor-period参数后,恢复速度明显提升。
etcd:集群的“数据库”
etcd是一个分布式键值存储,K8s的所有数据都存这里。包括Pod、Service、ConfigMap、Secret等等。它基于Raft协议保证一致性。
这里我要强调一点:etcd是K8s的命根子。如果etcd挂了,整个集群就失忆了。我见过有人把etcd部署在同一个节点上,结果节点宕机,集群直接瘫痪。所以,etcd一定要做高可用部署,至少3个节点。
警告:etcd的磁盘性能直接影响集群响应速度。建议使用SSD,并定期做快照备份。我曾经因为磁盘IO瓶颈,导致API Server响应延迟从10ms飙升到2s。
工作节点组件
工作节点是真正跑业务的地方。每个节点上都有三个核心组件:kubelet、kube-proxy、容器运行时。
kubelet:节点的“代理人”
kubelet是每个节点上的代理,负责管理Pod的生命周期。它从API Server接收PodSpec,然后调用容器运行时创建、启动、停止容器。
kubelet还会定期向API Server上报节点状态,包括资源使用情况、Pod状态等。如果kubelet挂了,这个节点上的Pod就没人管了。
我遇到过一个问题:某个节点上的kubelet日志疯狂报错,说无法连接API Server。排查发现是网络策略把6443端口给封了。嗯,这种低级错误,谁都会犯一次。
kube-proxy:网络的“交通警察”
kube-proxy负责实现Service的负载均衡。它维护节点上的网络规则,把发往Service IP的流量转发到后端的Pod上。
kube-proxy有三种模式:userspace(老古董)、iptables(默认)、IPVS(高性能)。我个人推荐用IPVS模式,性能更好,规则更清晰。
我曾经在iptables模式下遇到过规则爆炸的问题。一个集群里Service多了,iptables规则能上万条,每次更新都卡半天。换成IPVS后,问题迎刃而解。
容器运行时:真正的“执行者”
容器运行时负责拉取镜像、创建容器、管理容器生命周期。常见的运行时包括Docker、containerd、CRI-O。
K8s通过CRI(Container Runtime Interface)与运行时交互。所以理论上,只要实现了CRI接口,任何运行时都能用。containerd是目前最主流的选择,轻量、稳定。
我记得有一次,生产环境的Docker版本太老,导致K8s无法正常管理容器。升级到containerd后,问题解决。所以,建议新集群直接用containerd。
Pod设计哲学与生命周期
Pod是K8s里最小的调度单元。它不是一个容器,而是一个或多个容器的组合。这些容器共享网络命名空间、存储卷,以及生命周期。
为什么要有Pod?说白了,就是为了解决“紧密协作的进程”的问题。比如一个Web服务器和一个日志收集器,它们需要共享网络和存储,用Pod最合适。
Pod设计哲学
- 单一职责:一个Pod通常只运行一个主容器,辅助容器(Sidecar)负责日志、监控、代理等。
- 共享网络:Pod内的容器共享同一个IP和端口空间,可以通过localhost通信。
- 共享存储:Pod内的容器可以挂载相同的Volume,实现数据共享。
- 原子调度:Pod要么全部调度到同一个节点,要么都不调度。
避坑指南:我曾经把两个需要独立扩缩容的服务放在同一个Pod里,结果发现根本没法单独管理。后来拆成两个Deployment,才恢复正常。记住:Pod内的容器应该是一荣俱荣、一损俱损的关系。
Pod生命周期
Pod的生命周期分为几个阶段:Pending -> Running -> Succeeded/Failed。还有两个特殊状态:Unknown(节点失联)和CrashLoopBackOff(容器反复崩溃)。
每个阶段都有对应的探针(Probe)来检测健康状态:
| 探针类型 | 作用 | 常用场景 |
|---|---|---|
| Liveness Probe | 检测容器是否存活 | 如果失败,kubelet会重启容器 |
| Readiness Probe | 检测容器是否就绪 | 如果失败,Service不会转发流量 |
| Startup Probe | 检测容器是否启动完成 | 用于启动慢的容器,避免被误杀 |
我建议每个Pod都配置Readiness Probe和Liveness Probe。尤其是启动慢的应用,一定要加Startup Probe。我曾经因为没配探针,导致一个Java应用启动时被反复重启,整整折腾了半小时。
小技巧:Pod的终止过程是优雅的。当Pod被删除时,kubelet会先执行PreStop Hook,然后发送SIGTERM信号,等待terminationGracePeriodSeconds(默认30秒),最后发送SIGKILL。所以,你的应用要正确处理SIGTERM信号,实现优雅关闭。
好了,这一章的内容就到这里。K8s的架构其实不复杂,但每个组件都有自己的脾气。理解它们的设计哲学,你就能更好地驾驭这个分布式操作系统。下一章,我们会深入Pod的调度策略,聊聊怎么让Pod“住”在最合适的节点上。