ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深入理解Kubernetes架构:控制平面、节点与网络全解析

深入理解Kubernetes架构:控制平面、节点与网络全解析 在云原生这块混久了你会发现一个特别有意思的现象很多人K8s用得飞起Deployment、Service、Ingress信手拈来但一旦被问到“K8s整体架构到底是怎样的”立刻就蔫了。要么只能背出Master和Node两个词要么把etcd、kube-apiserver、kubelet这些组件的关系说得一团糟。这不怪大家K8s的架构设计本身就不是那种“看一眼拓扑图就能懂”的东西它更像一张由控制循环、声明式API和事件驱动织成的大网你只有真正理解了这张网是怎么兜住成千上万个容器的才能在看任何一张K8s架构图时一眼就能找到自己的船在哪。这篇文章我想好好聊聊K8s技术架构这件事。不抄官方文档不堆架构图就用一个在一线运维和开发里反复摸爬滚打的人的口吻把控制面、节点面、Pod网络、控制器模式、部署选型、高可用设计、常用命令和GPU调度这些核心东西全部捋一遍。不管你是刚准备学K8s的入门者还是已经被线上问题折磨过几轮的开发者这篇文章都值得你花二十分钟慢慢看。1. 先别急着看拓扑图理解K8s架构的三个平面很多人第一次看K8s架构图第一反应是“这什么玩意儿怎么这么多方块和箭头”。我的建议是别一头扎进去数组件先把K8s的架构拆成三个平面去理解控制平面、工作节点平面、以及贯穿两者的网络平面。这三个平面一旦在脑子里立起来后续所有组件的关系都是水到渠成的事。1.1 控制平面整个集群的大脑和决策层控制平面负责所有“决策类”的事情。它不跑你的业务容器它只负责回答“集群现在应该处于什么状态”以及“怎样从当前状态逼近目标状态”。我们常说的Master节点就是控制平面的载体。一个标准的控制平面包含四个核心组件kube-apiserver、etcd、kube-controller-manager和kube-scheduler。这四个组件各有分工但协作逻辑有点像一家公司的“高管团队”——kube-apiserver是唯一的对外窗口所有请求都必须从这个门走etcd是记忆库所有状态都存在这controller-manager是执行总监盯着现实和目标的差距并推动落实scheduler是排班经理决定一个新来的Pod到底安排到哪台机器上去干活。这里我想特别强调一下kube-apiserver。它是整个集群的“交通枢纽”所有组件的通信、所有kubectl命令的执行最终都汇聚到它这里。它的设计是水平扩展友好的多副本部署时通过负载均衡对外提供服务但真正让它在架构中变得如此关键的原因是它是唯一直接读写etcd的组件。也就是说etcd里的数据不能绕过apiserver被其他组件直接改这保证了集群状态的一致性和安全性。1.2 工作节点平面真正干活的地方工作节点Node是业务容器实际运行的机器。每个节点上至少有kubelet、容器运行时比如containerd或Docker和kube-proxy三个进程。kubelet是apiserver安插在每个节点上的“监工”负责接收Pod定义、拉起容器、持续上报节点和Pod的健康状态。kube-proxy则负责处理Service的网络规则把发往Service VIP的流量按策略转发到后端Pod。我见过不少初学者问“kubelet是不是有点像Docker Compose”这么比喻其实不太严谨但方向是对的——kubelet确实管理着节点上所有容器的生命周期但它的管理方式比Compose复杂得多。它不只是“创建容器”还负责探针检查、资源预留、日志轮转、镜像拉取策略执行以及向控制面反馈节点压力状态。而kube-proxy通常以DaemonSet的形式跑在每个节点上用iptables或IPVS规则来实现Service的负载均衡。1.3 网络平面让一切可以寻址、可以互通K8s的网络架构有四个不可违背的约定Pod之间必须可以任意通信、Node和Pod之间必须可以任意通信、Pod看到的自己的IP和其他Pod看到的它是一致的、Service的实现不能依赖NAT。这四条规定直接决定了K8s网络必须采用扁平化模型。CNI插件比如Calico、Flannel、Cilium就是在这个模型下负责打通容器网络的它们通过CNI标准接口被kubelet调用在Pod创建时分配IP并完成网络配置。很多初学者会把K8s网络和Docker网络混为一谈实际上它们解决问题的层次完全不同。Docker的bridge网络主要解决单机容器互通K8s的扁平网络则要解决跨节点甚至跨数据中心的Pod通信。这个设计差异也解释了为什么K8s架构天生适合大规模分布式部署——网络层没有中心瓶颈每个节点的数据转发路径都是平等且高效的。2. 核心组件逐一拆解架构图上的每个方块到底在干嘛前面说了三个平面的概念但你光有概念还不够最好能把每个核心组件的职责边界、工作方式、以及它们之间的通信路径都吃透。这是面试中最高频的考察点也是在实际排查问题时你最能依赖的知识储备。2.1 etcd整个集群的“唯一事实来源”etcd本质上是一个分布式键值存储它存储了整个集群的所有数据Pod定义、Service定义、ConfigMap、Secret、节点信息、资源配额甚至是你某个Deployment的期望副本数。K8s所有组件都不维护本地持久状态任何一个组件重启后它都会重新从etcd拉取数据来恢复认知。这也是为什么etcd的高可用是整个K8s集群高可用的底线——etcd挂了控制面就失忆了。我遇到过有人问“能不能把etcd从Master节点挪出去单独部署”答案是能而且生产环境很多团队确实会做独立etcd集群。但这里有一个容易被忽视的点etcd的延迟直接影响整个控制面的响应速度所以它和apiserver之间的网络必须是又快又稳的哪怕是千兆网卡的对拷延迟都可能让你在集群规模大时感觉到明显的性能下降。此外etcd的版本兼容性和K8s的版本挂钩非常紧升级K8s版本时通常要先确认etcd版本是否在支持矩阵内这个细节坑过不少人。2.2 kube-apiserver所有命令和组件的唯一入口前面提到apiserver是交通枢纽这里补充几个具体的机制细节。第一apiserver提供的是RESTful API你执行kubectl get pods本质上就是向apiserver发了一个GET /api/v1/namespaces/default/pods的请求。第二apiserver内置了认证Authentication、授权Authorization和准入控制Admission Control三层关卡一个请求要进到etcd必须依次闯关成功。第三apiserver是唯一无缝对接etcd的组件其他组件通过watch机制监听apiserver的资源变化而不是直接盯etcd所以apiserver天然承担了“状态广播中心”的角色。在生产环境中apiserver通常至少部署两个副本前面挂一个负载均衡器。但这会带来一个新的架构问题多个apiserver进程都在watch同一份etcd数据会不会出现状态重复或者不一致其实不会因为apiserver对apiserver之间的协调依赖的是etcd的watch和租约机制同一份资源变更事件只由apiserver转发一次。2.3 kube-controller-manager控制器模式的集中实现controller-manager是K8s架构中最能体现“控制器哲学”的组件。它内部包含了几十个不同的控制器循环比如Deployment控制器、ReplicaSet控制器、Node控制器、ServiceAccount控制器等。每一个控制器都在做同一件事不断比较“期望状态”和“当前状态”一旦发现差异就通过apiserver发指令着手修正。这里有个非常有用的理解方式控制器不是直接操作具体资源它只会向apiserver提交“修正动作”。比如ReplicaSet控制器发现某个Pod挂了它会创建一条创建新Pod的请求Node控制器发现某个节点失联超过阈值它会为这个节点上的Pod标记驱逐。这种“只汇报和请求不自己动手”的设计让K8s的故障自愈机制变得极其优雅——所有的修复动作都走正常的API流程可以被审计、可以被限流、可以被准入控制拦下。controller-manager本身也有高可用方案但它和apiserver不一样。apiserver多副本是靠前置LB做到的controller-manager多副本则是靠leader选举机制同一时刻只有一个副本在真正执行控制器逻辑其他副本处于待命状态。这个机制也值得你多留意排查“为什么某个控制器没生效”时第一件事就是看controller-manager当前哪个实例是leader。2.4 kube-scheduler为新Pod寻找最优归属scheduler的工作说起来很直接当一个Pod被创建而且没有被指定nodeName时scheduler就负责从集群里挑一个最合适的Node出来。但它背后的调度策略极其丰富包括节点亲和性nodeAffinity、Pod亲和性和反亲和性podAffinity/antiAffinity、污点与容忍taint/toleration、资源请求与可用量匹配、拓扑分布约束等等。调度流程可以简化为两个阶段过滤Filtering和打分Scoring。过滤阶段把不满足硬性条件的节点剔除比如资源不足、端口冲突、不满足nodeSelector打分阶段则根据各项软性策略给剩余节点打分选出分数最高者。打分策略里面有很多考量比如尽量把同一应用的Pod分散到不同节点以提高容灾能力这种策略叫SelectorSpreadPriority面试时如果你能主动提到它是会加分的。2.5 kubelet和kube-proxy节点上的左右手kubelet是控制面在节点上的代理但它并不是“被动执行”的。kubelet会主动上报节点状态、Pod状态、镜像状态还会执行存活探针livenessProbe和就绪探针readinessProbe。如果存活探针失败kubelet会按策略重启容器如果就绪探针失败kubelet会告诉控制面这个Pod暂时没有能力接收流量Service的端点列表会把它摘除。kube-proxy则低调很多它只做一件事保证ClusterIP的访问规则存在并且生效。它支持的代理模式有三种userspace最早现在基本没人用、iptables默认最常用、IPVS适合大规模集群。三者的核心区别在复杂度和性能。iptables模式在Service数量成千上万时会因为规则链太长而出现明显延迟IPVS则用内核哈希表实现伸缩性更好。如果你维护的是上千个Service的集群建议直接切到IPVS模式。3. 从“如何部署”反推架构为什么控制面高可用这么折腾K8s架构学到最后你会发现最考验功力的不是背组件而是“把架构落地”。特别是高可用集群的搭建往往能让一个自认为懂K8s的人怀疑人生。三台Master到底怎么保证高可用etcd和Master绑在一起还是分离部署这背后都是架构决策。3.1 高可用集群的关键apiserver前置LB etcd多数派在正式环境里三台Master的高可用通常这么设计kube-apiserver三副本全部启动前面挂一个负载均衡器通常是HAProxy Keepalived或云LB三副本共享同一个etcd集群etcd也部署在三台Master上etcd通过Raft协议选举leader写操作必须超过半数节点确认所以三副本正好能容忍一个节点宕机。这里有一个容易绕晕的点既然apiserver是多副本且前面有LB那kubelet或kubectl连LB时会不会把请求打到不同副本上导致数据不一致不会因为apiserver本身是无状态的它以etcd为准所有副本读到的数据都来自同一个etcd。所以三台Master高可用的本质是“apiserver层无状态化 etcd层有状态多数派”。这个组合拳打好了集群就硬气多了。3.2 部署工具选型kubeadm、二进制还是KubeKey我知道很多人在部署K8s时会纠结工具选型。这里给出我的经验判断如果只是学习或测试环境直接用kubeadm官方支持最好、文档最多、排错最容易如果是离线部署或需要高度定制可以用二进制方式手动部署虽然折腾但能让你对架构理解加深一大截如果是生产环境且不想自己维护太多部署细节KubeKey支持KubeSphere全家桶这类工具就很合适它对高可用部署的封装做得比较完整。有一个容易踩的坑kubeadm默认生成的证书有效期只有一年如果你不想一年后集群突然失联必须提前做好证书续期方案。官方推荐的做法是设置定时任务跑kubeadm cert renew all并把续期后的证书分发到所有节点。这个问题在社区里被问过无数次但每年依然有大量集群因为证书过期而挂掉真的需要重视。3.3 多Master集群的故障切换实测体验我自己搭建三Master集群时遇到过一件很有意思的事我把第一个Master的kubelet停掉观察集群会不会自动切换apiserver。当时的真实情况是整个集群的读写并没有中断因为LB把流量转发到了另外两个Master上但如果停掉的是etcd的leader节点就会观察到短暂的写延迟因为etcd需要重新选举leader这段窗口期通常持续几百毫秒到几秒钟。这个实验给我的启发是在做架构设计时一定要区分“apiserver故障”和“etcd故障”它们的高可用机制完全不同故障影响面也完全不同。apiserver故障是前置LB立刻切换几乎无感知etcd故障是多数派重新选举可能会有短暂写阻塞。理解了这一点你就能明白为什么有些团队会把etcd单独部署在延迟极低的三台专用机器上绝不心疼这点额外成本。4. 从架构延伸出的几个高频问题ExternalIP、Docker对比、GPU调度和Operator架构学透了再回头看那些热搜关键词里的具体问题你会发现它们都不是孤立的。ExternalIP、K8s和Docker的区别、调用GPU、Operator这些本质上都是从架构逻辑中派生出来的实践问题。4.1 ExternalIP和Service流量路径很多人在配置Service时想让外部直接通过某个IP访问集群内的Pod这时候就会用到ExternalIP。Kube-proxy会监听Service定义里的externalIPs字段把发往这个IP的流量按VIP规则转发到后端Pod。看起来很简单但这里有几个前提必须满足这个IP必须已经路由到你集群的节点上而且不能和任何Pod IP、节点IP冲突。我的建议是非必要不直接用ExternalIP因为它的管理和可移植性都很差。更推荐的做法是用CloudProvider的LoadBalancer或者干脆用Ingress暴露HTTP服务。ExternalIP更适合那些控制面网络在自己手里的裸金属环境需要快速暴露少量服务但又不想引入额外组件的时候使用。4.2 K8s和Docker的区别别再只是说“一个是编排一个是容器”这个问题几乎是被面试官问烂了但很多人的回答还停留在“Docker是容器引擎K8s是容器编排平台”这个层面。我可以给你一个比较有信息量的说法Docker的核心是“单机容器管理”它把进程、文件系统、网络栈封装成一个可复制的单元K8s的核心是“大规模容器编排”它把数百台机器上的容器当作一个统一的资源池通过声明式API和控制器循环保证应用始终运行在想让它运行的状态中。更进一步说K8s架构里甚至不直接认Docker它经由CRIContainer Runtime Interface与容器运行时交互。Docker之前是默认运行时现在很多场景下已经换成containerd占用的资源更少也不需要额外维护Docker守护进程和K8s的通信适配层。这也能解释为什么现在新版的K8s节点上你经常看不到Docker相关的进程——不是它不重要了而是它被更轻的containerd替代了。4.3 K8s调度GPUExtended Resource机制和显存分配很多人需要让K8s调度GPU资源跑AI训练或推理这属于典型的“架构扩展能力”场景。K8s本身不认识GPU但它提供了Extended Resource机制GPU厂商以NVIDIA为例通过device plugin暴露资源把显卡数量、显存信息注册为节点的可分配资源。之后你在Pod里写上nvidia.com/gpu: 1调度器就能把Pod调度到有GPU的节点上。这里面有个非常容易踩的坑GPU资源通常要以显卡为单位调度但AI场景经常需要“一张卡多人用”也就是MIG或vGPU切分。默认的Extended Resource不支持小数资源如果你只想在共享GPU上分配十分之一算力就需要借助厂商定制方案比如NVIDIA MIG和vGPU的device plugin。不少团队在GPU集群设计早期没意识到这个粒度问题等到模型多了才发现一张卡被独占得厉害只能推倒重做成本相当高。4.4 Operator把运维经验写进控制循环Operator是K8s架构中一个进阶话题但它恰恰是理解整个架构设计哲学的最好例子。背景是这样K8s内置的控制器只能管理Pod、Deployment这类通用资源但像“一个生产级数据库集群”这种有状态应用的运维包含了备份、故障转移、扩缩容、升级等大量专家经验通用控制器根本无法覆盖。Operator的思路就是把专家经验写成代码并扩展K8s API让K8s像管理Pod一样管理这个数据库集群。一个Pod异常了Deployment能拉起一个全新的替代品一个数据库节点异常了你当然也可以让StatefulSet拉起一个替代品但谁来保证数据不丢、主从关系不变Operator把这些复杂判断全部封装在控制循环里并且可以借助自定义资源定义CRD暴露一个清晰的API给用户。我见过不少团队用了Operator后数据库运维成本肉眼可见地下降排障时间从小时级降到分钟级。这是K8s架构中最值得深入研究的方向之一也是未来很长时间内云原生领域的人才缺口所在。5. 常用命令背后的架构逻辑会看日志更要会看资源很多人在学K8s时习惯背命令kubectl get pods、kubectl logs背得滚瓜烂熟但一旦集群出了问题就不知道从哪里下手。我觉得这还是因为没把命令和架构结合起来。下面我按排查问题时的思路分享几个最常用的命令以及它们各自对应的架构环节。5.1 查状态kubectl get 和 kubectl describe 的互补使用kubectl get pods -o wide能看到Pod被调度到了哪个节点、Pod的IP是多少、是否就绪这对应的是从apiserver读Pod的当前状态。kubectl describe pod则能看到更详细的事件流比如镜像拉取失败、探针失败、被驱逐等信息这些事件其实是kubelet通过apiserver写下来的运行日志。事件信息的价值在于帮助你理解kubelet视角的“为什么”。很多人在Pod起不来时只盯着get events但忽略了一个更高效的做法结合kubectl get node看节点状态是否Ready、是否有内存或磁盘压力。因为Pod调度失败往往不是因为Pod本身有问题而是节点资源不足或节点被标记为不可调度这类信息一眼就能在节点状态里看出来。5.2 看资源kubectl top 和kubectl get all 的局限性kubectl top pod显示的是Pod的实时资源使用量数据来源是metrics-server采集的指标。如果没有部署metrics-server这个命令就会直接报错。很多刚上手的人在Portainer或Rancher界面里看到过图形化监控但在命令行里跑kubectl top失败就懵了其实原理很简单这个命令依赖集群内是否跑着metrics采集组件。kubectl get all虽然看起来能列出所有资源但它其实不包含ConfigMap、Secret、PVC这些非默认命名空间的资源也不包含由CRD定义的自定义资源所以排查问题时千万别依赖这一个命令否则很容易漏掉真正的异常点。我的习惯是查Pod用get pods查服务发现用get svc和get endpoints查配置用get cm分门别类地去看比一把梭更高效。5.3 日志和交互kubectl logs / exec / cp 的组合拳kubectl logs和kubectl exec是进入Pod内部最常用的两个手段。logs直接拉取Pod内第一个容器的日志多个容器时要指定-c 容器名。exec则可以通过-it参数进入容器内交互式shell这相当于在架构层面直接打通了从外部到Pod内部的通道。我特别想提醒一点kubectl cp虽然可以把文件从Pod拷回本地但它依赖tar命令如果对端容器里没有tar就会直接失败。不管用哪个命令都要意识到“Pod是可能的容器是短暂的”这一架构特点。你打开容器的shell做临时排查没问题但别把配置直接改在容器里因为容器一旦被重建就啥都没了。正确做法是改Deployment或ConfigMap让Pod重建后自动带上新配置。6. 实战排查思路一个线上问题如何顺着架构找到根因讲了不少理论最后我想用一个我在实际项目中遇到的案例来收尾。这个例子能把前面聊的架构知识和排查命令完整串起来也让你感受一下“顺着架构找根因”和“瞎猫碰死耗子式排错”的差距。6.1 问题现象Service 不通但 Pod 都正常当时的情况是一个服务通过Service暴露但外部调用方反馈偶尔超时查看Pod状态全是Runningkubectl get pods看起来没有任何异常。很多人到这里会陷入死循环觉得一切都正常但问题就是存在。我当时的第一反应是别慌直接从流量链路去拆。链路大概是这样的外部请求 - Node节点上的kube-proxy规则 - 后端Pod。我依次检查三个环节先确认Service的TargetPort相对应Pod的端口确实在监听再用kubectl get endpoints确认Service背后的端点列表是否和Pod IP一致最后在节点上用iptables -t nat -L -n或ipvsadm -L -n看规则链是否存在。那个case的根因是后端Pod滚动更新时新Pod的readinessProbe还没通过旧Pod又已经被摘除导致端点列表短暂为空流量打到了空转发上。这个问题如果只看Pod状态可能几个小时都定位不到。6.2 分支排查DNS解析问题了怎么办另一个高频问题是Pod访问Service域名时时好时坏这通常是CoreDNS出问题。排查套路是先kubectl get pods -n kube-system | grep coredns看CoreDNS Pod状态再看CoreDNS Pod是否占用过多CPU或内存最后在故障Pod里用nslookup或dig测一下Service域名解析是否正常。如果Pod网络没问题但DNS解析超时很可能是节点上iptables规则把DNS流量打到了异常地址。这种场景下架构知识的价值就体现出来了。CoreDNS本质上也是一个Deployment它也有自己的Service和Endpoints。如果你不理解控制面和工作负载之间的信息同步机制你就很难想到“一个Pod能通则另一个Pod不能通”其实是CoreDNS的Pod被调度到了某个网络异常节点上或者CoreDNS的Service后端列表里有未曾Ready的Pod。6.3 给新手的建议从架构出发而不是从命令出发排障最忌讳的就是“随机敲命令”。我见过太多人在kubectl get events看不到明确信息后就不知道怎么办了。我的建议是每一次排障都从架构链路出发。请求从哪里来到哪个组件通过什么规则转发最终落在哪里中间涉及哪一层网络每一层可能用到什么命令去验证。你把这条链路想清楚很多时候还没敲命令就已经猜到了七八成。7. 关于K8s架构我的几点真实体会聊到这里该说的架构原理、实操步骤和排障思路都已经覆盖得比较全面了。最后再分享几个我在实际使用中的个人体会与其说是建议不如说是踩坑后的总结。第一个体会是K8s的架构文档再多都不如自己亲手搭一套集群来得深刻。别怕麻烦也别怕踩坑用kubeadm从零搭一个三节点集群再手动部署一个metrics-server和一个Ingress控制器你对控制面、节点面、网络面的理解会瞬间上几个台阶。网上有太多“一键部署”工具但那包装得越好的工具能让你学到的东西就越少。第二个体会是理解架构不等于理解你的业务运行在上面的方式。同一套K8s跑无状态Web服务和跑有状态数据库对架构的要求完全是两回事。无状态服务可以放心依赖Deployment滚动更新和HPA弹性伸缩有状态服务则要提前规划好StatefulSet、PVC、备份恢复链路甚至要考虑跨可用区调度。架构只是平台怎么用好这个平台才是长期价值的来源。第三个体会是别忽略版本带来的架构差异。K8s演进速度很快从kube-proxy的IPVS支持到CronJob的稳定从EndpointSlice到拓扑感知路由几乎每个版本都有架构层面的优化。保持读官方Release Notes的习惯远比在各种群里看碎片化经验更有价值。如果你正准备深入K8s就拿这篇文章当一张地图把这几个关键领域的文档挨个啃一遍再多动手搭几遍集群。等你能不看架构图就把控制面、节点面、Pod网络、调度器、控制器的协作流程完整讲给别人听时你就不再是那个“只会用kubectl”的人了而是真正站在K8s架构之上看问题的从业者。
返回列表