kubernetes容器中域名解析优化

栏目: 编程工具 · 发布时间: 5年前

内容简介:一些需要解析外部DNS域名的应用,当运行在容器中时,如果我们在容器的network namespace中对dns报文(udp port 53)进行抓包,可能会发现在正确解析之前,还经过了若干次多余的尝试。下面是我在容器中可以看到,在最后(倒数第3、4行)正确解析之前,先是依次查询了下面几个域名,并且均查询了IPv4和IPv6:

多余的DNS查询

一些需要解析外部DNS域名的应用,当运行在容器中时,如果我们在容器的network namespace中对dns报文(udp port 53)进行抓包,可能会发现在正确解析之前,还经过了若干次多余的尝试。

下面是我在容器中 ping google.com ,同时在容器的network namespace中抓到的包。

sudo nsenter -t 3885 -n tcpdump -i eth0 udp port 53
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
10:09:11.917900 IP 10.244.2.202.38697 > 10.96.0.10.domain: 11858+ A? google.com.default.svc.cluster.local. (54)
10:09:11.918847 IP 10.96.0.10.domain > 10.244.2.202.38697: 11858 NXDomain*- 0/1/0 (147)
10:09:11.922468 IP 10.244.2.202.38697 > 10.96.0.10.domain: 15573+ AAAA? google.com.default.svc.cluster.local. (54)
10:09:11.923001 IP 10.96.0.10.domain > 10.244.2.202.38697: 15573 NXDomain*- 0/1/0 (147)
10:09:11.923248 IP 10.244.2.202.43230 > 10.96.0.10.domain: 62042+ A? google.com.svc.cluster.local. (46)
10:09:11.923828 IP 10.96.0.10.domain > 10.244.2.202.43230: 62042 NXDomain*- 0/1/0 (139)
10:09:11.924005 IP 10.244.2.202.43230 > 10.96.0.10.domain: 54769+ AAAA? google.com.svc.cluster.local. (46)
10:09:11.924494 IP 10.96.0.10.domain > 10.244.2.202.43230: 54769 NXDomain*- 0/1/0 (139)
10:09:11.924704 IP 10.244.2.202.36252 > 10.96.0.10.domain: 20727+ A? google.com.cluster.local. (42)
10:09:11.925154 IP 10.96.0.10.domain > 10.244.2.202.36252: 20727 NXDomain*- 0/1/0 (135)
10:09:11.925316 IP 10.244.2.202.36252 > 10.96.0.10.domain: 13066+ AAAA? google.com.cluster.local. (42)
10:09:11.925758 IP 10.96.0.10.domain > 10.244.2.202.36252: 13066 NXDomain*- 0/1/0 (135)
10:09:11.925929 IP 10.244.2.202.35582 > 10.96.0.10.domain: 38821+ A? google.com.lan. (32)
10:09:11.927244 IP 10.244.2.202.35582 > 10.96.0.10.domain: 4430+ AAAA? google.com.lan. (32)
10:09:11.927416 IP 10.96.0.10.domain > 10.244.2.202.35582: 38821 NXDomain 0/0/0 (32)
10:09:11.928600 IP 10.96.0.10.domain > 10.244.2.202.35582: 4430 NXDomain 0/0/0 (32)
10:09:11.928839 IP 10.244.2.202.45290 > 10.96.0.10.domain: 45577+ A? google.com. (28)
10:09:11.929129 IP 10.244.2.202.45290 > 10.96.0.10.domain: 37586+ AAAA? google.com. (28)
10:09:11.929303 IP 10.96.0.10.domain > 10.244.2.202.45290: 45577 1/0/0 A 172.217.160.78 (54)
10:09:11.929541 IP 10.96.0.10.domain > 10.244.2.202.45290: 37586 1/0/0 AAAA 2404:6800:4008:801::200e (66)

可以看到,在最后(倒数第3、4行)正确解析之前,先是依次查询了下面几个域名,并且均查询了IPv4和IPv6:

google.com.default.svc.cluster.local.
google.com.svc.cluster.local.
google.com.cluster.local.
google.com.lan.

但是这8次查询都失败了,因为并不存在这样的域名。

kubernetes的容器域名解析

要想解释上面的现象,需要先从kubernetes的容器域名解析开始讲。

kubernetes上运行的容器,其域名解析和一般的 Linux 一样,都是根据 /etc/resolv.conf 文件。如下是容器中该文件的内容。

nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local lan
options ndots:5

nameserver即为kubernetes集群中,kube-dns的svc IP,集群中容器的nameserver均设置为kube-dns。

kubectl get svc -n kube-system
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                  AGE
kube-dns         ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP,9153/TCP   236d

那么 searchndots 是做什么的呢?

search和ndots

在解释search和ndots之前,需要先了解一个概念:FQDN(Fully qualified domain name)。

FQDN是完整域名,一般来说,域名最终以 . 结束表示是FQDN,例如 google.com. 是FQDN,但 google.com 不是。

对FQDN,操作系统会直接查询DNS server。那么非FQDN呢?这里就要用到search和ndots了。

ndots 表示的是域名中必须出现的 . 的个数,如果域名中的 . 的个数不小于 ndots ,则该域名为一个FQDN,操作系统会直接查询;如果域名中的 . 的个数小于 ndots ,操作系统会在 search 搜索域中进行查询。

例如上面的例子, ndots 为5,查询的域名 google.com 不以 . 结尾,且 . 的个数少于5,因此操作系统会依此在 default.svc.cluster.local svc.cluster.local cluster.local lan 四个域中进行了搜索,其中前面3个搜索域是由kubernetes注入的,最后的 lan 是操作系统默认的搜索域。

ndots 默认值为1,也就是说,只要域名中有一个 . ,操作系统就会认为是绝对域名,直接查询。

ndots 上限为15。

ndots:n
	Sets a threshold for the number of dots which must appear in a name given to res_query(3) (see resolver(3)) before an initial absolute query will be made.  The default  for  n  is  1, meaning  that  if  there  are  any  dots  in a name, the name will be tried first as an absolute name before any search list elements are appended to it.  The value for this option is silently capped to 15.

kubernetes为什么使用搜索域

为什么呢?先来看看代码。

var (
	// The default dns opt strings.
	defaultDNSOptions = []string{"ndots:5"}
)
func (c *Configurer) generateSearchesForDNSClusterFirst(hostSearch []string, pod *v1.Pod) []string {
	if c.ClusterDomain == "" {
		return hostSearch
	}

	nsSvcDomain := fmt.Sprintf("%s.svc.%s", pod.Namespace, c.ClusterDomain)
	svcDomain := fmt.Sprintf("svc.%s", c.ClusterDomain)
	clusterSearch := []string{nsSvcDomain, svcDomain, c.ClusterDomain}

	return omitDuplicates(append(clusterSearch, hostSearch...))
}
func (c *Configurer) GetPodDNS(pod *v1.Pod) (*runtimeapi.DNSConfig, error) {
	...
	case podDNSCluster:
		if len(c.clusterDNS) != 0 {
			dnsConfig.Servers = []string{}
			for _, ip := range c.clusterDNS {
				dnsConfig.Servers = append(dnsConfig.Servers, ip.String())
			}
			dnsConfig.Searches = c.generateSearchesForDNSClusterFirst(dnsConfig.Searches, pod)
			dnsConfig.Options = defaultDNSOptions
			break
		}
	...
	if utilfeature.DefaultFeatureGate.Enabled(features.CustomPodDNS) && pod.Spec.DNSConfig != nil {
		dnsConfig = appendDNSConfig(dnsConfig, pod.Spec.DNSConfig)
	}
}

kubernetes搜索域

从函数 generateSearchesForDNSClusterFirst 中可见,搜索域有三个:nsSvcDomain、svcDomain、clusterDomain。

kubernetes之所以要设置搜索域,目的是为了方便用户访问service。

例如,default namespace下的Pod a,如果访问同namespace下的service b,直接使用 b 就可以访问了,而这个功能,就是通过nsSvcDomain搜索域 default.svc.cluster.local 完成的。

类似的,对于不同namespace下的service,可以用 ${service name}.${namespace name} 来访问,是通过svcDomain搜索域完成的。

clusterDomain设计的目的,是为了方便同域中非kubernetes上的域名访问,例如设置kubernetes的domain为 ieevee.com ,那么对于 s.ieevee.com 域名,直接使用 s 来访问就可以了,当然前提是当前namespace中没有一个叫做 s 的svc。(是的。搜索域是有优先级的)

ndots默认值

ndots 默认值是写死的,5。

为什么是5呢?

thockin在 issue 33554 中做了解释,概况来说:

  1. kubernetes需要支持同namespace下service快速访问,例如 name ,因此 ndots>=1,对应搜索域 $namespace.svc.$zone
  2. kubernetes需要支持跨namespace下service快速访问,例如 kubernetes.default ,因此 ndots>=2,对应搜索域 svc.$zone
  3. kubernetes需要支持同namespace、跨namespace下,非service名称的快速访问,例如 name.namespace.svc ,因此 ndots>=3,对应搜索域 $zone
  4. kubernetes需要支持statefulset中的每个pod的访问,例如 mysql-0.mysql.default.svc ,因此 ndots>=4
  5. kubernetes需要支持SRV records( _$port._$proto.$service.$namespace.svc.$zone ),因此 ndots>=5

不过呢,如果你的使用情况并不像上面这么复杂,这个值可能并不适合你。

比如说,我们只会用到同namespace下(形如 a )、跨namespace下的service访问(形如 a.b ),因此,ndots默认值为2更合适,但该值是写死在代码中的,不支持定制化,但可以通过下面的方法修改。

ndots修改

ndots是可以被修改的,可以通过 pod.Spec.DNSConfig 改写。

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsConfig:
    options:
      - name: ndots
        value: "2"

通过上面的修改,我们再来看容器中的DNS报文,就只有下面几条了。

sudo nsenter -t 3885 -n tcpdump -i eth0 udp port 53
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
10:30:35.917282 IP 10.244.2.202.39480 > 10.96.0.10.domain: 60870+ A? google.com. (28)
10:30:35.919194 IP 10.244.2.202.39480 > 10.96.0.10.domain: 9908+ AAAA? google.com. (28)
10:30:35.927047 IP 10.96.0.10.domain > 10.244.2.202.39480: 60870 1/0/0 A 216.58.200.238 (54)
10:30:35.929089 IP 10.96.0.10.domain > 10.244.2.202.39480: 9908 1/0/0 AAAA 2404:6800:4008:801::200e (66)

这对于应用的性能会有一定的提升,具体可以参见Ref。

dns cache

我们观察一些 python 类型的容器应用,会发现它们会发出非常多的DNS请求,几乎每次涉及到域名都需要发出DNS请求;但是对于一些 Java 应用,会发现它们发出的DNS请求非常规律,一般是每30秒一个。

这是因为,不同语言对于dns cache的处理不同了。

目前来看,只有Java做了dns cache。从JDK 1.6开始,Java默认会对DNS做缓存,主要是以下两个配置:

  • networkaddress.cache.ttl:域名解析成功后,DNS缓存时间,默认是30秒
  • networkaddress.cache.negative.ttl:域名解析失败后,冷却时间,默认是10秒

其他语言可以支持,但都需要一定hack。

总结

本文从一个多余的DNS查询现象开始,介绍了FQDN和DNS的搜索域,回答了kubernetes为什么需要搜索域这个问题,并且提出了一个解决多余DNS查询的方案,以及介绍了不同语言对于dns cache的处理情况。

Ref:


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

老二非死不可

老二非死不可

方三文 / 机械工业出版社 / 2013-12 / 39.00

关于投资 价值投资者为啥都买茅台? 怎样识别好公司与坏公司? 做空者真的罪大恶极吗? 国际板对A股会有什么影响? 波段操作,止损割肉到底靠不靠谱? IPO真的是A股萎靡不振的罪魁祸首吗? 关于商业 搜狐的再造战略有戏吗? 新浪如何焕发第二春? 百度的敌人为什么是它自己? 我为什么比巴菲特早两年投资比亚迪? 民族品牌这张牌还靠谱......一起来看看 《老二非死不可》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器