场景描述
以k8s环境为例,在kubernetes集群中是通过CoreDNS来控制集群DNS流量走向。
在业务环境中,存在eth0、eth1、eth2等多个网络设备,其对应不同的网络出口。那如何将指定DNS流量进行分流呢?

大家可能想到的是:根据三元组信息(源IP、协议、出口IP)+ 代理转发的方式实现分流。
没错,下面按照这种思路实现~
简述下kubernetes集群中DNS请求流程:
- 进入pod centos 容器中,执行
nslookup www.baidu.com
命令 - 进行域名解析时,会首先找到容器中的
/etc/resolv.conf
文件,根据此文件会将dns服务器指向CoreDNS容器地址 - DNS请求到达CoreDNS后,会针对 www.baidu.com 域名解析,如果不是kubernetes集群域名,则DNS请求到CoreDNS forward插件
- forward插件会根据配置,读取kubernets宿主机中
/etc/resolv.conf
文件,将www.baidu.com域名通过公网DNS解析 - 此时CoreDNS容器网络环境与kubernets宿主机网络没连通,是利用hostPort将宿主机53端口映射到CoreDNS容器中,将DNS报文传输并通过互联网络解析完成。
我们了解CoreDNS解析流程后,会发现在第5步骤中:CoreDNS容器与Kubernetes宿主机网络没有连通,导致从CoreDNS没办法获取到宿主机的网络设备,无法按照我们预期进行DNS流量分流。
这里说明一下CoreDNS容器默认没有配置hostNetwork网络模式(hostNetwork代表继承宿主机的网络栈),所以对于CoreDNS容器来说是网络隔离的。
下面进入到咱们今天的话题《通过设置net命名空间进行网络分流》。
大致思路为:CoreDNS收到集群中dns请求后,forward插件在向公共DNS服务器发起 net.Dial()
网络请求前,将该线程上下文设置成kubernetes宿主机的net命名空间,那么此时 net.Dial()
网络请求就可以获取到宿主机所有的网络设备,达到网络分流。
第一步,将宿主机里命名空间文件挂在到CoreDNS容器中
执行kubectl edit deployment.apps -n kube-system coredns
命令,修改CoreDNS Deployment。增加内容如下:
yaml
volumes:
- name: host-namespace
hostPath:
path: /proc/1/ns
这里是将Linux系统中1号进程的命名空间目录挂进了CoreDNS容器中。 相应的将该目录映射到容器任意一个位置,比如创建/host-netns
目录。
第二步,修改CoreDNS项目源码实现DNS网络请求分流
在封装
dns.Client{}
结构体时,这里 netcardIP
变量可以理解为 eth0
网卡IP,代表从该网口作为出口。
重新封装dns.Client{}
后,调用dialInNamespace()
函数将本线程设置为宿主机的网络命名空间。

通过系统调用,将网络命名空间文件描述设置当前线程。

第三步,修改CoreDNS权限模式
这里需要将容器权限更改为特权模式,修改方式也是更改 CoreDNS Deployment
配置。
