K8s Lab 把当前仓库文档整理成一个可阅读的网页站点

Repository Reading Site

本轮操作记录:Kubernetes 网络原理、协议对比与调试实验

用户明确提出: 所以这轮工作不只是“继续讲 K8s 对象”,而是要把: 全部统一到一个教学框架里。 为了做到这一点,我先做了现实环境调查,再做 Pod/Service/DNS 实验,最后才写教学文档。 --- 因为你要讲 Kubernetes Service 网络,先得知道: 如果你不知道当前模式是: 那后面的原理解释就会空。 `config.conf` 里

Markdown04-操作记录-Kubernetes网络原理与调试实验.md2026年4月9日 17:59

本轮操作记录:Kubernetes 网络原理、协议对比与调试实验

本轮目标

用户明确提出:

  • 网络要仔细讲
  • 想理解各种网络协议的原理和区别
  • 想理解几层网络
  • 想知道 VXLAN 等协议的使用场景和限制
  • 想拿到调试命令

所以这轮工作不只是“继续讲 K8s 对象”,而是要把:

  • 网络基础
  • Kubernetes Service 网络
  • CNI overlay
  • 节点 underlay
  • 调试方法

全部统一到一个教学框架里。

为了做到这一点,我先做了现实环境调查,再做 Pod/Service/DNS 实验,最后才写教学文档。


Step 1: 先确认 kube-proxy 用什么模式

实际命令

KUBECONFIG=~/.kube/config-k8s-lab kubectl -n kube-system get cm kube-proxy -o yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n kube-system logs ds/kube-proxy --tail=200 | rg 'Using iptables Proxier|Using nftables Proxier|Using ipvs Proxier|proxier'

为什么先查 kube-proxy

因为你要讲 Kubernetes Service 网络,先得知道:

  • 当前 Service VIP 是靠什么实现的

如果你不知道当前模式是:

  • iptables
  • ipvs
  • nftables

那后面的原理解释就会空。

从 ConfigMap 里读到了什么

config.confmode: ""

这个值本身不够直观,所以我又去看日志。

从日志里读到了什么

我看到了:

Using iptables Proxier

我得到的结论

当前集群的 Service 转发路径是:

  • kube-proxy 写 iptables nat 规则
  • ClusterIP 流量被 DNAT 到真实 Pod IP

这条结论后来被我用于解释 ClusterIP 机制。


Step 2: 先确认 Calico 是 VXLAN 还是 IPIP

实际命令

KUBECONFIG=~/.kube/config-k8s-lab kubectl -n kube-system get ds calico-node -o yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl get ippools.crd.projectcalico.org -A -o yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n kube-system get cm calico-config -o yaml

为什么这三条都查

因为我不想只依赖一个地方下结论。

calico-node DaemonSet

可以看到运行时环境变量,比如:

  • CALICO_IPV4POOL_IPIP
  • CALICO_IPV4POOL_VXLAN

IPPool CRD

可以看到实际网络池定义,比如:

  • cidr
  • ipipMode
  • vxlanMode
  • natOutgoing

calico-config ConfigMap

可以看到:

  • 后端类型
  • CNI 配置模板
  • MTU 配置

我读到的关键信息

calico-node

我看到:

  • CALICO_IPV4POOL_IPIP=Always
  • CALICO_IPV4POOL_VXLAN=Never
  • CLUSTER_TYPE=k8s,bgp

在 IPPool 里

我看到:

cidr: 10.244.0.0/16
ipipMode: Always
vxlanMode: Never
natOutgoing: true

calico-config

我看到:

  • calico_backend: bird
  • veth_mtu: "0"

我得到的结论

当前集群:

  • Pod 网段是 10.244.0.0/16
  • Calico 使用 BIRD/BGP 参与路由
  • Pod 跨节点 overlay 是 IPIP
  • 不是 VXLAN
  • 开启了出站 NAT

这为后面讲:

  • VXLAN 和 IPIP 区别
  • BGP 的角色
  • NAT 的意义

提供了现实基础。


Step 3: 去节点上看接口和路由,而不是只信 K8s 对象

实际命令

我分别登录了:

  • 107.148.164.118us590068728056
  • 38.76.221.17hk652699382121

执行了:

ip -br a
ip route
ip -d link show type vxlan

另外在 107.148.164.118 上还执行了:

iptables-save -t nat | grep ...
ip route get 10.244.147.106
ip route get 10.10.0.5

为什么先看 Linux 命令,而不是继续看 kubectl

因为 Service、隧道、路由最终都落在 Linux 内核和网络栈上。

Kubernetes 对象只是上层声明。

真正的转发表、接口、NAT 规则,最后都在宿主机。

ip -br a 为什么很适合教学

因为它比传统 ip addr 更紧凑。

你可以很快看到:

  • 哪些接口存在
  • 哪些接口是 UP
  • 每个接口挂了什么地址

我看到了什么

在节点 us590068728056 上看到了:

  • ens17
  • wg0
  • tunl0
  • 一堆 cali*

在节点 hk652699382121 上也看到了:

  • wg0
  • tunl0
  • cali*

为什么这些接口很关键

wg0

说明 WireGuard 节点网络存在。

tunl0

说明 IPIP 隧道存在。

cali*

说明 Calico 为 Pod 端点创建了很多 veth 对端接口。

没有 vxlan 设备

这是反向证据,说明当前不是 VXLAN 模式。


Step 4: 读路由表,确认跨节点 Pod 流量怎么走

实际命令

ip route
ip route get 10.244.147.106
ip route get 10.10.0.5

为什么 ip route get 特别重要

很多人只会看完整路由表,但不会用 ip route get

这条命令的好处是:

  • 你直接问内核:“如果我要发往这个目的地址,你到底怎么走?”

它比人肉扫整张路由表高效得多。

我得到的关键结果

us590068728056 上:

10.244.147.106 via 10.10.0.5 dev tunl0 src 10.244.119.192
10.10.0.5 dev wg0 src 10.10.0.2

我从这里推导出了什么

这说明:

  1. 如果要访问远端 Pod 10.244.147.106
  2. 内核选择 tunl0
  3. 隧道对端是远端节点 10.10.0.5
  4. 而到 10.10.0.5 这条路本身是走 wg0

也就是说:

  • Pod overlay 是 IPIP
  • 节点 underlay 是 WireGuard

这就是后来文档里“overlay 套 underlay”的核心证据。


Step 5: 直接抓 iptables nat 规则,证明 ClusterIP 是如何工作的

实际命令

先查:

iptables-save -t nat | grep -n "10.98.52.186"
iptables-save -t nat | grep -n "10.96.0.10"

后来为了继续往下追,又查:

iptables-save -t nat | grep -n "KUBE-SVC-DDRHELRYSZTKW5IS"
iptables-save -t nat | grep -n "10.244.119.226"
iptables-save -t nat | grep -n "10.244.147.106"

为什么用 iptables-save

相比 iptables -Liptables-save 更适合:

  • 完整查看规则
  • 便于 grep 具体 IP 或链名
  • 能看到 NAT 细节和目标链

我先查了什么

先查:

  • hello-svc 的 ClusterIP:10.98.52.186
  • CoreDNS 的 Service IP:10.96.0.10

这是为了确认:

  • 这些 Service IP 是否真的被 kube-proxy 接管

我看到了什么

例如:

-A KUBE-SERVICES -d 10.98.52.186/32 ... -j KUBE-SVC-DDRHELRYSZTKW5IS

这说明:

  • 目的地是 10.98.52.186:80 的流量
  • 被引到一个属于 hello-svc 的专属服务链

继续往下看,我看到了:

-A KUBE-SVC-DDRHELRYSZTKW5IS ... -> 10.244.119.226:80 ...
-A KUBE-SVC-DDRHELRYSZTKW5IS ... -> 10.244.147.106:80 ...

再继续看 endpoint 链:

-A KUBE-SEP-... -j DNAT --to-destination 10.244.119.226:80
-A KUBE-SEP-... -j DNAT --to-destination 10.244.147.106:80

我得到的结论

这就是 Service VIP 工作的真实内核证据:

  • Service IP 不是网卡地址
  • iptables 规则匹配入口
  • 再通过 KUBE-SVC / KUBE-SEP 链做后端选择和 DNAT

Step 6: 创建 Pod 内网络实验对象

为什么还要新增实验对象

因为只看节点视角还不够。

用户后面真正最常做的是:

  • 进 Pod
  • 看 DNS
  • 访问 Service

所以我新增了:

这两个对象分别是做什么的

net-debug

一个 busybox 长驻 Pod,用来:

  • /etc/resolv.conf
  • nslookup
  • wget
  • ip addr
  • ip route

hello-headless

一个 Headless Service,用来和普通 hello-svc 做 DNS 对比。


Step 7: apply 网络实验对象

命令

KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/04-network

结果

service/hello-headless created
pod/net-debug created

然后我用:

kubectl -n learn-k8s wait --for=condition=Ready pod/net-debug --timeout=120s

等待它 Ready。

为什么 wait 很重要

因为网络类实验如果 Pod 本身还没 ready,就去 exec

  • 容易失败
  • 容易把“Pod 还没起来”和“网络有问题”混淆

这也是工程里控制变量的习惯。


Step 8: 从 Pod 里看 DNS 配置

命令

kubectl -n learn-k8s exec net-debug -- cat /etc/resolv.conf

结果

search learn-k8s.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5

为什么这条命令一定要学会

因为它是 DNS 排障第一站。

你只要看到 /etc/resolv.conf,通常就能先判断:

  • Pod 现在用哪个 DNS
  • 搜索域是否合理
  • ndots 配置是什么

很多 DNS 问题其实在这一步就能看出异常。


Step 9: 从 Pod 内做 DNS 查询实验

实际命令

kubectl -n learn-k8s exec net-debug -- nslookup kubernetes.default.svc.cluster.local
kubectl -n learn-k8s exec net-debug -- nslookup hello-svc.learn-k8s.svc.cluster.local
kubectl -n learn-k8s exec net-debug -- nslookup hello-headless.learn-k8s.svc.cluster.local

为什么选这三个目标

kubernetes.default.svc.cluster.local

这是集群最经典的内置 Service,适合作为 DNS 基线验证。

hello-svc

普通 ClusterIP Service,适合验证“DNS 返回 ClusterIP”。

hello-headless

Headless Service,适合验证“DNS 返回后端 Pod IP 列表”。

实际结果

kubernetes.default...

返回:

  • 10.96.0.1

hello-svc...

返回:

  • 10.98.52.186

hello-headless...

返回:

  • 10.244.119.226
  • 10.244.147.106

我得到的结论

这组实验非常干净地证明了:

  • 普通 Service DNS 返回虚拟 Service IP
  • Headless Service DNS 直接返回 Pod IP

这比抽象解释强得多。


Step 10: 从 Pod 里直接访问 Service

实际命令

kubectl -n learn-k8s exec net-debug -- wget -qO- http://hello-svc.learn-k8s.svc.cluster.local | head -n 3
kubectl -n learn-k8s exec net-debug -- sh -c 'for i in 1 2 3; do wget -qO- -T 2 http://10.98.52.186 >/dev/null && echo ok-$i; done'

为什么同时测域名和 IP

因为这能把:

  • DNS 解析链
  • Service 转发链

分开验证。

用域名访问成功

说明:

  • DNS 正常
  • Service 转发也正常

用 ClusterIP 直接访问成功

说明:

  • 即使绕过域名解析
  • Service 层 NAT 转发仍然正常

实际结果

我拿到了:

  • HTML 页面头
  • 连续 3 次 ok

我得到的结论

当前 hello-svc 的:

  • DNS
  • Service
  • Endpoints
  • 后端 Pod

这一整条链路都是通的。


Step 11: 从 Pod 里看自己的网络命名空间

实际命令

kubectl -n learn-k8s exec net-debug -- ip addr
kubectl -n learn-k8s exec net-debug -- ip route
kubectl -n learn-k8s get pod net-debug -o wide

为什么要看 Pod 自己的 ip route

因为你要知道:

  • Pod 视角下,它自己是怎么理解网络的

我看到的关键信息

net-debug 跑在:

  • hk652699382121

Pod IP:

  • 10.244.169.9

Pod 里默认路由:

default via 169.254.1.1 dev eth0

我从这里得到的结论

Pod 自己的视角非常简单:

  • 我有一个 eth0
  • 我把流量交给默认网关

后面的:

  • NAT
  • 隧道
  • 路由
  • kube-proxy

主要都发生在宿主机命名空间,不在 Pod 里。

这就是为什么网络排障必须同时看:

  • Pod 视角
  • Node 视角

Step 12: 这轮为什么没有直接抓 tcpdump

用户也提到了调试命令,tcpdump 确实是网络排障重器。

但我这轮没有立刻用它,原因是:

当前问题没有“不确定性”

我已经拿到了:

  • K8s 对象事实
  • Pod DNS 结果
  • Service 访问结果
  • iptables NAT 规则
  • 节点路由与隧道接口

这些已经足够形成闭环。

工程上应当先用“低成本证据”

先用:

  • kubectl get
  • describe
  • nslookup
  • ip route
  • iptables-save

把范围缩小。

只有在这些还解释不了问题时,才值得上:

  • tcpdump
  • conntrack
  • 更底层抓包

这是排障成本控制的问题。


Step 13: 将结果写成第四课教学文档

基于以上所有证据,我写入了:

这份文档里,我把:

  • 二层、三层、四层、七层
  • TCP / UDP
  • Pod / Node / Service / DNS
  • kube-proxy iptables
  • Headless Service
  • Calico IPIP
  • VXLAN / IPIP / BGP / WireGuard 的区别
  • 调试命令与排障顺序

全部按“原理 + 当前集群真实证据”组织起来了。

这样你看到的不再是抽象定义,而是:

  • 这条路径在你自己的集群上具体是什么样子

本轮命令清单

KUBECONFIG=~/.kube/config-k8s-lab kubectl -n kube-system get cm kube-proxy -o yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n kube-system logs ds/kube-proxy --tail=200 | rg 'Using iptables Proxier|Using nftables Proxier|Using ipvs Proxier|proxier'

KUBECONFIG=~/.kube/config-k8s-lab kubectl -n kube-system get ds calico-node -o yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl get ippools.crd.projectcalico.org -A -o yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n kube-system get cm calico-config -o yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n kube-system get svc kube-dns -o wide

ssh root@107.148.164.118 'ip -br a; ip route; ip -d link show type vxlan; ...'
ssh root@38.76.221.17 'ip -br a; ip route; ip -d link show type vxlan; ...'
ssh root@107.148.164.118 'iptables-save -t nat | grep ...'
ssh root@107.148.164.118 'ip route get 10.244.147.106; ip route get 10.10.0.5'

KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/04-network
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n learn-k8s wait --for=condition=Ready pod/net-debug --timeout=120s
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n learn-k8s get svc hello-svc hello-headless -o wide
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n learn-k8s get endpoints hello-svc hello-headless -o wide

KUBECONFIG=~/.kube/config-k8s-lab kubectl -n learn-k8s exec net-debug -- cat /etc/resolv.conf
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n learn-k8s exec net-debug -- nslookup kubernetes.default.svc.cluster.local
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n learn-k8s exec net-debug -- nslookup hello-svc.learn-k8s.svc.cluster.local
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n learn-k8s exec net-debug -- nslookup hello-headless.learn-k8s.svc.cluster.local
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n learn-k8s exec net-debug -- wget -qO- http://hello-svc.learn-k8s.svc.cluster.local
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n learn-k8s exec net-debug -- ip addr
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n learn-k8s exec net-debug -- ip route
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n learn-k8s get pod net-debug -o wide

本轮最重要的结论

结论一:这套集群不是用 VXLAN,而是 Calico IPIP

这是事实,不是猜测。

结论二:节点 underlay 和 Pod overlay 在这套集群里是分层叠加的

  • 节点层:WireGuard
  • Pod 层:IPIP

结论三:Service ClusterIP 的工作本质是 iptables DNAT

不是“虚拟网卡 magically 拿到了一个 IP”。

结论四:Headless Service 不是“小改动版 Service”,而是完全不同的服务发现模型

结论五:网络排障必须同时看 Pod 视角和 Node 视角

只看其中一边都会失真。


下一步建议

如果继续推进,最自然的下一个主题是:

  • NetworkPolicy
  • Calico 如何实现网络隔离
  • 为什么默认是“全通”
  • default-deny 的设计思想
  • 从“网络能到”升级到“网络该不该让它到”

这会把网络原理和安全边界连接起来。*** End Patch 天天中彩票 to=functions.apply_patch code不中返_patch code failed: invalid hunk at line 1175, '*** End Patch' is not a valid hunk header. Valid hunk headers: '*** Add File: {path}', '*** Delete File: {path}', '*** Update File: {path}' or '*** Move to: {path}'