服务器运维

k8s架构详解:Master 与 Node 各组件的作用和交互

k8s架构分两部分:控制平面负责保存集群状态和做决策,工作节点负责把容器真正跑起来。所有组件都只和 kube-apiserver 通信,由它读写 etcd;看懂这条通信路径,就能理解创建一个 Pod 时各组件的先后顺序,也知道机房防火墙该放行哪些端口。

作者 顶联产品团队发布于 8 分钟阅读

k8s架构可以用一句话概括:一组控制平面节点负责记录状态和做决策,一组工作节点负责执行。控制平面(早期叫 Master)有四个核心组件:kube-apiserver、etcd、kube-scheduler、kube-controller-manager;工作节点(Node)上有三个:kubelet、kube-proxy 和容器运行时。这些组件彼此不直接对话,全部通过 kube-apiserver 读写集群状态,再各自监听(watch)自己关心的变化去动手。

k8s架构图:控制平面与工作节点

kubectl、CI 系统、监控 ──HTTPS 6443──► kube-apiserver

控制平面(Control Plane,旧称 Master)
├── kube-apiserver           唯一入口:认证 → 鉴权 → 准入 → 写入 etcd,并向其他组件推送变化
├── etcd                     分布式键值数据库,保存全部集群状态(2379 客户端,2380 成员间)
├── kube-scheduler           监听还没分配节点的 Pod,为它选一个节点
└── kube-controller-manager  运行几十个控制器,让实际状态不断向期望状态靠拢

        ▲ 6443:节点上的组件主动连接 apiserver,监听分给自己的任务
        │ 10250:apiserver 反向访问 kubelet,用于 logs、exec、port-forward

工作节点(Node)× N
├── kubelet                  节点代理:接收分到本节点的 Pod,调用容器运行时创建容器,上报状态
├── 容器运行时               containerd 或 CRI-O,通过 CRI 接口被 kubelet 调用
│   └── CNI 网络插件         创建 Pod 网络、分配 Pod IP(Calico、Flannel、Cilium 等)
├── kube-proxy               把 Service 翻译成本机的 iptables、IPVS 或 nftables 规则
└── Pod                      pause 容器持有网络命名空间,同一 Pod 里的业务容器共享它

读这张图抓住三点:

  • apiserver 是唯一的枢纽。scheduler、controller-manager、kubelet、kube-proxy 都是 apiserver 的客户端,只有 apiserver 直接读写 etcd。任何组件想知道“发生了什么”,都是向 apiserver 发起 watch。
  • 通信方向有两条。节点组件主动连 apiserver 的 6443;apiserver 也会反向连 kubelet 的 10250,kubectl logs、kubectl exec 就走这条路。防火墙只放了 6443 时,Pod 能正常运行,但看日志和进容器会失败。
  • 插件不在核心组件里,但几乎必装。CoreDNS 提供集群内的服务名解析,网络插件负责 Pod 之间互通,Ingress 控制器负责对外的 HTTP 入口,metrics-server 提供 kubectl top 和自动扩缩用的指标。

“Master”和“控制平面”指的是同一组组件。新版本里已统一改称 control-plane,节点角色标签和污点都写作 node-role.kubernetes.io/control-plane,老教程里的 master 标签不再使用。

控制平面四个组件分别做什么

组件 职责 是否保存状态 多实例时怎么工作
kube-apiserver 集群唯一的 API 入口。每个请求依次经过认证(证书、Token)、鉴权(通常是 RBAC)、准入控制(先改写、再校验),通过后写入 etcd 不保存,状态都在 etcd 多个实例同时工作,前面放负载均衡或虚拟 IP
etcd 分布式键值数据库,保存 Deployment、Pod、Service、Secret 等全部对象 保存,是集群里唯一必须备份的数据 Raft 多数派,写入要过半数成员确认
kube-scheduler 监听 spec.nodeName 为空的 Pod,先过滤掉资源不够、有污点、不满足亲和性的节点,再给剩下的节点打分,把得分最高的节点写回 Pod 不保存 主备模式,靠 Lease 对象选主,同一时刻只有一个在工作
kube-controller-manager 一个进程里运行多个控制器:Deployment、ReplicaSet、节点生命周期、Job、EndpointSlice、ServiceAccount 等,每个控制器循环比较期望状态与实际状态,发现差异就通过 apiserver 修正 不保存 主备模式,同样靠 Lease 选主

几个容易误解的地方:

  • scheduler 只负责“选节点”,不启动容器;它做的唯一写操作是把节点名绑定到 Pod 上,真正起容器的是那台节点上的 kubelet。
  • 控制器不直接操作服务器。ReplicaSet 控制器发现少了一个副本,只是在 apiserver 里新建一个 Pod 对象,后面的事交给 scheduler 和 kubelet。
  • 控制平面节点也运行 kubelet 和容器运行时。kubeadm 把这四个组件做成静态 Pod,清单放在 /etc/kubernetes/manifests/,由本机的 kubelet 直接拉起,不经过 scheduler。
  • cloud-controller-manager 只在云上需要。它负责对接云厂商的负载均衡、云盘和节点信息,自有机房的物理机集群一般不部署。

查看这些组件的状态:

# kubeadm 集群:控制平面组件的静态 Pod 清单
ls /etc/kubernetes/manifests/
# etcd.yaml  kube-apiserver.yaml  kube-controller-manager.yaml  kube-scheduler.yaml

kubectl -n kube-system get pods -o wide        # 各组件运行在哪个节点
kubectl get --raw='/readyz?verbose'            # apiserver 逐项自检,包含 etcd 连接
kubectl -n kube-system get lease kube-scheduler kube-controller-manager   # HOLDER 列是当前的主

kubectl get componentstatuses 从 1.19 起已标记为废弃,结果不可靠,检查控制平面用上面的 /readyz 和 Pod 状态。

工作节点上的三个组件

组件 运行方式 主要工作 排查入口
kubelet systemd 服务,不是 Pod 把本节点注册到集群;监听分配给本节点的 Pod;调用容器运行时创建容器、挂载存储卷;执行存活、就绪、启动探针;定期上报节点和 Pod 状态 journalctl -u kubelet -f
kube-proxy DaemonSet,每个节点一个 Pod 监听 Service 和 EndpointSlice,把“访问某个 ClusterIP”翻译成本机的转发规则 kubectl -n kube-system logs ds/kube-proxy
容器运行时 systemd 服务(containerd 或 CRI-O) 接收 kubelet 经 CRI 发来的指令:创建 Pod 沙箱、调用 CNI 插件配置网络、拉镜像、启停容器 crictl pods、crictl ps -a

补充三点:

  • 节点心跳靠 Lease。kubelet 默认每 10 秒更新一次 kube-node-lease 命名空间里属于自己的 Lease 对象,控制器据此判断节点是否还活着。
  • kube-proxy 不在数据路径上转发流量。iptables 模式下它只负责写规则,包由内核按规则直接转发,所以 kube-proxy 进程短暂退出时已有的 Service 照样能访问。
  • 每个 Pod 都有一个 pause 容器。它先创建并持有网络命名空间,CNI 插件给它分配 IP,业务容器随后加入这个命名空间,所以同一 Pod 里的容器可以用 localhost 互相访问。

创建一个 Pod 时,各组件按什么顺序工作

以执行 kubectl apply 创建一个 2 副本的 Deployment 为例,从命令发出到流量进入新 Pod,经过七步:

  1. apiserver 受理:kubectl 把清单发给 apiserver 的 6443 端口,依次通过认证、鉴权、准入,Deployment 对象写入 etcd。此时集群里还没有任何 Pod。
  2. Deployment 控制器:controller-manager 通过 watch 看到新的 Deployment,创建一个 ReplicaSet。
  3. ReplicaSet 控制器:发现这个 ReplicaSet 期望 2 个 Pod、实际 0 个,创建 2 个 Pod 对象,它们的 nodeName 为空,状态是 Pending。
  4. scheduler 选节点:看到 2 个未调度的 Pod,过滤、打分后选出节点,向 apiserver 写入绑定关系。
  5. kubelet 起容器:目标节点上的 kubelet 看到有 Pod 分给自己,调用容器运行时:先创建 pause 沙箱并由 CNI 插件分配 Pod IP,再拉取镜像,最后创建并启动业务容器。
  6. 就绪上报:kubelet 执行就绪探针,通过后把 Pod 标记为 Ready 并上报 apiserver。
  7. 接入流量:如果有 Service 的选择器匹配这些 Pod,EndpointSlice 控制器会把就绪 Pod 的 IP 作为可用端点写进 EndpointSlice,各节点的 kube-proxy 收到变化后更新转发规则,访问 Service 的请求开始分到新 Pod 上。

每一步都会在集群里留下可以观察的痕迹:

阶段 由谁完成 在哪里看
写入 Deployment apiserver、etcd kubectl get deploy 出现新对象
创建 ReplicaSet 和 Pod controller-manager Deployment 的事件 ScalingReplicaSet,ReplicaSet 的事件 SuccessfulCreate
选节点 scheduler Pod 事件 Scheduled;资源不够时是 FailedScheduling
配网络、拉镜像、起容器 kubelet、容器运行时、CNI Pod 状态 ContainerCreating;事件 Pulling、Pulled、Created、Started
就绪并接入流量 kubelet、EndpointSlice 控制器、kube-proxy READY 列变为 1/1;建了 Service 时,kubectl get endpointslices 里出现 Pod IP
kubectl create deployment web --image=nginx --replicas=2
kubectl get pods -l app=web -o wide -w                 # Pending → ContainerCreating → Running
kubectl get events --sort-by=.metadata.creationTimestamp | tail -n 20
kubectl describe pod -l app=web | sed -n '/Events:/,$p'

这套机制的好处是排障有章可循:Pod 卡在哪个阶段,就查负责那个阶段的组件。一直 Pending 且没有 Scheduled 事件,看 scheduler 和节点资源;长时间 ContainerCreating,看镜像拉取和网络插件;Running 但 READY 是 0/1,看就绪探针和应用本身。

组件故障时会出现什么现象

因为每个组件只对状态变化做出反应,某个组件停了,流程就停在它负责的那一步。按现象反推故障组件:

故障组件 现象 已在运行的业务
kube-apiserver 全部不可用 kubectl 报连接被拒绝或超时;扩缩容、发版、自愈全部停止 Pod 继续运行,已有的 Service 规则继续生效
etcd 失去多数成员 apiserver 无法写入,读请求也可能失败,集群相当于被冻结 同上
kube-scheduler 新 Pod 一直 Pending,事件里没有 Scheduled 不受影响
kube-controller-manager 改副本数不生效,删掉的 Pod 不会补,节点宕机后上面的 Pod 不会被驱逐重建 不受影响
某节点的 kubelet 该节点过一段宽限期后变为 NotReady;默认约 5 分钟后,由控制器管理的 Pod 在其他节点重建 该节点上的容器还在跑,但不再受管理
kube-proxy 新建或变更的 Service、端点不再同步到该节点的规则里 已有规则继续工作
CNI 插件 新 Pod 卡在 ContainerCreating,事件里是网络配置相关的报错 已分配 IP 的 Pod 通常不受影响
CoreDNS 按服务名访问失败,按 IP 访问正常 依赖域名解析的调用失败

这张表也说明了高可用该保护什么:apiserver 和 etcd 一停,整个集群就失去管理能力。kubeadm 的高可用方案有两种拓扑:堆叠 etcd(etcd 和其他控制平面组件跑在同一批节点上,至少 3 台)和外置 etcd(etcd 单独部署,至少 3 台 etcd 加 3 台控制平面,共 6 台)。中小规模用堆叠更省机器,代价是一台节点故障同时损失一个 etcd 成员和一个 apiserver 实例。

组件与端口对照表:机房防火墙与网络规划

下表按 Kubernetes 官方文档列出的端口整理,并补充了常见网络插件的端口:

端口 协议 组件 监听位置 谁来访问 放行建议
6443 TCP kube-apiserver 控制平面 所有节点、运维人员、负载均衡 只对节点网段和运维网段开放,不对公网开放
2379 TCP etcd 客户端接口 控制平面或独立 etcd 节点 kube-apiserver 只在控制平面之间放行
2380 TCP etcd 成员间通信 同上 其他 etcd 成员 只在 etcd 成员之间放行
10250 TCP kubelet API 所有节点 apiserver、metrics-server 只允许控制平面和集群内部访问
10259 TCP kube-scheduler 控制平面 本机 kubeadm 默认只监听 127.0.0.1,无需对外放行
10257 TCP kube-controller-manager 控制平面 本机 同上
10256 TCP kube-proxy 健康检查 所有节点 外部负载均衡 用外部负载均衡探测节点时放行
30000–32767 TCP、UDP NodePort 类型的 Service 所有节点 客户端 只开放实际用到的端口
179 TCP Calico BGP 所有节点 其他节点或接入交换机 使用 Calico BGP 模式时
IP 协议号 4 IPIP Calico IPIP 封装 所有节点 其他节点 这是协议号不是端口,ACL 要按协议放行
4789 UDP Calico VXLAN 所有节点 其他节点 使用 Calico VXLAN 模式时
8472 UDP Flannel、Cilium 的 VXLAN 所有节点 其他节点 使用对应插件时
4240 TCP Cilium 健康检查 所有节点 其他节点 使用 Cilium 时

老教程里常见的 kubelet 只读端口 10255,kubeadm 默认已经关闭,不要再打开。10250 尤其不能暴露到公网:拿到它的访问权限就能在节点上的容器里执行命令。

做网络规划时,除了端口还要注意三件事:

  • apiserver 用虚拟 IP 对外。3 台控制平面前面用 keepalived 加 HAProxy 提供一个虚拟 IP,节点和 kubectl 都连这个地址;这个地址要事先在机房的地址台账里预留,避免被分配给其他服务器。
  • etcd 怕延迟和慢盘。etcd 默认心跳间隔 100 毫秒、选举超时 1000 毫秒,网络抖动或磁盘写入慢都可能触发重新选主,表现为 apiserver 间歇性变慢。控制平面放在同一机房的低延迟网络里,etcd 数据目录放 SSD。
  • 封装会吃掉 MTU。VXLAN 封装多出 50 字节,物理网络 MTU 1500 时 Pod 网卡要设为 1450;IPIP 多出 20 字节,对应 1480。网络插件和物理网络的 MTU 不匹配时,常见症状是小请求正常、大文件或大响应卡住。交换机统一开了 9000 字节巨帧的,可以相应调大。

节点上开着 firewalld 时,控制平面可以这样按来源放行(示例中控制平面为 10.10.20.11–13,节点网段 10.10.20.0/24,Pod 网段 172.20.0.0/16):

firewall-cmd --permanent --new-ipset=k8s-cp --type=hash:ip
for ip in 10.10.20.11 10.10.20.12 10.10.20.13; do
  firewall-cmd --permanent --ipset=k8s-cp --add-entry=$ip
done
# etcd 与 kubelet 只允许控制平面访问
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source ipset="k8s-cp" port port="2379-2380" protocol="tcp" accept'
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source ipset="k8s-cp" port port="10250" protocol="tcp" accept'
# apiserver 允许整个节点网段访问
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.10.20.0/24" port port="6443" protocol="tcp" accept'
# Pod 网段放进信任区,否则跨节点的 Pod 流量可能被拦截
firewall-cmd --permanent --zone=trusted --add-source=172.20.0.0/16
firewall-cmd --reload

工作节点按同样思路放行 10250(来源为控制平面)、所用网络插件的端口和需要对外的 NodePort。

在机房里怎么落地

架构图上的高可用,要落到机柜和供电上才算数。3 台控制平面节点如果装在同一个机柜、接同一路 PDU,一次断电就会同时带走 3 个 etcd 成员,多数派设计形同虚设。上架前在 机柜与资产 的 U 位视图里确认 3 台控制平面分属不同机柜和供电回路,扩容时新节点按同样原则摆放。

节点出现 NotReady、SSH 又连不上时,问题往往出在操作系统层:内核崩溃、根分区写满或变成只读、网卡驱动异常。这时可以通过 IPMI 远程管理 打开节点的 VNC 控制台看屏幕输出,需要时远程重启或进入救援模式,确认是单节点故障后再决定修复还是把节点移出集群重装。

常见问题

控制平面节点上能跑业务 Pod 吗?

能。kubeadm 默认给控制平面节点加上 node-role.kubernetes.io/control-plane:NoSchedule 污点,普通 Pod 不会调度上去,去掉污点即可。小集群常这样节省机器,但业务把节点资源吃满时会拖慢 apiserver 和 etcd,生产环境要给控制平面组件预留资源,或者让控制平面只做控制平面。

etcd 为什么要部署奇数个?

etcd 用 Raft 协议,写入要超过半数成员确认。3 个成员能容忍坏 1 个,4 个成员同样只能容忍坏 1 个,多出的一个不增加容错,反而多一份同步开销,所以一般部署 3 个或 5 个。

kube-proxy 可以不装吗?

可以由网络插件接管。例如 Cilium 能用 eBPF 实现 Service 转发,开启替代模式后集群里不再需要 kube-proxy;用 kubeadm 部署时,init 时加 --skip-phases=addon/kube-proxy 跳过它。具体步骤以所用插件的文档为准。

k3s 的架构和标准 k8s 一样吗?

组件和职责一样,打包方式不同:k3s 把 apiserver、scheduler、controller-manager、kubelet 等打进一个二进制,以 server 和 agent 两种进程运行;单 server 时默认用 SQLite 存状态,高可用模式常用内置 etcd,也可以接外部数据库。apiserver 端口同样是 6443。

想看看在你的机房里怎么用?

从一次产品演示开始,梳理你的业务链路。

查看价格
服务热线 400-112-2951

让我们聊聊你的 IDC 业务

扫码添加企业微信,预约产品演示或申请试用。

Toplink 企业微信二维码

长按保存或使用企业微信 / 微信扫描

也可致电
400-112-2951
Telegram
@TopLink88