Kubernetes组件全解析:从控制面到工作节点
Kubernetes 这东西光看名字可能觉得离自己很远但只要是做后端、运维或者云原生相关工作的早晚要跟它打交道。我早年刚接触 K8s 的时候最头疼的就是面对一摞陌生的术语API Server、etcd、controller、scheduler、kubelet 这些词在文档里反复出现但没人告诉我它们到底各自在干嘛整个系统是怎么协作起来的。直到后来自己动手搭集群、排查问题、读源码才慢慢把这张拼图拼完整。这篇博文就从组件这个角度切入把控制面、工作节点、核心交互逻辑逐层拆开讲清楚适合刚入门 K8s、或者已经用过但总觉得概念模糊的读者目标是让你看完之后脑子里能有一个清晰的组件地图。1. 整体架构与控制面核心组件解析要理解 Kubernetes 组件首先要明白它的整体设计哲学。K8s 采用的是控制面与工作节点分离的主从架构控制面负责决策和状态管理工作节点负责实际运行容器。类比一下控制面像一个公司的管理层负责制定计划、审批任务、监控全局工作节点像一线执行团队只负责把手头的活儿干完定期向管理层汇报状态。1.1 控制面的总体职责与协作模型Kubernetes 控制面由几个组件共同构成这些组件各自独立运行又通过 API 互相协作。这里有一个很重要的设计理念所有组件之间的通信都是通过 API Server 进行的组件与组件之间不直接访问数据库、不直接调用对方接口全部走一层统一的门面。这种设计带来的好处是解耦任何组件被替换或升级时只要它还能跟 API Server 交互整个系统就不受影响。控制面最核心的职责可以概括为三件事存储和分发集群状态、响应外部请求并对资源进行编排、持续对比期望状态和实际状态并执行修正。这三件事分别对应着 etcd、API Server 加 Controller Manager、Scheduler 加 Controller Manager 的组合。听起来有点绕我建议你不要按文档顺序去死记而是按“数据从哪来、决策在哪做、动作由谁发出”来理解。先记住控制面组件清单etcd、kube-apiserver、kube-controller-manager、kube-scheduler另外还有一个选配的 cloud-controller-manager一般只有在云厂商环境里才会用到。我先逐个讲清楚每个组件是干什么的再把它们串起来。1.2 kube-apiserver所有请求的唯一入口kube-apiserver 是 Kubernetes 最重要的组件没有之一因为它是全系统唯一和 etcd 直接打交道的组件。所有其他组件、kubectl 命令行工具、外部客户端需要读写集群状态时都只能找它。它暴露的是 RESTful API也就是通过 HTTP 请求来操作各种资源对象比如 Pod、Deployment、Service、ConfigMap 等等。API Server 的具体工作可以分成这几块身份认证与鉴权每个请求必须携带证书或 TokenAPI Server 验证身份后再通过 RBAC 机制判断这个用户或组件有没有权限执行相应操作。校验与准入控制请求进入后先做合法性校验比如字段格式对不对、引用的资源是否存在然后经过 Admission Controller 准入控制这部分可以做一些策略化的干预比如自动为 Pod 注入 Sidecar、强制打标签、限制资源配额之类。状态持久化通过后API Server 把对象序列化写入 etcd同时负责监听 etcd 的变化把变更推送给所有关心这些资源的客户端。从工作负载角度来看API Server 是整个集群的数据中枢。比如你运行 kubectl apply -f nginx.yaml这条命令本质上就是向 API Server 发送一个创建 Deployment 的 POST 请求。API Server 接受后校验、写入 etcd然后通知所有相关组件这个新对象需要协调处理。也就是说你看到的“创建 Deployment”在底层其实就是对 API Server 的一次 HTTP 调用。实操中有几个容易踩坑的点。我见过不少人以为 Pod 跑不起来要查 kubelet 日志结果发现请求根本没到 kubelet 这层而是被 API Server 的准入控制拦截了。排查这类问题时第一件事就是看 API Server 日志它会把请求认证失败、鉴权拒绝、校验报错等信息都记录下来。另外API Server 的高可用部署通常用负载均衡器在前面挂多个实例注意它本身是有状态依赖的——依赖 etcd但 API Server 各实例之间是无状态的因为状态全在 etcd 里。1.3 etcd集群状态的唯一权威存储etcd 是一个分布式的键值存储数据库Kubernetes 用它保存所有资源对象的定义和状态。可以理解为整个集群的“脑皮层”所有记忆都存放在这里。API Server 是唯一能读写 etcd 的组件其它任何组件都不能直接访问它。这种设计大幅度降低了并发冲突和一致性问题因为对 etcd 的所有访问都收敛到一个组件。etcd 本身采用 Raft 一致性算法保证多个节点之间的数据强一致。生产环境通常部署三个或五个节点这样即使挂掉少数节点集群仍能正常读写不会丢数据。为什么是奇数个节点因为 Raft 算法需要多数派同意才能提交写入三个节点容忍一台故障五个节点容忍两台故障。关于 etcd 的实操我要强调两个点。第一是备份的重要性。etcd 里面存着集群的全部状态一旦数据损坏且没有备份整个集群的资源定义就全没了。常见的备份方式是定期执行 etcdctl snapshot save保存快照文件然后离线保存或放到对象存储。第二是性能监控。etcd 的性能直接影响 API 请求延迟如果集群规模很大很多小对象频繁更新etcd 压力会很大。日常关注 etcd 的磁盘 fsync 延迟、DB 大小、Leader 选举次数这几个指标能提前发现隐患。很多人容易把状态数据和应用数据搞混。etcd 里存的是 Deployment 的定义、Pod 的期望状态、Service 的 Endpoint 列表这类元数据不存业务日志、不存数据库内容。容器跑起来后的实际输出、文件写入都在节点本地磁盘或存储卷里etcd 不关心这些。搞清这个边界对理解系统架构非常重要。1.4 kube-scheduler决定 Pod 跑去哪台机器kube-scheduler 的核心任务是给新创建的 Pod 挑选一个最合适的节点。它盯着 API Server 的资源变化一旦发现有还没有被调度到节点的 Pod就按一套复杂的算法来选节点然后通过 API Server 把绑定关系写回集群。调度过程分为两个阶段过滤器加打分。过滤器阶段先把不满足条件的节点排除掉比如节点资源不足、有节点亲和性限制不满足、污点不兼容等。打分阶段则对剩余节点根据一系列优先级策略打分例如资源余量越均衡得分越高、符合 Pod 亲和性得分越高。最终得分最高的节点就被选为调度目标。这里分享一个常见误解很多人以为调度器把 Pod 调度到某台机器后Pod 就立刻在那台机器上运行了。实际上调度器只是做了一次决策把“这个 Pod 应该去 Node A”这一结论写入 etcd。真正让容器跑起来的是目标节点上的 kubelet它通过 API Server 看到有新的 Pod 被分配给自己才会去拉镜像、创建容器。如果你在运维中遇到 Pod 一直处于 Pending 状态大概率是调度器找不到合适的节点。常见原因包括节点资源不足、存在不匹配的污点与容忍、节点被打了调度不可用的标记。此时可以用 kubectl describe pod 查看调度失败的原因它会给出明确的错误信息比如 0/5 nodes are available: 5 Insufficient cpu。1.5 kube-controller-manager运维大脑与状态纠偏controller-manager 是一个运行着海量控制器的进程这些控制器关注的是一种叫“期望状态”的东西。Kubernetes 的核心哲学之一是声明式管理你告诉系统目标是什么系统自己去想办法达成。controller-manager 就是那个持续对比期望状态与实际状态、负责纠偏的执行引擎。常见的控制器包括 Deployment Controller、ReplicaSet Controller、StatefulSet Controller、Node Controller、Namespace Controller 等等。每个控制器都通过 API Server 监听自己关心的资源变化然后做出相应动作。举一个具体场景你创建了一个 Deployment声明要有三个副本。ReplicaSet Controller 看到当前副本数不足三个就调用 API 创建三个 Pod 对象之后如果某个 Pod 被误删控制器发现实际数变少又会立刻补一个新的。正是因为这套机制Kubernetes 才能实现不停机自愈。从实现角度讲控制器采用调谐循环的设计模式每轮循环从 API 获取期望状态和实际状态计算差异执行修正动作循环往复。这种模式的好处是鲁棒。即使某次修正动作失败下一轮循环还会再次尝试即使控制器重启了它也会在恢复后凭借 API Server 里的状态信息重新拉起全套任务。几乎所有业务里的“自动修复”需求都能在 Kubernetes 里用这种模式优雅实现这也是它最有价值的设计思想之一。2. 工作节点组件与容器运行机制控制面负责决策真正让容器跑起来的是工作节点。每个工作节点上都运行着一组固定组件kubelet、kube-proxy、容器运行时通常为 containerd。这些组件的协作模式和控制面有所不同它们要处理的是具体节点上的事务比如拉取镜像、启动容器、配置网络规则、上报状态。2.1 kubelet节点上的管家kubelet 是每个节点上最重要的代理组件负责管理本节点上所有 Pod 的生命周期。它持续监听 API Server看有没有新 Pod 被调度到本节点一旦发现就负责去跟容器运行时交互把容器拉起来。它同时还要定期向 API Server 上报本节点的状态包括资源使用情况、节点的 Ready/NotReady 条件等。kubelet 的工作可以拆成几步从 API Server 获取分配到本节点的 Pod 清单从镜像仓库拉取镜像调用容器运行时创建容器持续监控容器的健康状态在容器异常退出时根据重启策略决定是否重启。它还负责执行探针Probe检查比如 liveness probe 检测容器是否还活着readiness probe 检测它是否已经可以接收流量。某一项探针失败kubelet 就会采取相应措施要么重启容器要么从 Service 后端列表里摘除。实操中kubelet 是让人又爱又恨的组件。它出问题时症状往往很直观比如 Pod 卡在 ContainerCreating、无法启动、反复 CrashLoopBackOff。这时候不要急先看 kubelet 日志通常在 /var/log 目录或通过 journalctl -u kubelet 查看。日志里会直接告诉你镜像拉不下来的原因、容器启动失败的原因、健康检查失败的原因。再强调一个细节kubelet 与 API Server 的通信中节点证书需要更新。证书过期是生产环境中比较常见的事故原因之一会导致 kubelet 无法上报状态节点在控制面那边被标记为 NotReady。如果不对接自动证书续期流程务必记好证书到期时间提前规划轮换流程。我见过不止一次因为证书过期导致整批节点从集群里消失排查起来会让人非常焦灼。2.2 容器运行时真正启动容器的地方容器运行时是实际执行容器创建和生命周期管理的组件Kubernetes 通过 CRI 标准接口与它交互。当前主力运行时是 containerd更早的时候常见的是 docker但 K8s 从 1.24 版本开始就移除了对 Docker 直接作为运行时支持的代码推荐的底层运行时就是 containerd或者其它符合 CRI 标准的运行时比如 CRI-O。作为用户大多数时候不需要直接跟容器运行时打交道但了解它有几个好处。第一排查问题时crictl 工具比 docker 命令更通用它可以统一查看 containerd 管理的 Pod 和容器包括容器状态、日志、镜像列表。第二当容器启动异常时可以绕过 kubelet 直接用运行时工具手动创建一下容器帮助判断问题是出在 kubelet 配置还是镜像本身有问题。运行时的选择会直接影响节点资源占用和稳定性。在 Docker 时代容器由 docker-engine 加 containerd 加 dockershim 三层协作移除了 docker 之后链路缩短为 kubelet 加 containerd整体更简洁资源开销也更低。社区往这个方向走本质上是想减薄抽象层让系统更容易排查和优化。需要留个心眼的是镜像仓库的拉取策略和认证。如果节点无法从镜像仓库拉取镜像最典型的原因是网络不通、镜像不存在、私有仓库凭证缺失或过期。这些问题在 kubelet 日志里都有明确提示多花一分钟读日志往往比胡乱重启节点更高效。2.3 kube-proxy流量转发与负载均衡的关键实现kube-proxy 负责处理集群内部的网络代理和负载均衡它保证了 Service 的虚拟 IP 能够被正确转发到后端 Pod。注意它并不是传统的代理进程而是通过设置内核规则来实现流量转发最常用的模式是 iptables 和 IPVS。具体原理是这样的当你创建一个 Service并配置了 Selector 选中若干 PodAPI Server 会不停更新 EndpointSlice 对象记录这些后端 Pod 的 IP 列表。kube-proxy 监听 Service 和 EndpointSlice 的变化把规则写入本机内核。当一个客户端访问 Service 的 ClusterIP 时流量经过内核规则被 DNAT 到某一个后端 Pod。那它跟 kubelet 有什么区别kubelet 负责的是 Pod 和容器的运行kube-proxy 负责的是网络转发规则。两者是两个维度的事情即使某个节点上没有运行任何业务 Pod只有 kube-proxy 在Service 的转发规则依然会在必要时建立。实际运维中用 IPVS 模式通常能获得更好的性能和更灵活的调度算法比如加权轮询、最少连接等但配置比较复杂。默认 iptables 模式对大多数中小集群都够用性能瓶颈通常集中在规则数量极大时。如果集群规模非常大或者流量模式复杂再评估切换到 IPVS。想验证 kube-proxy 是否正常可以在一台节点上访问 ClusterIP再配合 tcpdump 抓包来验证流量是否被正确转发。2.4 组件的颗粒度与跨节点协作示例为了把这些组件真正串起来这里模拟一次完整的应用发布流程用户执行 kubectl create deployment nginx --imagenginx:latest。整个过程的推进顺序大致如下kubectl 命令把创建请求发送到 API Server。API Server 校验请求后将 Deployment 对象写入 etcd。Deployment Controller 监测到新的 Deployment发现它需要三个副本于是创建 ReplicaSet 对象。ReplicaSet Controller 随后发现当前 Pod 数为零于是创建三个 Pod 对象。Scheduler 监测这三个未调度的 Pod逐一挑选合适的节点将绑定关系写回 API Server。目标节点的 kubelet 发现新 Pod 被分配到自己拉取 nginx 镜像通过 containerd 启动容器。kubelet 持续上报 Pod 状态最终 API Server 记录的所有 Pod 变成 Running。每一步看起来都不复杂但正是组件间的这种高度专一和清晰分工让 Kubernetes 能够支撑大规模集群的自动化管理。如果你把前面每个组件的作用再对照一遍会发现整个过程确实没有一个组件负责面面俱到每个组件只管自己那一亩三分地。3. 通用关键机制与组件间通信原理组件之间不是凭空自动协作的它们依赖一套完整的 API 与事件机制。了解这套机制的底层逻辑能让你在排查故障时站得更高不只是盯着某个组件看而是能把故障现象投射到整条协作链路里。3.1 声明式 API 与最终一致性的设计取舍Kubernetes API 是声明式的这意味着你提交的是一个期望状态而不是一组待办命令。比如你写一份 Deployment YAML声明我要三个副本系统自己去决定先创建 ReplicaSet再创建三个 Pod。这比命令式操作更复杂但它带来了一个巨大的优势自动纠偏。系统不会因为某一步中断就停摆它会在下一次循环中重新对比期望和实际状态自己补上没做完的工作。这种思路的灵感来自于控制理论里的闭环反馈实际效果也像一个恒温器温度低了就加热高了就停止加热永不休止。你不需要告诉恒温器下一步干什么只需要设定好目标温度。这个比喻套到 Kubernetes 上非常贴切。这也是为什么 K8s 能非常适合做弹性伸缩、自愈、滚动更新这类自动化能力因为它的架构天然允许以状态差为驱动来执行一切操作。理解这个思想后你再去看 Deployment、Service、HPA 这些资源对象就不会觉得它们是孤立的配置数据而是一整套期望状态 调谐回路的实现。3.2 控制器模式从状态差到自愈能力控制器模式不仅仅是 controller-manager 里的实现方式它几乎贯穿了 K8s 的所有核心抽象。一个控制器循环通常有四个步骤从 API Server 获取期望状态比如 ReplicaSet 期望三个副本。获取实际状态比如当前运行的 Pod 列表。计算两个状态的差异。调用 API Server 执行修正动作比如创建缺失的 Pod 或清理多余的 Pod。这个模式在 Node 层面也一样存在。Node Controller 会定期检查节点的健康状态如果发现节点失联超过阈值就会把节点上的 Pod 标记为缺失并触发重建到其它节点。这种机制意味着故障恢复未必需要人工介入系统自己就能做大量自愈工作。但在使用这个能力时有个前提值得留意对于有状态应用比如数据库或队列服务自愈重建并不等于零丢失。控制器确实能创建新 Pod但新实例的数据恢复通常需要依赖持久化存储和备份。如果完全不考虑数据复制节点宕机后看似 Pod 重建了底层数据可能还只存在于旧节点上这个坑对新手是很有迷惑性的。3.3 API 对象的字段、Group 与版本演化接触 K8s 时经常看到 apiVersion: apps/v1 这样的字段。这个字段表示的是 API 资源的 API 组和版本。Kubernetes 把资源分为不同 Group例如 apps 组包含 Deployment、StatefulSet 等core 组包含 Service、Pod 等。版本则用来表示资源结构的演进比如 v1beta1 是测试版结构v1 是稳定版。理解 Group 和 Version 的实际意义在于升级兼容性。旧集群上的资源可能使用旧版本 API升级 K8s 后某些 API 版本会被弃用对应的 YAML 清单可能需要改写新版本字段。提前检查资源清单中使用的 apiVersion 可以避免升级事故。我建议在集群升级前用 kubectl convert 工具结合文档检查所有资源清单千万别只盯着工作负载Service、Ingress、CRD 这些都可能是升级引爆点。另一个复杂的点是 CRD。CRD 允许你自定义新类型的 API 资源让 K8s 能管理你自己的领域对象。比如 Prometheus Operator 就通过 CRD 定义了 Prometheus 和 ServiceMonitor 这类资源。理解 CRD 和控制器模式之后你可以把任何期望状态能被代码调谐的对象都接入 Kubernetes。这也是为什么 K8s 能从一个容器编排器长成一套声明式基础设施操作系统的核心原因。4. 组件层面的故障实战排查手记理论讲完了下面进入实战。基于我多次的排查经历把组件层面最常见的故障现象、排查路径和解决手段整理成列表方便按图索骥。请注意排查的第一步一定是先确认现象然后再定位组件而不是在多个组件日志里打转。4.1 Pod 一直 Pending先问调度器现象是 Pod 长时间停留在 Pending查 API Server 没有报错节点状态也正常。这种问题大概率出在调度层。当前首要排查动作是 kubectl describe pod 输出的 Events 字段会显示调度失败原因。常见原因可以归纳为三类节点资源不足CPU、内存、GPU 等任何要求超过节点可用量。优先看节点状态里 Allocated resources区分 Request 与 Limit 的差异。调度约束不满足节点亲和性、污点与容忍条件不匹配。需要检查 Pod 的 nodeSelector、affinity、tolerations 配置。节点被标记不可调度节点上有 node.kubernetes.io/unschedulable 污点或者没有 Ready 条件。用 kubectl get nodes 查看节点状态与污点即可。需要注意的一个细节是调度失败的信息不会出现在 kubelet 日志里因为请求还没到节点。如果你一直盯着 kubelet 找原因方向就错了。4.2 容器反复 CrashLoopBackOff分清退出码CrashLoopBackOff 表示容器启动后反复崩溃。这个状态之所以让人迷惑是因为表面看 Pod 是存在的但容器却顽固地退出。排查重点是容器退出码和日志。先用 kubectl logs 查看容器日志如果没有日志用 kubectl describe pod 看 State 和 Last State 里的 Exit Code。常见退出码对应的问题包括退出码 0容器主动退出可能是任务类运行完就退出但 Deployment 期望它常驻需要调整启动命令让它保持前台运行。退出码 1一般是应用启动时检查失败比如配置读取错误、端口绑定失败、依赖服务没就绪。退出码 137代表被强制 SIGKILL 杀死最常见原因是容器超过内存 Limit 触发了 OOM查看 Last State 里的 OOMKilled 字段。退出码 143容器收到 SIGTERM常见于手动停止或滚动更新时优雅退出超时。在广东话有句话叫痛过才知死这些退出码背后的意义都是我在一次次容器崩溃里练出来的。生产环境里遇到 CrashLoopBackOff 时还要检查探针是否误判。有时容器进程没问题但 readinessProbe 检测路径不对kubelet 会一直不把容器标记为就绪导致它无限重启。4.3 节点显示 NotReady检查 kubelet 与运行时健康状态节点 NotReady 意味着 kubelet 无法正常上报状态给控制面。节点层面的问题往往影响面广可能导致所有业务 Pod 迁移或被重建需要在集群层面规划好容量冗余。排查顺序建议如下登录节点执行 systemctl status kubelet 查看 kubelet 服务的运行状态。查看 kubelet 日志里的错误信息常见的有证书过期、kubelet 无法连接 API Server、容器运行时连接失败。检查容器运行时状态比如 crictl info 是否正常返回。检查节点资源磁盘空间不足、inode 耗尽、内存不足都可能让 kubelet 变得不健康kubelet 会主动将该节点标记为 NotReady 以保护集群。一个容易被忽略但高频的坑是磁盘压力过大。kubelet 检查到节点磁盘使用率超过阈值默认 85%后会开始驱逐节点上的部分 Pod并可能将节点置为压力状态。如果你发现节点 NotReady 的同时还在大量清理镜像这大概率不是网络问题而是磁盘空间问题。4.4 组件日志查看快捷手册为了快速定位问题我整理这套组件日志查看速查表建议收藏到自己的运维手册里组件日志位置 / 查看方式常见排查场景API Server静态 Pod 方式kubectl logs -n kube-system kube-apiserver-请求被拒绝、鉴权失败、准入控制报错Controller Managerkubectl logs -n kube-system kube-controller-manager-Deployment、ReplicaSet 不按期望创建或删除Schedulerkubectl logs -n kube-system kube-scheduler-调度失败、Pending 状态、调度策略不生效etcd依赖部署方式默认通过 etcdctl 配合日志数据读写异常、长时间延迟、快照失败kubeletjournalctl -u kubelet -f容器启动失败、节点 NotReady、健康检查报错容器运行时crictl logs、crictl ps容器拉取仿真失败、运行状态异常4.5 一次典型故障复盘镜像拉取失败引发的连锁问题最后分享一个我实际处理过的案例。某次生产环境发布新版应用后部分 Pod 卡在 ContainerCreating并且集中在某几个节点。最初怀疑是镜像仓库不稳定但单独在这几个节点上手动 crictl pull 却成功。这就有点诡异了。检查 kubelet 日志后发现kubelet 在反复重试拉取但报错却是image pull deadline exceeded。手动能成功但 kubelet 失败说明问题不是仓库而是 kubelet 与运行时之间的镜像缓存同步。后来发现这几个节点的根分区磁盘空间不足导致 containerd 在拉取镜像后写入镜像层时超时。手动 pull 时镜像已经被缓存所以检查不出问题。清理掉这些节点上的无用镜像和日志文件后Pod 陆续恢复。这个案例想说明的是K8s 的故障往往不是孤立的一个磁盘告警可能引发一连串看起来像网络问题、仓库问题、调度问题的假象。排查时千万别只看表面症状要顺着链路往下多摸一层很多问题最终都指向资源层比如磁盘、内存、文件描述符。5. 从组件到集群给新手的几条实操建议组件讲完了最后说点经验层面的东西。如果你是刚开始搭建和运维 K8s这里有三条建议我觉得最值得先刻在脑子里。第一不要在组件多而复杂时想着一步到位先跑通最小集群再说。所谓最小集群最朴素的理解就是一台控制面加至少一台工作节点。你可以在物理机、虚拟机里手动部署也可以直接用 kubeadm 初始化。这个过程中你会亲手看到 API Server、etcd、controller-manager、scheduler、kubelet 这些进程被拉起来的顺序和配置方式。比自己幻想只要敲几行命令就会神奇地跑起来要踏实得多。第二各种全家桶工具确实很方便但不能替代你对每个组件的认识。只用一键脚本或托管服务的人遇到问题往往只能干瞪眼。真正把集群从零搭一次你才会理解证书该怎么签发、清单里每个配置的作用是什么、某个组件崩溃时怎么自救。等你能在裸集群上完成一次应用发布、一次节点宕机恢复演练再回过去用工具类产品会觉得底气和判断力完全不同。第三推荐去试着读一读《深入理解 Kubernetes 源码》这类材料但不建议一上来就硬啃。更可行的路径是先跑通基本操作再在遇到具体故障时针对性地翻阅某个组件的实现。比如你好奇 scheduler 为什么选这台节点不选另一台就去读它的调度策略源码。带着问题去读效率远高于逐页阅读。源码没有想象中那么可怕核心流程代码往往都很清晰读懂了里面提到的 interfaces、workqueue、informer 这些概念对整个体系的掌握会进一大步。组件是整个 K8s 的细胞。每个组件职责清晰、接口固定、数据流动有序这样的架构才撑得起复杂的生产环境。我第一次手动搭集群时看着 kubeadm init 一行行吐日志才真正意识到这些组件之间环环相扣的关系。希望你也能在这个探索过程里找到乐趣——哪怕中途被各种奇怪故障折腾到怀疑人生等集群终于稳定运行的那一刻前面所有折腾都会变成你最珍贵的经验。