写在前面
承接篇9 组件。本篇把那些组件串起来,演示两条最核心的调用链路:
- 管理面:一次
kubectl apply 怎么把一个 Deployment 跑起来(组件协作部署) - 数据面:一次外部 HTTP 请求怎么从公网打到 Pod(流量怎么进来)
读完这两条,“apiserver / etcd / controller / scheduler / kubelet / kube-proxy / CNI / CoreDNS / Ingress 这些到底怎么配合"就彻底通了——排查问题也知道该看哪个环节。
一、两条链路总览
1
2
3
4
5
6
7
8
9
10
11
| 管理面(控制流)—— 部署一个 Pod
你 → kubectl → apiserver → etcd → controller-manager → scheduler
→ kubelet → runtime → kube-proxy → Pod Running
这是"声明 → 调谐 → 执行"链路,组件靠 watch apiserver 协作
数据面(数据流)—— 一次外部请求
用户 → 公网 DNS → LoadBalancer → Ingress Controller → Service
→ kube-proxy 规则(iptables/ipvs DNAT) → Pod IP(CNI 转发)→ container
这是"流量 → 路由 → 转发 → 到达"链路,靠网络组件 + 内核规则
|
两条链路分开看,但共享组件(apiserver 是管理面枢纽,kube-proxy / CNI 是数据面枢纽)。
二、管理面:kubectl apply 全流程
场景:kubectl apply -f deploy.yaml(一个 replicas: 3 的 Deployment)。
步骤拆解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| ① kubectl apply
kubectl 读 YAML,发 HTTPS POST/PUT 到 apiserver
附上 kubeconfig 里的证书/Token(认证)
② apiserver 收请求
认证(你是谁)→ 授权(RBAC:你能创建 Deployment 吗)→ 准入(mutating/validating webhook)
校验 YAML schema,乐观锁写入 etcd
返回成功给 kubectl
③ etcd 存储
Deployment 对象持久化(期望状态:replicas=3,image=nginx)
此时还没有任何 Pod —— 只是"账本上记了要 3 个"
④ Deployment Controller(在 controller-manager 里)watch 到
它 watch apiserver 的 Deployment 资源
看到新 Deployment → 创建一个 ReplicaSet(版本 v1)
⑤ ReplicaSet Controller watch 到
看到新 RS 期望 3 副本,但当前 0 → 创建 3 个 Pod 对象(Pod 还没绑定节点)
⑥ kube-scheduler watch 到未调度的 Pod
对每个 Pod:过滤可用节点 → 打分 → 选定 → 写 Pod.spec.nodeName
Pod 现在"知道"自己该跑在哪个节点
⑦ 目标节点的 kubelet watch 到分给自己的 Pod
kubelet 一直 watch apiserver:"有没有分给我的新 Pod?"
收到 → 调 CRI(containerd)拉镜像、起容器
挂载 volume、跑 liveness/readiness probe
⑧ kubelet 上报状态
Pod Running、容器就绪 → 写回 apiserver → etcd 更新实际状态
ReplicaSet Controller 看到实际=期望,停止创建
⑨ kube-proxy watch 到 Service 变化(如果有)
新 Pod 的 Endpoints 变了 → 在每个节点更新 iptables/ipvs 规则
现在 Service 的 ClusterIP 能路由到这些 Pod
⑩ 用户 kubectl get pods 看到 Running
—— 链路闭合
|
关键认知:watch + reconcile
1
2
3
4
5
6
| 组件间不互相直连:
kubectl 不通知 controller,controller 不通知 scheduler
全靠"watch apiserver"——apiserver/etcd 是唯一的消息总线
每个组件被动等待自己关心的资源变化,各自响应
这就是 K8s 的"事件驱动 + 声明式"架构
|
排查 Pod 没起来:① kubectl get 卡在 Pending → scheduler 问题(资源 / 亲和);② ImagePullBackOff → kubelet 拉镜像失败;③ CrashLoopBackOff → kubelet 起了但容器崩(应用问题)。每个阶段对应不同组件,kubectl describe pod 的 Events 串起链路。
三、数据面:外部请求进 K8s 全流程
场景:用户访问 http://app.example.com,集群里有个 Ingress 把它路由到 web Service(后面 3 个 Pod)。
步骤拆解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| ① 用户浏览器:http://app.example.com
② 公网 DNS 解析 app.example.com → 一个公网 IP
这个 IP 是云 LoadBalancer / Ingress Controller 的对外入口
③ 流量到云 LoadBalancer(type=LoadBalancer 的 Service,由 CCM 创建)
LB 把流量转到某节点的 NodePort(Ingress Controller 通过它暴露)
④ Ingress Controller(一个 Pod,通常是 NGINX)收到请求
读 Host: app.example.com,查 Ingress 规则
匹配到 → 找到目标 Service(web)
⑤ Ingress Controller 查 Service 的 Endpoints
Endpoints = web Service 选中的 Pod IP 列表(由 Endpoint Controller 维护)
Ingress Controller 选一个 Pod IP(负载均衡)
⑥ Ingress Controller 直连 Pod IP
不经过 Service 的 ClusterIP / kube-proxy!
(Ingress Controller 自己读 Endpoints 直接路由到 Pod)
跨节点:CNI 负责路由(overlay/VXLAN 或 BGP,把 Pod IP 路由到目标节点)
⑦ 流量到目标节点的 Pod 网络命名空间 → container 端口
container 处理请求,返回响应(原路或直接出网)
|
关键认知:ClusterIP 是虚拟的,Ingress 直连 Pod
1
2
3
4
5
| 反直觉点:Ingress Controller 不"经过 Service ClusterIP"
- ClusterIP 是虚拟的(没有网卡),靠 kube-proxy 的 iptables 规则 DNAT
- Ingress Controller 每个请求都走 DNAT 路径会很慢 + 不灵活
- 所以它直接 watch Endpoints,拿 Pod IP 列表,自己负载均衡,直连 Pod
- 这也是为什么删 Pod 时 Ingress Controller 要 watch Endpoints 更新,避免发到已死的 Pod
|
四、对比:集群内 Pod 访问 Service(走 kube-proxy)
外部请求是"经 Ingress Controller 直连 Pod”。集群内 Pod 访问 Service 名则是"走 kube-proxy 规则"。两条不一样:
1
2
3
4
5
6
7
8
9
10
| 集群内 Pod → http://web-app(Service 名)
① CoreDNS 把 web-app 解析成 Service 的 ClusterIP(如 10.96.0.10)
② Pod 发包到 10.96.0.10:80
③ 包到达本节点内核 → 命中 kube-proxy 写的 iptables/ipvs 规则
④ 规则把目的地址 DNAT:10.96.0.10 → 某个真实 Pod IP(如 10.244.1.5)
⑤ CNI 把包路由到 Pod(本节点直连;跨节点靠 overlay/BGP)
⑥ Pod 收到
关键:ClusterIP 是"虚拟入口",真正干活的还是 Pod IP
kube-proxy 的规则就是"虚拟入口 → 真实 Pod"的映射
|
1
2
3
| 两种访问路径对比:
外部 → Ingress Controller → 直连 Pod IP(绕过 ClusterIP)
内部 → Service 名 → CoreDNS → ClusterIP → kube-proxy DNAT → Pod IP
|
五、组件在两条链路里的角色
1
2
3
4
5
6
7
8
9
| 管理面(apply)的主力:
apiserver(枢纽)/ etcd(账本)/ controller-manager(reconcile)
/ scheduler(调度)/ kubelet(执行)
数据面(请求)的主力:
CoreDNS(名字解析)/ Ingress Controller(七层路由)
/ Endpoints(Pod 列表)/ kube-proxy(DNAT 规则)/ CNI(跨节点路由)
共享:apiserver 是管理面枢纽;kube-proxy/CNI 是数据面枢纽
|
六、按故障现象定位环节
1
2
3
4
5
6
7
8
9
10
| 现象 卡在哪 查
─────────────────────────────────────────────────────────────
kubectl apply 后 Pod 不出现 controller/调度 kubectl get events
Pod Pending scheduler kubectl describe pod(FailedScheduling)
ImagePullBackOff kubelet/runtime 镜像名/仓库权限
CrashLoopBackOff 应用 kubectl logs
Service 名解析失败 CoreDNS kubectl get pods -n kube-system(coredns)
Service ClusterIP 不通 kube-proxy/CNI iptables-save / 跨节点 ping Pod IP
Ingress 503/502 Ingress Controller kubectl describe ingress + controller 日志
外部访问超时 LB/Ingress 入口 LB 健康检查、NodePort、安全组
|
七、小结
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| 管理面(kubectl apply):
apply → apiserver → etcd → controller 创建 RS/Pod
→ scheduler 选节点 → kubelet 起容器 → kube-proxy 更新规则
组件靠 watch apiserver 协作,不直连
数据面(外部请求):
公网 → DNS → LB → Ingress Controller(查 Ingress+Endpoints)
→ 直连 Pod IP(CNI 跨节点路由)
ClusterIP 是虚拟的,靠 kube-proxy 的 iptables/ipvs DNAT 实现
核心认知:
✓ apiserver/etcd 是管理面总线,组件 watch 它协作
✓ Service ClusterIP 虚拟,流量靠 kube-proxy 规则 DNAT 到 Pod
✓ Ingress Controller 直连 Pod IP,不走 ClusterIP
✓ CNI 管 Pod IP 分配和跨节点路由
✓ 故障按"管理面 vs 数据面"先分流,再定位具体组件
|
至此 K8s 学习笔记 10 篇走完:资源怎么用(1-8)+ 组件是什么(9)+ 组件怎么协作(10)。配上本站「云托管 K8s 实战」5 篇(云上落地),K8s 从原理到生产就齐了。