数据库到底能不能上 K8s?Cilium 式高性能网络或许才是那个变量
【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium
“数据库不适合容器化”——这句话在云原生圈子里流传了很久。支持者搬出 StatefulSet 的运维复杂度、存储的持久化难题;反对者则盯着一个更物理、更难以绕开的问题:性能。MySQL、Redis、Kafka 这类对延迟和连接密度极度敏感的工作负载,一旦被塞进 Pod,走一遍 CNI 的数据路径,吞吐掉几个百分点、P99 延迟翻倍,立刻就成了“K8s 不适合跑数据库”的实锤。
但很少有人追问:这些性能损耗到底损耗在哪里?是容器隔离本身,还是那一层被默认接受、从未被审视过的网络数据面?本文不打算站队“能不能”,而是把问题拆开——数据库容器化的性能焦虑从哪来,网络数据面在其中占多大权重,以及当 eBPF 把 Linux 内核变成一张可编程的网络芯片时,结论是否还能成立。
数据库容器化的性能焦虑来源
先做一个区分:数据库上 K8s 的“焦虑”其实来自两个完全不同的层面。
第一层是调度与状态管理。数据库是有状态服务,K8s 的 Pod 是易失的,需要 StatefulSet、PV/PVC、Operator 来兜底。这一层的问题本质是“工程复杂度”,不是“物理性能”,业界已有大量成熟实践。
第二层才是真正让 DBA 睡不着觉的:网络数据面的额外开销。数据库是高 IO 型工作负载,一次查询的延迟由网络往返、协议解析、磁盘/内存访问共同决定。在裸机上,客户端到 MySQL 的连接是一跳直连;在 K8s 里,这条链路被插入了一整套“集装箱装卸”流程——veth 对、CNI 网桥、iptables NAT 规则、kube-proxy 的 Service 转发。
每一层都在吃掉微秒级的延迟,而数据库恰恰对微秒敏感。更致命的是,传统方案的开销并不恒定:连接数越多、新建连接越频繁,iptables 规则链的匹配成本就越高,性能衰减呈非线性。
网络数据面在其中的真实权重
要回答“网络到底占多大权重”,先看一个反直觉的事实。Cilium 官方性能基准(见 benchmark.rst)在一套 100Gbps 直连的裸机环境上,用 netperf 对多种 CNI 做了横向对比,结论令人意外:eBPF 数据面在单流 TCP 吞吐测试中,性能甚至超过了节点到节点的裸机基线。
原因写得很直白:eBPF 能够绕过节点上的 iptables 层,而裸机基线反而还要走一遍 iptables。换句话说,容器化带来的“网络性能损失”,很大一部分根本不是容器造成的,而是传统网络栈——iptables 规则匹配、NAT 转换、kube-proxy 的用户态转发——造成的。
这对数据库意味着什么?看两个更贴近数据库场景的指标:
- TCP_RR(请求/响应速率):模拟短往返、长连接的交互,正是 REST/gRPC 调用数据库的形态。32 进程并发下,Cilium 能达到接近100 万 req/s,系统资源消耗约 30%(数据见 benchmark.rst)。
- TCP_CRR(连接建立速率):模拟高新建连接场景。文档明确指出,这一项“展示了不同配置之间的巨大差异”,因为它放大了 iptables 的固有成本——iptables 为每个连接执行大部分工作后再缓存结果,连接风暴恰恰是其最差场景。
数据库连接池的每次扩容、每个短连接查询,都会直接命中这两个指标。如果网络数据面能把请求/响应速率做到接近裸机基线,同时把新建连接成本压到最低,那么“容器化损失”这块最大的石头就被搬走了。
eBPF 高性能网络能否改写结论
Cilium 的核心思路,是把网络功能从用户态和 iptables 里搬进内核的 eBPF 虚拟机,在收包的最早时刻就地完成处理。仓库的 eBPF 介绍文档(intro.rst)列出了它使用的几类钩子,每一类都对应一项数据库关心的能力:
- XDP 钩子:位于网卡驱动收到报文的最早位置,在协议栈做任何处理之前就能运行 BPF 程序,是过滤非法流量、防 DDoS 的最快路径;
- TC Ingress/Egress 钩子:挂载在 veth 宿主侧,对进出容器的每个报文执行策略与转发,这是 Pod 流量进入/离开节点的必经关卡;
- Socket Operations + Socket Send/Recv 钩子:在 socket 层直接拦截 TCP 事件,实现 socket-level acceleration——连接建立后,报文可以绕过整套网络栈直接从一个 socket 投递到另一个 socket。
最后一条对数据库几乎是“量身定制”:长连接下的反复查询,走 socket 直通路径,几乎不产生额外的数据面开销。这也是文档在 lifeofapacket.rst 中专门描绘的“Life of a Packet”加速路径。
模式选择:原生路由还是封装?
Cilium 提供两种主流数据面模式,取舍逻辑本身就是性能考量(详见 routing.rst):
- Encapsulation(VXLAN/Geneve):对底层网络要求最低,节点间用 UDP 隧道互通,但每个报文要背 50 字节的封装头,有效 MTU 变小,吞吐上限被压低;
- Native Routing(原生路由):不再封装,把非本端点的报文直接交给内核路由子系统,让 Pod IP 在物理网络里“真路由”。云厂商环境(AWS ENI、GKE)正是基于这一模式,让 Pod IP 原生可路由、免 SNAT。
对跑数据库的集群,原生路由模式的意义在于:跨节点流量不再经过隧道封装与解封装,少一层处理就少一段延迟,也把 MTU 和吞吐的损失降到最低。
替换 kube-proxy:拆掉服务转发的“收费站”
Service 是 K8s 访问数据库的标准入口,而传统 kube-proxy 用 iptables 实现 Service 转发,这正是性能黑洞所在。Cilium 提供了完整的 kube-proxy 替换方案(kubeproxy-free.rst):用kubeadm init --skip-phases=addon/kube-proxy跳过 kube-proxy,然后以kubeProxyReplacement=true部署 Cilium,即可用 eBPF 实现 ClusterIP、NodePort、LoadBalancer、externalIPs 乃至 hostPort 的全部处理。
文档特别点明,这套替换依赖socket-LB(socket 层负载均衡)特性——负载均衡决策从每报文做一次,提前到连接建立时在 socket 层完成一次。对数据库集群而言,这直接砍掉了连接生命周期里最昂贵的重复劳动:不再需要每个报文都去遍历 NAT 规则链。
带宽管理:把 QoS 精确到 Pod
数据库集群常见的另一痛点是租户隔离与流量整形:一个跑批任务的 Pod 可能把整条链路打满,殃及在线业务。Cilium 的带宽管理器(bandwidth-manager.rst)用EDT(最早出发时间)+ eBPF实现按 Pod 的带宽限速,支持kubernetes.io/egress-bandwidth/kubernetes.io/ingress-bandwidth注解,并可作为BBR 拥塞控制的前置条件——BBR 对长肥管道的吞吐提升众所周知,而这正是跨可用区同步、日志传输这类数据库周边流量的刚需。
文档还给了验证命令:cilium-dbg status | grep BandwidthManager,正常输出为EDT with BPF [BBR] [eth0],一眼确认 QoS 已生效。
结论:变量不在容器,而在数据面
回到开头的问题。数据库能不能上 K8s?如果焦虑来自 StatefulSet 和存储,那是工程问题,答案在 Operator 生态里;如果焦虑来自性能,那么数据说明:传统 CNI 的性能折损,大部分不是容器隔离的固有代价,而是 iptables 与 kube-proxy 这套古董数据面的技术债。
Cilium 的意义在于用 eBPF 把 Linux 内核变成可编程、可观测、可热更新的数据面:socket 层加速让长连接数据库流量逼近裸机、原生路由去掉封装开销、kube-proxy 替换拆掉转发收费站、EDT/BBR 让 QoS 精确到 Pod。当网络这一层不再拖后腿,数据库容器化剩下的争论,就回到了它本来的位置——存储与运维,而不是网络。
当然,eBPF 并非银弹:它对内核版本有硬性要求,调试难度也远高于 iptables——网易数帆等团队在落地实践(见其公开分享)中反复提到的“eBPF 调试难、内核版本要求高”正是真实的代价。但方向已经很清晰:决定数据库能不能上 K8s 的,从来不是 K8s 本身,而是你选择用哪一层网络去承载它。Cilium 式的 eBPF 数据面,正在把这个问题的答案从“不能”改写为“可以,但要有正确的网络基础设施”。
【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考