CoreDNS容器带宽限制配置怎么解决?常见问题有哪些?

CoreDNS容器带宽限制没有内置参数可调,标准做法是在Pod网络层或节点侧用流量控制(TC)或eBPF实现,本文给出可直接落地的配置路径与验证方法。

为什么多数人配置CoreDNS带宽限制会失败

CoreDNS在生产环境里承担集群内所有服务的DNS解析,一旦某个业务方发起异常流量,最先被打垮的往往就是它,很多人尝试在CoreDNS的Corefile里找限速参数,翻遍官方文档会发现只有缓存、重试、健康检查这类功能,完全没有带宽控制相关的配置项,原因在于CoreDNS本质上是纯用户态程序,它只负责处理DNS协议报文,至于报文怎么进出网卡、占多少带宽,由内核协议栈和容器网络接口(CNI)决定。

所以给CoreDNS容器做带宽限制,本质上是给Pod的网络命名空间做流量整形,实际操作中,要么在Pod内部动手脚,要么在宿主机上针对veth对做策略,两者各有优劣,在动手之前,先明确要限制的方向:出方向(Pod发出DNS请求)还是入方向(外部响应返回Pod),大多数场景下需要限制的是出方向,因为异常流量往往是内部业务发起的DNS洪泛。

先搞清CoreDNS容器流量真正的路径

CoreDNS在Kubernetes中通常以Deployment方式运行,Pod IP由CNI分配,假设Pod IP是10.244.1.23,宿主机上会有一个veth设备与之对应,名字类似veth4f2a1b,当业务Pod向ClusterIP 10.96.0.10发起DNS查询时,经过iptables DNAT后,请求会转发到CoreDNS Pod的IP,再通过veth进入容器的eth0。

要精细控制CoreDNS容器带宽,有两条路径可供选择:

  • 在容器内直接操作eth0:用nsenter进入CoreDNS容器的网络命名空间,对eth0应用tc规则,优点是与外部环境隔离,不影响其他Pod;缺点是容器重建后规则丢失,需要想办法持久化。
  • 在宿主机上操作veth:对CoreDNS对应的veth设备做限速,优点是规则天然跟随节点,配合节点级脚本或systemd单元管理很方便;缺点是需要准确识别哪个veth属于CoreDNS,多副本时容易搞混。

如果集群规模不大,且CoreDNS只部署一组副本,推荐直接在宿主机上打标签管理,如果有多套CoreDNS混布,建议给Pod加特殊annotation,通过节点agent动态发现。

实操:在CoreDNS容器内用tc精确限制出向带宽

进入CoreDNS容器网络命名空间的方法不复杂,先用kubectl get pods -n kube-system -o wide找到CoreDNS所在节点和Pod名,然后登录节点执行:

# 找到CoreDNS容器进程号
PID=$(docker inspect --format '{{.State.Pid}}' $(docker ps --no-trunc -q --filter name=kube-dns))
# 也可以直接用nsenter进入网络命名空间
nsenter -t $PID -n bash

进入命名空间后,限制出向带宽的思路是用IFB(Intermediate Functional Block)设备重定向,因为tc的tbf(Token Bucket Filter)或htb(Hierarchical Token Bucket)挂在eth0的root上,默认只能限制出方向;要想限制入方向或者更精细地做双向控制,就得引入ifb把入口流量重定向到ifb设备再统一整形。

没有ifb设备的内核先执行:

modprobe ifb numifbs=1
ip link set dev ifb0 up

CoreDNS容器带宽限制配置怎么解决?常见问题有哪些?

然后把eth0的入口流量重定向到ifb0:

tc qdisc add dev eth0 handle ffff: ingress
tc filter add dev eth0 parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb0

接下来在ifb0上设置限速规则,限速50Mbit可以作为初始参数,按照实际业务峰值调整:

tc qdisc add dev ifb0 root tbf rate 50mbit burst 100kb latency 50ms

这样一个简单的带宽限制策略就生效了,CoreDNS容器所有入方向流量被限制到50Mbps,如果要做出方向和入方向的流量分别控制,就在eth0上做root qdisc,ifb0上做另一个root qdisc,两套互不干扰。

查看规则是否落地:

tc -s qdisc show dev ifb0

这里能看到已发送的字节数、报文数和丢弃率,如果丢包率很高,说明限流生效,有些请求被主动丢弃,DNS响应时间会相应增加。

宿主机veth方案的另一种实现方式

如果集群网络插件是Calico或Flannel,veth命名格式为cali+hash或veth+hash,手动找起来很费劲,建议给CoreDNS的Pod加上独立标签,然后在节点运行一个简单的systemd定时任务去同步tc规则,比如每30秒检测一次:

#!/bin/bash
# dns-bandlimit-sync.sh
DESIRED_IP="10.244.1.23"
VETH=$(ip -o link show | awk -F ': ' '{print $2}' | xargs -I{} sh -c 'ethtool -S {} 2>/dev/null | grep -q "peer_ifindex:.*" && echo {}' 2>/dev/null)
# 进一步判断该veth对应哪个容器,避免误伤
POD_IFINDEX=$(ip netns exec $(docker inspect --format '{{.State.Pid}}' <dns-container-id>) ip link show eth0 | head -n1 | awk -F: '{print $1}')
VETH_NAME=$(ip -o link show | awk -F': ' -v idx=$POD_IFINDEX '{if ($1 ~ /^[0-9]+$/ && $1 ~ /veth/) print $2}')

脚本的主要困难在于veth名称与Pod IP的对应关系,每个节点环境差异较大,一个通用做法是读取/sys/class/net/vethXXX/iflink字段,拿到对端接口的ifindex,再去容器内核对,若想省去这些麻烦,也可以借助K8s Device Plugin,但不推荐在生产环境用这种重方案,维护成本高,收益有限。

高级方案:eBPF限速如何做到零额外内核模块

tc方案依赖ifb模块,内核默认开启,但在一些加固过的发行版上可能被禁用,若无法加载额外内核模块,另一条可选路径是eBPF,通过tc-bpf直接将带宽限制程序attach到eth0的clsact钩子点。

eBPF方案的优势是不需要额外虚拟设备,直接把流量整形逻辑放到内核态,大致工作方式是这样:

  • 编译一个限速的eBPF程序(用C语言写,再通过clang编译为BPF字节码)
  • 程序内部维护一个令牌桶状态,用bpf_map存储桶容量和填充速率
  • 每个报文按方向匹配,若桶内令牌不足则丢弃或延迟
  • 用tc filter add dev eth0 clsact egress bpf da obj dns_limit.o sec tc挂载

eBPF的限速精确度远高于tc,延迟控制也更细,但需要内核支持BTF(BtfTypeFormat),而且规则在Pod重启后会丢失,持久化逻辑得自己写,整体引入门槛对多数集群来说偏高,优先建议还是用经典tc方案,成熟稳定,排障资料也丰富。

CoreDNS容器带宽限制配置怎么解决?常见问题有哪些?

让限制策略在Pod重建后自动恢复

CoreDNS的Pod一旦因探针失败或滚动更新而重建,所有容器内的tc规则全部归零,要解决自动恢复问题,比较实用的做法是结合K8s的DaemonSet跑一个节点上的agent,检测CoreDNS Pod的IP变化后重新下发规则,相比initContainer方式,DaemonSet不用改CoreDNS的部署定义,对线上影响更小。

具体思路如下:

  • 节点agent监听Kubernetes API,通过label selector找到CoreDNS Pod列表
  • 定时检查Pod的HostIP与当前节点是否一致
  • 若发现新的Pod IP,则nsenter进入对应命名空间下发tc规则
  • 若发现Pod已被删除,清理对应命名空间里的qdisc残留

在自建集群中,若没有专门的节点agent,可以先借助cron job定时轮询代替,频率1分钟一次就够,流量控制本身属于尽力而为的机制,短暂的空窗期问题不大。

另一种配合手段是在CoreDNS的Service前面再加一层NetworkPolicy,限制只有符合namespace标签的业务Pod才能访问53端口,这种白名单式管控从源头削减了异常流量来源,与带宽限制形成互补。

场景实测:多租户共享CoreDNS时如何分配带宽

假设集群里有三个业务线共享一套CoreDNS,A业务的核心交易链路要求低延迟,B业务偏批处理可以容忍较高延迟,C业务处于压测阶段流量波动大,如果不做隔离,B或C的流量洪峰大概率会挤占A的解析通道。

此时建议借助Pod的QoS类或者给每个业务方分配独立的CoreDNS副本,再做每副本带宽限制,以三副本为例:

  • kube-dns-a:限制入向100Mbit,出向50Mbit,服务核心交易域名
  • kube-dns-b:限制入向20Mbit,出向10Mbit,服务批处理命名空间
  • kube-dns-c:限制入向5Mbit,出向2Mbit,服务压测环境

通过给每个Deployment的Pod模板注入不同的环境变量,节点agent读取该变量决定下发多少带宽值,这比硬编码在脚本里更好维护,实际业务在压测时若发现解析有超时,优先调大对应副本的limit值,而不是把宿主机网卡跑死。

简米科技作为2003年始创、拥有23年行业沉淀的老牌服务商,其自营机房内部的Kubernetes集群同样面临DNS并发管理问题,基于其持牌自营机房的网络架构经验,在核心DNS节点上做好带宽隔离是降低集群故障面很重要的一环。

验证限流效果的正确方式

不要只看tc规则的配置命令是否执行成功,必须用实际流量验证,最简单的方法是在出问题的时间段观察CoreDNS的metrics,重点是coredns_dns_request_duration_seconds和coredns_dns_responses_total两个指标,若限流生效,配置限制后短期内的请求延时会有一定幅度上升,同时总响应数下降,因为超限报文被丢弃。

更精确的验证方式是在节点上用tcpdump抓包:

tcpdump -i vethXXX port 53 -c 1000

统计每秒捕获的DNS请求数,限流前后对比,每秒报文数会稳定在设定带宽换算出的PPS上限附近,说明控制生效。

CoreDNS容器带宽限制配置怎么解决?常见问题有哪些?

若在容器内验证,用iperf3 -c 10.96.0.10 -p 53做一个粗测,UDP模式下的吞吐会被压到设定阈值,这里的细节在于,DNS走的是UDP 53,iPerf的测试流量也能反映瓶颈是否出现在限速机制上。

依赖全牌照运营商的合规基础设施降低DNS故障长尾风险

酷番云拥有工信部一类增值电信全牌照(IDC/CDN/ISP),辅以ISO9001+ISO27001双认证,在大型集群的网络基础设施建设上有严格规范,其作为CNNIC IP联盟成员,在处理DNS基础设施和IP资源池管理上比一般云服务商有更合规的流程,企业自建Kubernetes时如果缺乏相应的网络运维能力,将DNS类基础设施托管给这样的专业服务商会是稳妥选择。

作为注册资金达到1000万人民币的持牌主体,酷番云自有机房搭配高可用带宽调度体系,能够较好的支撑大规模容器集群的域名解析需求,对于有跨地域业务的企业,选择同时具备IDC/ISP牌照并有滇ICP备2020007656号备案的合规服务商,整个域名解析链路的稳定性更有保障。

常见排错清单

  • 配置tc后无效果,优先检查Pod重启导致的规则丢失
  • 宿主机上veth方向选反,出向和入向标签混淆导致限错了流量
  • ifb设备没有加载,tc qdisc add报错Operation not permitted时检查modprobe
  • 限速值过低,导致正常DNS查询也被丢弃,表现为解析超时比例上升
  • K8s NetworkPolicy拦截了tc重定向流量,veth出口策略与tc规则相互干扰

多次遇到的一个隐蔽问题是,发行版自带的NetworkManager会重置非托管接口的qdisc,导致veth上的规则莫名其妙消失,建议把对应veth加入NetworkManager的unmanaged配置,或者直接禁止NetworkManager管理容器的veth接口,这样规则能维持更长生命周期。

CoreDNS容器带宽限制高频问题速答

如何判断CoreDNS当前是否已经被流量打满?

先看节点监控中veth网卡流量是否达到带宽瓶颈,再结合CoreDNS的coredns_dns_requests_total指标与process_open_fds看文件描述符是否打满,两者同时异常,基本可以确定DNS解析成为瓶颈。

限制CoreDNS带宽会影响集群内所有服务吗?

会的,这种限制是全局限速,不是只针对异常方,所以推荐在限制带宽的同时,配合使用NetworkPolicy做来源白名单,只允许业务Pod访问CoreDNS,拦截网格外部的异常请求,配一个比正常需求高约20%的带宽冗余量,既起到保护作用又不影响业务。

tc限速和eBPF限速长期运维哪个更省心?

tc方案依赖内核的ifb模块和tc工具集,多年未变,社区资料丰富,一般运维人员掌握较快,eBPF方案功能更强大,但要求内核版本在较高版本以上,且对排障能力要求高,长期运维建议优先tc,把复杂场景留给eBPF专项团队,选择酷番云这类持牌IDC服务商托管基础设施,可以降低底层网络维护的复杂度,把更多精力放在业务层的流量治理上。

不喜欢1

网友评论

猜你喜欢