Posts for: #Network

KubeCon North America 2025 Review

KubeCon North America 2025 13号结束了,官网上也有了些会议资料。挑了几个感兴趣的话题总结下。

Dynamic Routing with Multi-Cluster Inference Gateway

https://kccncna2025.sched.com/event/27FeP/ai-inference-without-boundaries-dynamic-routing-with-multi-cluster-inference-gateway-rob-scott-google-daneyon-hansen-soloio?iframe=no&w=100%&sidebar=yes&bg=no

这段时间正好在做 AI 网关,这个话题可以说是“瞌睡了送枕头”。

推理服务和传统 API 流量相比,在 payload,响应时间,后端资源开销上都有着很大的差异(见下图)。 2781cd595fe30c8da311d124a8e4ee35_MD5

因此,推理网关需要做到后端负载感知的调度(传统 API 网关也有类似的方案,尤其是在后端机型不一样,普通的 rr 无法均匀负载时,做后端负载感知动态调权)。在 Gateway 和推理实例间引入了一个 EPP(Endpoint Picker)组件(注:EPP 现在也是 Kubernetes 做推理服务的一个通用组件),采集推理实例的指标来动态选择推理后端。 3462773fb295a9dabdabc886b1de43ca_MD5

benchmark 数据显示,使用推理网关相比传统负载均衡,推理实例间的负载更加均衡,请求排队更少,从而降低了响应时间。

在多集群场景下,这套方案需要解决 3 个问题:

  1. 服务发现:Cluster Inference Services 如何暴露给 Gateway?
  2. 后端选择:Gateway 如何在多集群间分配流量?
  3. 路由模式:流量如何从 Gateway 转发到集群?

第一个问题作者提了 3 个解决方法: fd7a6ffacbfd58fcd6482dd40b861461_MD5

不是关注重点,略过。

第二个问题,简单的 RR 和 Active-Passive 肯定就失去了推理网关负载感知的优势。所以,在 EPP 感知负载之外,Gateway 也得做负载感知。作者也提了两个方法: 1d3df4bac299f39b0c917b886bab2a27_MD5 f4bd518bc6dce2999d5805d5b2d46dac_MD5

从层级上来说,EPP Aggregate Metrics 方案更加简洁,毕竟在 EPP 上还得做二次调度。

最后一个问题,如果 EPP 能跨集群直接访问,direct routing 是最合适的方式,不行的话再加一层网关,使用 Cluster-Local Gateway 做暴露也能访问。

VRF: An Overview

什么是 VRF(virtual routing forwarding)

VRF是一种实现三层网络隔离的关键技术。它通过创建多个路由表,为不同的网络流量提供独立的转发路径。 这意味着,任何三层网络结构,如接口的IP地址、静态路由的配置,甚至BGP(边界网关协议)会话,都可以被映射到特定的VRF中。这种映射机制就像是为每个VRF构建了一个独立的网络空间,彼此之间相互隔离,极大地增强了网络的安全性和管理的便利性。在MPLS VPN(多协议标签交换虚拟专用网络)等应用场景中,VRF为实现大规模的网络隔离和灵活的路由策略提供了基础框架。就像 VLAN 隔离了二层网络一样,VRF 隔离了三层网络。

5eb20c3e919fe3724b92c2ae7a66a7da_MD5

为什么需要 VRF

在 VRF 出现之前,Linux 用户主要采用两种方式来尝试实现类似的功能:策略路由(policy routing)和网络命名空间(net namespace)。然而,这两种方法都存在明显的局限性。

策略路由虽然能够通过多个路由表和策略规则来模拟 VRF 的部分功能(事实上,在 Linux 中,也是基于策略路由来去对 VRF 做的实现),但它的缺点十分突出。这种方式在配置和管理上非常复杂,难以确保网络隔离的有效性,在面对严格的网络审计时,往往无法通过。其复杂性不仅增加了运维的难度,还可能导致网络故障的风险上升,因此不被推荐使用。

网络命名空间在容器技术兴起后得到了广泛应用,它能够为容器提供全面的网络隔离。但在模拟 VRF 功能时,却显得有些“大材小用”。网络命名空间会对所有网络相关的资源进行完全隔离,包括设备、接口、ARP 表和路由表等。这意味着,即使是一些不需要隔离的服务,也会被隔离在不同的命名空间中。以 LLDP(链路层发现协议)为例,在使用网络命名空间的情况下,若要在不同的网络隔离环境中使用 LLDP,就需要在每个命名空间中单独运行实例,并且由于默认套接字相同,还需要为每个实例创建独特的套接字。这不仅增加了系统的开销,还使得管理变得更加复杂。相比之下,VRF 在隔离三层网络结构的同时,允许全局配置的共享和非三层感知服务的统一运行,大大提高了资源的利用效率。

Policy Routing VRF Net Namespace
隔离路由表 隔离三层网络 整个协议栈从二层到 socket 隔离

a9f7c55f8f39572d339b138fb1e12429_MD5c9b6614c864c7d35a8ef0a4f12ecdbfa_MD59edc8b051f504bf72140d1238513d687_MD5

VRF 配置

在 Linux 系统中配置 VRF,主要借助iproute2包来完成一系列操作。

  • 创建 VRF,并关联到 table 1
test1@test1:~$ ip link add vrf-1 type vrf table 1
test1@test1:~$ ip link set vrf-1 up
  • 添加接口到 VRF,可以看到 wg0 的 master 是 vrf-1,所有 wg0 的流量会使用关联的 vrf-1 路由表进行路由
test1@test1:~$ ip link set wg0 master vrf-1
test1@test1:~$ ip -d link show wg0 
9: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1400 qdisc noqueue master vrf-1 state UNKNOWN mode DEFAULT group default qlen 1000
    link/none  promiscuity 0 minmtu 0 maxmtu 2147483552 
    wireguard 
    vrf_slave table 1 addrgenmode none numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
  • 添加和查看 VRF 静态路由
root@test1:~# ip route add default via 10.1.0.10 vrf vrf-1
root@test1:~# ip route show table 1 
default via 10.1.0.10 dev wg0 
local 10.1.0.10 dev wg0 proto kernel scope host src 10.1.0.10 
root@test1:~# ip route show vrf vrf-1 
default via 10.1.0.10 dev wg0 

VRF 之间路由

有两种方法可以执行跨 VRF 路由。第一种方法涉及一个 VRF 的表中配置的路由,指向绑定到不同 VRF 的设备。

Linux 收包和发包流程

流程图

From 《Understanding Linux Network Internals》

image-20220128172744059

image-20220129171450456

收包流程

TL; DR

image-20220129171450456

  • 收包NET_RX_SOFTRQ的软中断处理函数是net_rx_action
  • net_rx_action中会调用网卡驱动注册的poll回调函数处理
  • poll回调函数将数据帧从网卡ring buffer中取出,构造skb:
    • 运行xdpdrv上的bpf program,得到action result
    • 如果是XDP_PASS,构造skb,并初始化skb中一些metadata字段
  • 调用内核的GRO和RPS处理流程
  • 进入__netif_receive_skb_core,处理skb:
    • 运行xdpgeneric上的bpf program,得到action result
    • 如果是XDP_PASS,遍历ptype_alldev->ptype_all,进行抓包处理
    • tc ingress 处理sch_handle_ingress
    • 查找ptype_basedev->ptype_specific,交由对应的三层协议栈回调函数处理

NAPI

**NAPI的思想是从完全的中断收包模型,改用中断和polling混合。**如果内核在处理旧的数据帧时,收到了新的数据帧,网卡设备没有必要再触发一个中断。内核继续处理设备input queue里的数据(该设备的interrupt禁止了),在队列为空时重新使能中断。

从内核的角度,NAPI有如下的优势:

  • 降低CPU的负载(更少的中断)
  • 更多的设备处理公平性

以ixgbe网卡为例,描述下NAPI处理流程。

注册

ixgbe驱动在初始化中断向量时会调用netif_napi_add初始化NAPI,==ixgbe_poll函数注册到napi结构体,并将napi加入到设备的napi_list内==:

/**
 * ixgbe_alloc_q_vector - Allocate memory for a single interrupt vector
 * @adapter: board private structure to initialize
 * @v_count: q_vectors allocated on adapter, used for ring interleaving
 * @v_idx: index of vector in adapter struct
 * @txr_count: total number of Tx rings to allocate
 * @txr_idx: index of first Tx ring to allocate
 * @xdp_count: total number of XDP rings to allocate
 * @xdp_idx: index of first XDP ring to allocate
 * @rxr_count: total number of Rx rings to allocate
 * @rxr_idx: index of first Rx ring to allocate
 *
 * We allocate one q_vector.  If allocation fails we return -ENOMEM.
 **/
static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter,
                int v_count, int v_idx,
                int txr_count, int txr_idx,
                int xdp_count, int xdp_idx,
                int rxr_count, int rxr_idx)
{
	/* ... */

    /* initialize NAPI */
    netif_napi_add(adapter->netdev, &q_vector->napi,
               ixgbe_poll, 64);
}

中断处理函数

ixgbe驱动收到中断后,会调用ixgbe_msix_clean_rings