k8s实践——命名空间隔离+request-key机制解决CSI内核态域名解析

0x01 背景

Pod需要使用远程存储的PV,由同k8s集群内的服务提供的存储服务。一开始的做法是:

  1. CSI中解析Service的clusterIP。
  2. 然后使用clusterIP挂载PV卷。

但因为走clusterIP时,经过多次转换:

  1. clusterIP到Pod IP 经过了1次NAT
  2. Pod IP到最终服务。经过1次转发,具体性能损耗跟 CNI 实现相关。

导致了最终client写PV的性能损失严重。

0x02 解决方法

既然走容器网络导致性能差,修改服务端的部署形式为 hostNetwork,绕过容器网络。但带来一个问题,存储服务可能切换节点,导致 client 端无法正常重连(切换节点带来的数据不一致的问题能处理),这一点不能接受。

新的方案:

为服务端创建一个 Headless Service,针对 Deployment 类型的负载Headless Service会解析提到所有Pod的 IP 地址列表具体见官方文档,那唯一的问题还剩下 client 重连时,这个域名怎么解析?因为使用的驱动是内核提供的,内核中无法直接使用glibc的域名解析功能,即无法使用外部的DNS Server,即使是/etc/resolv.conf中指定的。

0x03 request-key 机制

通过调查了解到内核提供了request-key机制,可以从内核调用到用户态的应用。request-key本来是用于内核和用户态之间的安全token管理的,后也扩展用于其他用途。以内核解析域名来说,大概流程如下:

  1. 内核发起域名解析转到dns_resolver模块【内核态】。
  2. 发起request-key请求转到key管理模块【内核态】。
  3. key管理模块调用/sbin/request-key,调用到用户态【内核态】。
  4. /sbin/request-key根据/etc/request-key.conf中的配置,分发到对应的命令调用,示例为/sbin/key.dns_resolver【用户态】。
  5. /sbin/key.dns_resolver调用glibc域名解析,完成解析,并调用request-key相关系统调用,设置好payload,即域名对应的IP地址【用户态】。

但还有新的问题:key.dns_resolver只能使用/etc/hosts和/etc/resolv.conf解析域名,不支持从额外的dns server解析域名。

0x04 具体的方案

所有的方法都要通过修改 /etc/request-key.conf配置文件指定自己的程序进行解析。

后面的流程有以下方案:

自己写脚本,通过/etc/request-key.conf配置文件指定自己的脚本,通过kubectl去查询 Pod IP地址,调用/sbin/request-key将结果写回。

问题:C语言中对字符串的处理在dns_resolver和request-key两个模块之间发生了冲突,使用/sbin/request-key写入的IP地址被dns_resolver内核模块认为非法,这个方案行不通,详见QA部分解释。

通过C调用key-utils的SDK,可以实现同样的功能,但基本上照抄key.dns_resolver的实现。突然想到可以用Python调用so库的方法,验证了下基本可行。但又有一个新的问题:

Python标准库中的域名解析同样不支持指定域名,想要支持就要引入第3方的dns模块。

最终方案比较:

方案 优点 缺点
写Python调用key-utils的SDK so完成IP写回内核 灵活控制对coreDNS的访问。 需要调用第3方的dns解析服务、或者直接访问 kube-apiserver获取IP,加重kube-apiserver的负担。
shell脚本通过unshare mount namespace 隔离,生成临时的/etc/resolv.conf,调用/sbin/key.dns_resolver实现 不用访问 kube-apsierver,根据kubelet的配置可获取coreDNS的地址,不用感知具体的DNS解析细节。更通用,其他的 headless也可以用 无法控制调用频率

考虑这种异常切换解析并不会太频繁,最终选择了第2种方案。mount namespace 可以方便地通过 unshare -m 来实现。

0x05 补充QA

Q:/sbin/key.dns_resolver支持从/etc/hosts解析域名,为什么不修改 /etc/hosts?

A:/etc/hosts是全局配置,修改冲突不容易控制,出现冲突时影响不可控。

Q:为什么不能修改/etc/resolv.conf配置,指向coreDNS?

A:虽然coreDNS也支持将非k8s域名转向宿主中/etc/resolv.conf中的指定的DNS,但这种机制依赖 coreDNS,对整个系统的影响过大。

Q: 为什么不用/sbin/request-key回写解析到的IP地址?

A:这种实现了验证了,发现request-key和dns_resolver的实现关于C中字符串的处理有不一致的地方,前者payload长度未包含\0,后者要求包含。这一点是通过bpf钩子确认的。

0x06 总结

问题的解决过程中尝试了多种方案,最终最适合的方案巧妙运用了命名空间隔离机制,这也是了解容器底层原理的好处。

同时带来一点关于命名空间的用途回顾:

  1. 容器内不希望被宿主机影响。
  2. 容器内不期望影响宿主机(本文中的场景),可随意设置/etc/resolv.conf。
相关推荐
SilentSamsara18 小时前
存储卷体系:EmptyDir/HostPath/PV/PVC/StorageClass 的选型决策树
服务器·微服务·云原生·容器·架构·kubernetes·k8s
SilentSamsara20 小时前
Service 与 Ingress:从 ClusterIP 到云厂商 ALB 的完整流量路径
linux·运维·服务器·微服务·kubernetes·k8s·运维开发
SilentSamsara21 小时前
ConfigMap 与 Secret:配置注入的四种姿势与安全边界
linux·运维·服务器·安全·微服务·kubernetes·k8s
没有口袋啦2 天前
《基于 GitOps 理念的企业级自动化 CI/CD 流水线》
阿里云·ci/cd·云原生·自动化·k8s
鼎道开发者联盟5 天前
OpenClaw在K8s Pod中稳定运行的Docker制作指南(源码版)
docker·k8s·openclaw
恼书:-(空寄7 天前
HPA与VPA自动伸缩实战(应对流量洪峰的弹性方案)
k8s
Hns.7 天前
使用K3s 搭建K8S集群保姆级教学
容器·k8s
w6100104667 天前
Cka-2026-gateway解释
gateway·k8s·cka
陌陌卡上9 天前
我在 Debian 11 上把 K8s 单机搭起来了,过程没你想的那么顺(/opt 目录版)
运维·k8s·系统·debian11
w6100104669 天前
cka-2026-cri-dockerd
运维·k8s·cka