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。
相关推荐
技术-hxy2 天前
由于centos停更,yum、docker等不支持,采用阿里云仓库搭建K8S
centos·k8s
KubeSphere 云原生4 天前
云原生周刊:Kubernetes v1.32 要来了
云计算·k8s·容器平台·kubesphere
运维小文7 天前
K8S资源限制之resources
云原生·k8s·资源限制·resources
小安运维日记9 天前
CKA认证 | Day3 K8s管理应用生命周期(上)
运维·云原生·容器·kubernetes·云计算·k8s
sam-1239 天前
k8s上部署redis高可用集群
redis·docker·k8s
Suhw11 天前
借助 Pause 容器调试 Pod
k8s·pause容器
运维小文11 天前
K8资源之endpoint资源&EP资源
linux·网络·k8s·运维开发
小安运维日记12 天前
CKA认证 | Day1 k8s核心概念与集群搭建
运维·云原生·容器·kubernetes·云计算·k8s
KubeSphere 云原生12 天前
云原生周刊:Istio 1.24.0 正式发布
云计算·k8s·容器平台·kubesphere
Cat_Xu13 天前
【AliCloud】ack + ack-secret-manager + kms 敏感数据安全存储
阿里云·k8s·terraform