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。
相关推荐
腾飞开源5 小时前
01_K8s干货笔记之认识K8s
运维·笔记·云原生·容器·kubernetes·k8s·容器化部署
小匠石钧知3 天前
02_在多个RockyLinux10虚拟机上安装k8s集群
云原生·容器·kubernetes·k8s
ShirleyWang0129 天前
让headlamp控制台能访问
linux·服务器·python·k8s·k3s
spider_xcxc9 天前
K8s 部署学习笔记
docker·容器·kubernetes·云计算·k8s
ShirleyWang0129 天前
Day02 K3s NGF(Nginx Gateway Fabric)单 Worker 环境网关更新与故障处置 SOP
linux·服务器·python·k8s·k3s
ShirleyWang0129 天前
DAY01 K3s 私有化部署排障复盘与 SOP
linux·服务器·k8s·k3s·企业部署
XUHUOJUN13 天前
AKS 不是安装在 Windows Server 上,而是运行在 Windows Server 之上的 Azure 平台能力
windows·架构·k8s·azure local·azure stack
Geek-Chow13 天前
Connecting kubectl to a Private EKS Cluster Over an Internal Domain
kubernetes·k8s·aws
spider_xcxc15 天前
Docker Compose 容器通信详解:同一个 Compose 文件才能互通吗?
docker·容器·k8s·容器化·容器网络
欢醉20 天前
k8s调用服务异常cannot allocate memory
kubernetes·k8s