glibc,musl libc和go解析器之间的区别


前几天,我想在resolv.conf中调整超时,但是在Docker和Kubernetes这样的时代。即使仅使用Linux,解析器也不一定是glibc。因此,我研究了glibc,musl libc(高山)和go resolver之间的区别。

环境

我为每个容器使用了docker容器,并使用tcpdump检查了查询的状态。

1
2
macOS Catalina (10.15.7)
docker desktop 3.0.3 (51017)

容器图像为

1
2
alpine:3.12.3 / musl-1.1.24-r10
debian:buster-20201209 (10.7) / glibc 2.28-10

Go在Mac上使用1.15.6,交叉编译后在上面的高山上运行。
由于它是交叉编译的,因此默认情况下应为CGO_ENABLED=0,但我已明确构建了它。

支持状态,例如resolv.conf中的选项

resolv.conf的示例

1
2
3
4
nameserver 8.8.8.8
nameserver 8.8.4.4
search default.svc.cluster.local svc.cluster.local cluster.local asia-northeast1-b.c.project-id.internal c.project-id.internal google.internal
options ndots:5 timeout:5 attempts:2

resolve.conf中有各种选项,但是这三个解析器这次都支持

  • ndots
  • timeout
  • attempts

这是

的三个。

它支持search

nameserver
较早的musl libc不支持search(域完成),但是自1.1.13开始已受支持。
可以多次指定nameserver,但是只能使用3次。即使您指定4个或更多,也将仅使用3个。

让我们看看函数

的区别

圆点

首先,ndots,直到开始使用Kubernetes时我才知道。

您指定要查询的域中的点数(.),在Kubernetes中默认为5(ndots:5)。

例如ping www.google.com,点数为2。

点数为n点以上时为

如果

点的数量大于或等于ndots指定的数量,则将其确定为FQDN并查询DNS服务器,而无需完成search指定的域。现在,如果返回NXDOMAIN(没有这样的域),则glibc and go将查询search指定的域。但是,musl libc最后说,不进行补充查询就不会存在这样的域。

很久以前,musl最初并不支持域补全,因此从那时起一直在使用它的人们将始终通过FQDN指定它,因此这不是问题,但是如果您不这样做,知道,减少ndots即可解决它。您应该能够执行的域可能无法解析。

,如果点数少于ndots

如果

点的数量少于ndots,则对search指定的多个域进行补充和查询,以重复进行直到找到为止(我忘了检查上限)。重要的?但是,由ndots指定的数字比顺序更重要,并且5有很多。即使您指定FQDN service.namespace.svc.cluster.local,Kubernetes中的默认设置也是4。尝试完成search中列出的所有域后,您只需查询service.namespace.svc.cluster.local并最终获得所需的结果即使DNS服务器端缓存为负,但与DNS服务器的通信还是很多时代,所以很多浪费。 GKE默认设置最多枚举六个域。有四个EKS。缺省值是,即使存在更多无用的查询,也可以解析名称,而不是使ndots变小并创建无法解析的域。 ndots似乎还有调整的空间。

顺便说一句,如果在末尾添加.,则表示它是FQDN,因此,如果使用ping www.google.com.等,则没有解析程序将完成域。

名称服务器

glibc和musl之间指定多个服务器时的处理方式非常不同,因此我将首先对其进行说明。
glibc和Go查询第一个服务器,如果它们等待超时秒数并且没有响应,则查询下一个服务器。
musl libc同时将请求发送到多个名称服务器,并使用返回的第一个响应。如果指定了三个名称服务器,则将进行三倍的查询。

超时和尝试

timeoutattempts是相关的,应该一起对待。另外,它取决于nameserver的数量,因此我将对其进行总结。

如果有一个域名服务器

glibc和Go将引发查询并等待timeout秒,然后再次查询是否没有响应,总共进行attempts个查询。

musl libc等待每个查询timeout秒除以attempts。换句话说,包括重试在内,总共最多等待timeout秒。默认设置timeout:5 attempts:2每个等待2.5秒。

如果有两个名称服务器

glibc和Go查询第一个nameserver,等待timeout秒,然后查询第二个nameserver。这是attempts的一剂。 timeout:5 attempts:2的默认设置对每个nameserver查询两次,总共四次。每次等待5秒钟。

nameserver部分所述,

musl libc同时查询多个nameserver,因此与只有一个nameserver时相同。

如果有3个域名服务器

当有三个

nameserver时,glibc中timeout的行为会有所变化。等待第一个nameserver持续由timeout指定的秒数,第二个要比timeout指定的秒短,第三个要由timeout指定的秒(更长)。
对于timeout:5,分别为5秒,3秒和6秒。重复此设置attempts次。

Go总是等待timeout秒。

musl libc与以前相同。

超时时域完成

无论您查询多少次都超时,这并不重要,因为它不再有用,但我会写它,因为操作有所不同。

是否要使用

超时完成第二个及后续搜索域,glibc不会完成第二个及后续搜索域,但将查询未完成。 Go将尽职尽责地尝试所有域的完成情况,并且还会进行没有完成情况的查询。 musl libc查询第一个完成,仅此而已。没有完成就没有查询。

用于检查Go操作的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
    "context"
    "net"
    "os"
    "log"
)

func main() {
    ctx := context.Background()
    resolver := &net.Resolver{}
    for _, v := range os.Args[1:] {
        log.Printf("Resolving %s\n", v)
        names, err := resolver.LookupHost(ctx, v)
        if err != nil {
            log.Fatal(err)
        }
        for _, name := range names {
            log.Printf("%s\n", name)
        }
    }
}

可视化ndots的残暴程度:5

即使您用

句子编写它也很难理解,所以让我们看看当您尝试在GKE环境中访问storage.googleapis.com时会发生什么样的查询。使用default命名空间中设置的Pod执行ping storage.googleapis.com时,处理了tcpdump的输出。 (减小宽度)

令人震惊的是,每次您尝试访问Cloud Storage API时都会发生这种数量的查询。如果您不使用微服务进行Keep-Alive等操作,即使内部通信量很大,也可能会发生这种情况。如果您可以像JVM一样缓存DNS,那就太好了。

我想知道是否可以用FQDN完全指定

ndots:2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
14:53:34.706909 ?? A? storage.googleapis.com.default.svc.cluster.local. (66)
14:53:34.707130 ?? AAAA? storage.googleapis.com.default.svc.cluster.local. (66)
14:53:34.708881 ? NXDomain 0/1/0 (159)
14:53:34.708940 ? NXDomain 0/1/0 (159)
14:53:34.709051 ?? A? storage.googleapis.com.svc.cluster.local. (58)
14:53:34.709133 ?? AAAA? storage.googleapis.com.svc.cluster.local. (58)
14:53:34.709615 ? NXDomain 0/1/0 (151)
14:53:34.709689 ? NXDomain 0/1/0 (151)
14:53:34.709771 ?? A? storage.googleapis.com.cluster.local. (54)
14:53:34.709816 ?? AAAA? storage.googleapis.com.cluster.local. (54)
14:53:34.712211 ? NXDomain 0/1/0 (147)
14:53:34.712280 ? NXDomain 0/1/0 (147)
14:53:34.712387 ?? A? storage.googleapis.com.asia-northeast1-b.c.my-project-id.internal. (83)
14:53:34.712479 ?? AAAA? storage.googleapis.com.asia-northeast1-b.c.my-project-id.internal. (83)
14:53:34.716561 ? NXDomain 0/1/0 (189)
14:53:34.716623 ? NXDomain 0/1/0 (189)
14:53:34.716718 ?? A? storage.googleapis.com.c.my-project-id.internal. (65)
14:53:34.716760 ?? AAAA? storage.googleapis.com.c.my-project-id.internal. (65)
14:53:34.719891 ? NXDomain 0/1/0 (162)
14:53:34.720191 ? NXDomain 0/1/0 (162)
14:53:34.720304 ?? A? storage.googleapis.com.google.internal. (56)
14:53:34.720390 ?? AAAA? storage.googleapis.com.google.internal. (56)
14:53:34.724145 ? NXDomain 0/1/0 (145)
14:53:34.724352 ? NXDomain 0/1/0 (145)
14:53:34.724458 ?? A? storage.googleapis.com. (40)
14:53:34.724500 ?? AAAA? storage.googleapis.com. (40)
14:53:34.726930 ? 4/0/0 AAAA 2404:6800:4004:813::2010, AAAA 2404:6800:4004:81c::2010, AAAA 2404:6800:4004:81d::2010, AAAA 2404:6800:4004:81e::2010 (152)
14:53:34.726957 ? 16/0/0 A 172.217.161.80, A 172.217.175.16, A 172.217.175.48, A 172.217.175.80, A 172.217.175.112, A 216.58.197.144, A 172.217.25.208, A 172.217.25.240, A 172.217.26.48, A 172.217.31.176, A 172.217.161.48, A 172.217.174.112, A 172.217.175.240, A 216.58.220.112, A 216.58.197.208, A 216.58.197.240 (296)