Repository Reading Site
本轮操作记录:Kubernetes 网络原理、协议对比与调试实验
用户明确提出: 所以这轮工作不只是“继续讲 K8s 对象”,而是要把: 全部统一到一个教学框架里。 为了做到这一点,我先做了现实环境调查,再做 Pod/Service/DNS 实验,最后才写教学文档。 --- 因为你要讲 Kubernetes Service 网络,先得知道: 如果你不知道当前模式是: 那后面的原理解释就会空。 `config.conf` 里
本轮操作记录: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 是靠什么实现的
如果你不知道当前模式是:
iptablesipvsnftables
那后面的原理解释就会空。
从 ConfigMap 里读到了什么
config.conf 里 mode: ""。
这个值本身不够直观,所以我又去看日志。
从日志里读到了什么
我看到了:
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_IPIPCALICO_IPV4POOL_VXLAN
IPPool CRD
可以看到实际网络池定义,比如:
cidripipModevxlanModenatOutgoing
calico-config ConfigMap
可以看到:
- 后端类型
- CNI 配置模板
- MTU 配置
我读到的关键信息
在 calico-node 里
我看到:
CALICO_IPV4POOL_IPIP=AlwaysCALICO_IPV4POOL_VXLAN=NeverCLUSTER_TYPE=k8s,bgp
在 IPPool 里
我看到:
cidr: 10.244.0.0/16
ipipMode: Always
vxlanMode: Never
natOutgoing: true
在 calico-config 里
我看到:
calico_backend: birdveth_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.118(us590068728056)38.76.221.17(hk652699382121)
执行了:
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 上看到了:
ens17wg0tunl0- 一堆
cali*
在节点 hk652699382121 上也看到了:
wg0tunl0cali*
为什么这些接口很关键
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
我从这里推导出了什么
这说明:
- 如果要访问远端 Pod
10.244.147.106 - 内核选择
tunl0 - 隧道对端是远端节点
10.10.0.5 - 而到
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 -L,iptables-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 nslookupwgetip addrip 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.22610.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 访问结果
iptablesNAT 规则- 节点路由与隧道接口
这些已经足够形成闭环。
工程上应当先用“低成本证据”
先用:
kubectl getdescribenslookupip routeiptables-save
把范围缩小。
只有在这些还解释不了问题时,才值得上:
tcpdumpconntrack- 更底层抓包
这是排障成本控制的问题。
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}'