笃行
首页
个人 & 心法
互联网/硬件后台
游戏基础架构
UE 引擎
游戏业务
AI / 大模型
数据结构与算法
机器学习数学
通用基础
GitHub
首页
个人 & 心法
互联网/硬件后台
游戏基础架构
UE 引擎
游戏业务
AI / 大模型
数据结构与算法
机器学习数学
通用基础
GitHub
  • 接入与通信

    • 游戏基础架构与工具
    • 接入网关(长连接接入层)
    • 消息总线(共享内存 IPC)
    • 帧同步(Lockstep)
    • 全栈极限低延迟(物理层→应用层)
    • KCP 与 QUIC
  • 网络与服务网格

    • CNI 与 K8s 网络插件
    • 容器运行时:Docker 与 containerd
    • Istio 与 Cilium 服务网格
    • 服务网格:中心化 vs 去中心化
    • eBPF 原理与在网络/可观测/安全的落地
    • 自研 Mesh 服务网格 × K8s 部署
    • 自研 Mesh × K8s · 数据面深水区
    • Tco 协程框架 · 运行时深水区
    • K8s 异构 & 网络插件
  • 有状态服务

    • 分布式游戏存储(分布式 KV)
    • 有状态服务的数据迁移
    • 有状态服务的数据恢复与容灾
    • 有状态服务的 K8s 编排层治理(防漂移 + 节点崩溃恢复)
    • 内存配置热刷新与更新机制
  • 算法与协程

    • 一致性哈希四算法实现(Ring Hash / Ketama / Maglev / Jump Hash)
    • 蓄水池抽样(Reservoir Sampling)与加权扩展
    • C++ 协程:有栈 vs 无栈 vs C++20 原生(函数染色与 libco)
    • Raft 与 Gossip:CP 强一致共识 vs AP 最终一致传播
  • 流量与承载

    • 游戏秒杀场景承载
    • 令牌桶与漏桶
    • 限流与熔断
    • 限流:互联网 vs 游戏
    • 灰度发布 · Canary / BlueGreen / A-B / Shadow
    • 服务器日常系统开发与维护 SOP
    • 极限发压工具设计(多长连接 + 发压任务)
    • 性能分析与优化方法论(六条排查线)
  • 编译与排查

    • 编译优化与 LLVM/Clang/GCC
    • Sanitizer 工具链与内存泄漏定位

K8s 异构 & 网络插件

异构混部 · CNI 三大流派 · 从 Overlay 到 hostNetwork

一句话结论

异构靠标签+调度四把刀,CNI 按规模选(Flannel/Calico/Cilium),游戏后台跳出 Overlay 用 hostNetwork 直连。

场景问题

异构混部:一群"不一样"的机器混在一起

  • CPU 异构:x86 (Intel/AMD) + ARM (鲲鹏/飞腾/Graviton) + 国产化(龙芯/申威)
  • 加速器异构:GPU (A100/H100/T4) + NPU (Ascend) + FPGA + DPU
  • 调度目标:让 Pod 落到匹配的节点,避免 x86 镜像跑到 ARM 节点上崩
  • 典型场景:训练用 GPU 节点池,推理用 CPU 节点池,前端 Web 用普通节点池;游戏 DS 战斗集群一机一 Pod(本项目就是)

打个比方(Overlay vs hostNetwork):K8s 的 Overlay 网络像给每个 Pod 发一个"虚拟门牌号"——Pod 之间通信时,真实网络包外面还要再套一层信封(VXLAN 之类的封装/隧道),信封上写宿主机的真地址寄出去,到对面再拆信封、取出内层包。好处是 Pod 的 IP 和物理网络彻底解耦,调度器爱把它塞哪台机就塞哪台;代价是每个包都要拆装一次信封(封装开销),对一秒几十帧、包来包往的游戏战斗服,这点延迟累积起来要命。于是游戏后台干脆跳过 Overlay,让 Pod 直接用宿主机网络(hostNetwork)——不套信封、点对点直邮,延迟压到最低。类比失效边界:直邮虽快,但 Pod 直接占用宿主机的端口,同一台机上两个 Pod 就不能抢同一个端口(端口冲突),还丢掉了 Overlay 的网络隔离与灵活调度。所以 hostNetwork 只配"一机一 Pod、要极致低延迟"的场景(如游戏 DS),高密度混部时老实用 Overlay。

异构调度的四把刀

  • Node Label:kubectl label node n1 node_type=ds arch=arm64 gpu=a100
  • NodeSelector:Pod 声明 nodeSelector: {node_type: ds}——硬约束
  • Taint / Toleration:节点打 taint gpu=true:NoSchedule,Pod 显式容忍才能调度上——防止普通 Pod 抢 GPU 节点
  • Affinity / AntiAffinity:
    • nodeAffinity:软/硬亲和 (更强表达力)
    • podAntiAffinity:DS 战斗进程用 topologyKey: kubernetes.io/hostname 强制一机一 Pod
  • 多 Runtime:containerd(默认)+ kata-containers(虚拟化隔离)+ runc;通过 RuntimeClass 让 Pod 选运行时(调用链、CRI/OCI、dockershim 移除详见 容器运行时,本篇只讲网络与调度)

实现方案

异构落地清单

  • 每类节点打 label(arch=, gpu=, node_type=)
  • 每个工作负载明确 nodeSelector/Affinity,别赖默认调度器
  • 关键机器加 taint 防止普通 Pod 抢占
  • 多架构镜像用 Docker Manifest / docker buildx 构建 arm64+amd64 双平台,Pull 时自动匹配
  • 通过 topologySpreadConstraints 打散跨可用区/机架

CNI 选型决策

  • 中小集群、快速起步 → Flannel VXLAN
  • 需要 NetworkPolicy、跨机房 → Calico BGP(BGP 通)或 IPIP(BGP 不通)
  • 大集群、性能极致、L7 策略 → Cilium (eBPF)
  • 游戏后台、跨集群直连需求 → hostNetwork DaemonSet(本项目)+ 一层业务级 Mesh 做路由

Sidecar 精简与 XDS 下发爆炸的应对

  • Sidecar CR 声明依赖:只下发本 Pod 真正调用的服务,避免全量
  • 命名空间级隔离:Sidecar.workloadSelector + egress.hosts
  • eBPF Cilium:完全去 Sidecar,控制面成本降到近零
  • DPU 卸载:把 Envoy 或 eBPF 数据面卸载到 SmartNIC/DPU(未来趋势)

hostNetwork:直接住进宿主机的网络栈

机制:普通 Pod 的 sandbox 会新建一个 network namespace,CNI 在里面插 veth、分 Pod IP、通路由。hostNetwork: true 时,sandbox 不新建 netns,直接复用宿主机的 netns——于是 CNI 的组网工作基本被跳过:不分 Pod IP、不建 veth、不进网桥与隧道。结果是 status.podIP == status.hostIP,容器里 ip addr 看到的是宿主机的全部网卡,ss -lntp 看到的是宿主机的全部监听端口,容器内 bind 0.0.0.0:8000 就是占了宿主机的 8000。(普通 Pod 那条 veth → cni0 → 隧道设备的完整链路,详见 CNI 网络插件。)

打个比方:普通 Pod 是公司给你分了独立工位和分机号;hostNetwork 则是你不申请工位,直接坐前台——外人一进门就找到你(NodeIP:Port 直连),不用转接(无封装无 NAT)。类比失效边界:真实的 hostNetwork 不只是"换了个位置",而是共用整个网络栈——路由表、iptables、conntrack、net.* sysctl 全是宿主机那一份,容器里改 sysctl(若给了权限)会影响整台机器;而且前台只有一把椅子,谁先坐下别人就没得坐。

得到什么:零封装零 NAT(省掉 Overlay 的封包 CPU 与 MTU 税)、少两跳虚拟设备、外部可直接用 NodeIP:Port 连(游戏 DS 要把战斗服 IP:Port 直接下发给客户端,这是最短路径)、抓包/perf 直接在宿主机做,不用钻 netns。

付出什么(按踩到概率排序的四个坑):

  1. 端口冲突:调度器只认 Pod 声明的 hostPort,容器里自己 bind 的端口它一无所知。两个 Pod 抢同一端口时,后起的直接 CrashLoop。对策是 podAntiAffinity 一机一 Pod,或自管端口池并把端口如实写进 hostPort 让调度器参与冲突检测。
  2. 解析不到 Service:hostNetwork Pod 默认继承宿主机的 /etc/resolv.conf,指向机房 DNS 而不是 CoreDNS,解析 xxx.default.svc.cluster.local 直接 NXDOMAIN。修法是显式写 dnsPolicy: ClusterFirstWithHostNet——hostNetwork 的第一高频坑。
  3. NetworkPolicy 失效:策略作用在 Pod IP 平面上,hostNetwork Pod 用的是 Node IP,多数 CNI 对它不生效(Cilium 要额外开 host firewall 才管得到)。指望用 NetworkPolicy 兜安全的,这里会有个洞。
  4. 安全面放大:共享网络栈意味着容器能访问宿主机的 localhost——kubelet 的 10250、各类只监听 127.0.0.1 的管理/调试端口,全都暴露。生产上靠 Pod Security Admission(baseline/restricted 默认禁 hostNetwork)+ 白名单管住。

另外一处易忽略:Service 把 hostNetwork Pod 作为 Endpoint 时记录的是 Node IP,对端看到的源 IP 也是 Node IP,丢掉了 Pod 粒度,审计、按来源限流时要留意。

谁天生就得用 hostNetwork("鸡生蛋")

CNI 插件自己(Flannel/Calico/Cilium 的 DaemonSet)、kube-proxy、node-exporter、部分 Ingress Controller。原因很朴素:它们的职责就是把 Pod 网络配起来,那它们启动时 Pod 网络还不存在,只能借宿主机的网络栈。记住这个例子,hostNetwork 的定位就再也忘不掉。

本项目具体落地

  • hostNetwork: true DaemonSet,宿主机 0.0.0.0:8000
  • 业务 Pod 通过 Downward API 注入 HOST_IP,参数 --mesh=$HOST_IP:8000
  • 跨集群:多地域各一份 kubeconfig,运维面板读多集群 Nodes().List() 组网
  • 接入层:托管 K8s 直连 CLB(isDirectConnect: true),按运营商 -dx/-yd/-wt 拆 CLB

为什么这么做

K8s 网络四大原则

  1. 每个 Pod 都有独立 IP(Pod IP 平面)
  2. Pod 与 Pod 之间直接互通,无需 NAT
  3. 节点上的 Agent(kubelet/kube-proxy)能与本节点所有 Pod 通信
  4. Pod 看到的自己 IP == 别的 Pod 看到的它的 IP(IP 一致性)

Service / kube-proxy 的演进史(面试高频)

kube-proxy 的核心工作只有一件:把发往 Service ClusterIP(虚拟 IP,无实体) 的流量,负载均衡到后端一组真实 Pod IP(Endpoints)。它 watch API-Server 的 Service/Endpoints 变化,把规则同步到内核。四个模式一条主线:转发从用户态搬回内核态、规则匹配从 O(N) 优化到 O(1)、最后连 kube-proxy 本身都被 eBPF 干掉。

  • ① userspace(远古,已废弃):kube-proxy 自己在用户态监听端口做代理转发。每个包都要 内核态 ↔ 用户态来回拷贝一次,上下文切换开销巨大;且 kube-proxy 进程挂了转发就断。慢到没法用,早废弃。
  • ② iptables(K8s 默认):kube-proxy 只当"规则搬运工"——把转发规则写进内核 netfilter/iptables,转发全在内核态完成,不再拷贝到用户态,性能大幅提升。痛点是规则匹配是线性链表 O(N):每个 Service 生成一串 KUBE-SERVICES → KUBE-SVC-xxx → KUBE-SEP-xxx 规则,5000+ Service 时匹配一个包要顺着链表往下扫,且每次 Endpoint 变化要全量 rewrite 规则,sync 耗时飙到秒级甚至分钟级,规则刷新时还可能丢包。负载均衡靠 statistic 模块的概率跳转,本质是随机,不支持真正的算法。
  • ③ ipvs(大集群推荐):底层换成内核的 IPVS(IP Virtual Server,本就是 LVS 的内核模块),用 hash 表存规则,查找 O(1),规则再多匹配耗时也恒定;增量更新 Endpoint 无需全量 rewrite。支持丰富的负载均衡算法:rr(轮询)/ wrr(加权轮询)/ lc(最少连接)/ dh(目标哈希)/ sh(源哈希)。注意:ipvs 只管 LB,NetworkPolicy / SNAT 这些仍需要少量 iptables 规则兜底,是"ipvs + 少量 iptables"混合模式。大集群务必从 iptables 切到 ipvs。
  • ④ eBPF(Cilium kube-proxy replacement):更激进——完全不装 kube-proxy。Service → Endpoint 的转发表直接做进 eBPF map,在 socket / tc 挂载点做转发(socket-LB 甚至在建连时就把 ClusterIP 改写成 Pod IP,省掉每包 DNAT)。绕过整条 iptables/ipvs 链路,O(1) 查 map + 内核态直连,5000+ Service 规模优势碾压,同时天然支持 L3-L7 策略与超强可观测。代价是内核版本要求高(≥4.19,强推荐 ≥5.10)。

一句话记忆

userspace 慢在用户态拷贝;iptables 快了但规则 O(N);ipvs 用 hash 做到 O(1) 且支持真正的 LB 算法;eBPF 干脆把 kube-proxy 整个删掉,转发做进内核 map。

Ingress vs Gateway API

  • Ingress:K8s 早期方案,抽象度低(只 host/path),能力靠 annotations 各家自定义 → 不可移植
  • Gateway API(新一代):三层资源模型 GatewayClass / Gateway / HTTPRoute|TCPRoute|GRPCRoute,类型安全、跨实现可移植
  • 生产落地:Nginx-Ingress、APISIX、Envoy Gateway、Cilium Gateway、某云厂商托管 K8s 直连 Ingress(自定义 Ingress,非标准 nginx)

Service Mesh:数据面演进

  • Sidecar (Istio + Envoy):每 Pod 一个 Envoy——协议栈反复横跳 + XDS 全量下发(500 服务 × 10 Pod = 每 Envoy 5000 实例)
  • Node-level (Cilium Service Mesh / Ambient Istio):一节点一代理,去 Sidecar,共享数据面
  • eBPF 干掉反复横跳:Cilium eBPF 数据面内核态直连,绕过用户态 socket 拷贝

为什么别的选择不行

CNI 三大流派原理对比

插件数据面原理控制面优点缺点
Flannel (VXLAN)Overlay:Pod 报文封装成 UDP,节点间走 VXLAN 隧道 (VNI)简单,etcd 存 subnet 划分极简,装完就能跑;跨节点无需路由配合双封装性能损耗 ~10-15%;不支持 NetworkPolicy
Calico (BGP)BGP 直路:节点间用 BGP 广告 Pod CIDR,走宿主机路由表不封装BGP RR (Route Reflector) + Felix Agent性能接近裸金属;支持 NetworkPolicy;跨机房友好需要网络设备支持 BGP 或用 IPIP 模式回退
Calico (IPIP)轻量隧道:IP-in-IP 单封装同上BGP 不通时的兜底仍有一层封装
Cilium (eBPF)内核 eBPF 数据面:绕过 iptables/kube-proxy,直接在 socket/tc 层转发基于 eBPF map,控制面 daemon性能最强、可观测最强;L7 策略、mTLS 都能做内核版本要求 (≥4.19 强推荐≥5.10);学习曲线陡

生产踩坑清单

  • VXLAN 上 MTU 未减 50 字节:包分片导致跨节点 gRPC 超时 → 显式设置 mtu: 1450
  • iptables 规则过万导致 kube-proxy sync 分钟级:Service 5000+ → 切 ipvs 或 eBPF
  • NodeLocal DNSCache 未启:CoreDNS 打爆 → 每节点部署 DNSCache Pod
  • CNI IP 池耗尽:Pod 疯狂重启回收 IP 慢 → 加大子网 / 用 Calico IPPool 分层
  • NetworkPolicy 与 Ingress Controller 冲突:Ingress 拦截被 NetworkPolicy 阻掉 → 命名空间级白名单 + 显式放行 ingress-nginx
  • Cilium 升级踩内核 bug:eBPF map 冲突 → 升前灰度节点,滚动升级

Overlay 依赖 vs 主机网络直连(本项目实战对比)

Overlay 依赖(业界主流):

  • 跨集群靠 Submariner / Skupper / KubeVirt-CNI 之类构建二层
  • 好处是抽象干净;坏处是多一层封装 + 依赖 K8s 网络组件稳定性

主机网络直连(自研 Mesh 采用):

  • hostNetwork: true 的 DaemonSet;跨集群走公司内网直接连宿主机 IP
  • 规避 K8s 网络组件频繁异常(Sidecar 时代真实痛点)
  • 缺点:占用宿主机端口;调度需要 hostPort 冲突检测

沉淀结论

  • 异构混部靠 label + nodeSelector/affinity + taint + 多架构镜像四把刀落地,别赖默认调度器。
  • CNI 选型按规模走:小集群 Flannel VXLAN 图省事;要 NetworkPolicy/跨机房上 Calico BGP(不通回退 IPIP);大集群、L7、极致性能上 Cilium eBPF。
  • Service 转发 / kube-proxy 演进:userspace(用户态拷贝,废弃)→ iptables(内核态但规则 O(N),K8s 默认)→ ipvs(hash 表 O(1)+ 真 LB 算法,大集群推荐)→ eBPF(Cilium 直接替换掉 kube-proxy,转发进 eBPF map)。主线是转发搬回内核态 + 匹配从 O(N) 到 O(1) + 最终去 kube-proxy。大集群务必从 iptables 切 ipvs 或 eBPF,否则 5000+ Service 时 sync 分钟级还可能刷新丢包。
  • hostNetwork 机制是"不建 netns、复用宿主机网络栈",因此没有 Pod IP、跳过 CNI 组网;换来零封装与 NodeIP:Port 直连,代价是端口冲突、dnsPolicy 必须设 ClusterFirstWithHostNet、NetworkPolicy 失效、localhost 暴露。CNI 自己/kube-proxy/node-exporter 天生用它(网络还没配好,只能借宿主机)。
  • 游戏后台的结论是跳出 Overlay:hostNetwork DaemonSet + 业务级 Mesh 做路由,用宿主机 IP 直连规避 K8s 网络组件频繁异常,代价是占端口、需 hostPort 冲突检测。

记忆口诀

异构四把刀:Label / NodeSelector / Taint-Toleration / Affinity
CNI 三流派:Flannel-VXLAN封装 / Calico-BGP直路 / Cilium-eBPF内核
kube-proxy 演进:userspace用户态 / iptables-O(N) / ipvs-O(1) / eBPF去proxy
hostNetwork 机制:不建 netns 复用宿主 / 无 Pod IP(podIP==hostIP) / 跳过 CNI 组网
hostNetwork 四坑:端口冲突 / DNS 要 ClusterFirstWithHostNet / NetworkPolicy 失效 / localhost 暴露
谁天生用:CNI 自己·kube-proxy·node-exporter —— 网络还没配好,只能借宿主机(鸡生蛋)
游戏后台:hostNetwork / 宿主机IP直连 / 避开Overlay / 占端口

自测:合上资料能说清楚吗?

kube-proxy 从 iptables 到 ipvs 再到 eBPF,核心解决的是什么问题?

参考答案

主线是转发搬回内核态 + 匹配从 O(N) 到 O(1) + 最终去 kube-proxy。iptables 规则是线性链表 O(N),5000+ Service 时 sync 分钟级还可能丢包;ipvs 用 hash 表 O(1) 且支持真 LB 算法;eBPF 把转发做进 map,直接删掉 kube-proxy。

Flannel VXLAN 和 Calico BGP 数据面原理有何本质区别?各适合什么场景?

参考答案

Flannel VXLAN 是 Overlay 双封装(报文封成 UDP 走隧道),极简但性能损耗 ~10-15%、不支持 NetworkPolicy,适合中小集群快速起步。Calico BGP 用 BGP 广告 Pod CIDR 走宿主机路由表不封装,性能接近裸金属、支持 NetworkPolicy、跨机房友好,但需网络支持 BGP(不通则回退 IPIP)。

为什么游戏 DS 战斗集群选 hostNetwork 直连而不是业界主流的 Overlay?

参考答案

用 hostNetwork: true DaemonSet 让业务走宿主机 IP 直连,规避 K8s 网络组件频繁异常(Sidecar 时代真实痛点),跨集群走公司内网直连即可。代价是占用宿主机端口、调度需 hostPort 冲突检测。战斗进程还用 podAntiAffinity 强制一机一 Pod。

hostNetwork 在机制上到底做了什么?它有哪四个必知的坑?

参考答案

机制:sandbox 不新建 network namespace,直接复用宿主机的 netns,于是 CNI 的组网基本被跳过——不分 Pod IP、不建 veth、不进网桥与隧道,podIP == hostIP,容器里看到的是宿主机全部网卡与监听端口。

四个坑:① 端口冲突——调度器只认 hostPort,不知道容器里 bind 了什么,靠 podAntiAffinity 一机一 Pod 或自管端口池兜;② 解析不到 Service——默认继承宿主机 /etc/resolv.conf,必须显式设 dnsPolicy: ClusterFirstWithHostNet(第一高频坑);③ NetworkPolicy 失效——策略作用在 Pod IP 平面,它用的是 Node IP;④ 安全面放大——能访问宿主机 localhost(kubelet 10250、各类只听 127.0.0.1 的端口),靠 Pod Security Admission 管住。

另外 Service 记录的 Endpoint 是 Node IP,丢失 Pod 粒度。天生必须用 hostNetwork 的是 CNI 自己、kube-proxy、node-exporter——它们负责把网络配起来,启动时 Pod 网络还不存在。

异构混部要让 Pod 精准落到匹配节点,靠哪几个机制?

参考答案

四把刀:Node Label 打标(arch/gpu/node_type)→ NodeSelector 硬约束 → Taint/Toleration 防普通 Pod 抢 GPU 节点 → Affinity/AntiAffinity 更强表达力。镜像用 docker buildx 构建多架构 manifest,Pull 时自动匹配 arm64/amd64。关键:别赖默认调度器。

Sidecar Mesh 的 XDS 全量下发为什么会爆炸?怎么应对?

参考答案

500 服务 × 10 Pod = 每个 Envoy 要下发 5000 个实例,控制面成本爆炸。应对:用 Sidecar CR 声明依赖只下发真正调用的服务、命名空间级隔离(workloadSelector+egress.hosts)、上 eBPF Cilium 去 Sidecar 把控制面成本降到近零、或把数据面 卸载到 DPU。

内容来源

迁移自 guide/theme-k8s-network(综合整理)

相关专题:kube-proxy 背后的 eBPF 数据面原理(verifier/JIT/XDP、如何 O(1) 取代 iptables)详见 eBPF;Overlay/Underlay 判据、一个包经过 veth→cni0→flannel.1 的完整链路、IPAM 两级分配与跨节点(cross)模块、CNI 三流派实现与网络排障应对详见 CNI 网络插件;容器运行时(Docker/containerd、CRI/OCI、dockershim 移除、RuntimeClass 隔离谱系)详见 容器运行时。

最近更新: 2026/9/10 11:38
Prev
Tco 协程框架 · 运行时深水区