Linux DNS 查询剖析

作者 : 开心源码 本文共2483个字,预计阅读时间需要7分钟 发布时间: 2022-05-12 共205人阅读

我将详情容器如何完成 DNS 查询。你想的没错,也不是那么简单。

1) Docker 和 DNS

在 Linux DNS 查询剖析(第三部分)[3] 中,我们详情了 dnsmasq,其工作方式如下:将 DNS 查询指向到 localhost 地址 127.0.0.1,同时启动一个进程监听 53 端口并解决查询请求。

在按上述方式配置 DNS 的主机上,假如运行了一个 Docker 容器,容器内的 /etc/resolv.conf 文件会是怎么的呢?

我们来动手实验一下吧。

按照默认 Docker 创立流程,可以看到如下的默认输出:

  1. $ docker run ubuntu cat /etc/resolv.conf
  2. # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
  3. # DO NOT EDIT THIS FILE BY HAND — YOUR CHANGES WILL BE OVERWRITTEN
  4. # 127.0.0.53 is the systemd-resolved stub resolver.
  5. # run “systemd-resolve –status” to see details about the actual nameservers.
  6. search home
  7. nameserver 8.8.8.8
  8. nameserver 8.8.4.4

奇怪!

地址 8.8.8.8 和 8.8.4.4 从何而来呢?

当我思考容器内的 /etc/resolv.conf 配置时,我的第一反应是继承主机的 /etc/resolv.conf。但只需略微进一步分析,就会发现这样并不总是有效的。

假如在主机上配置了 dnsmasq,那么 /etc/resolv.conf 文件总会指向 127.0.0.1这个回环地址loopback address。假如这个地址被容器继承,容器会在其本身的网络上下文networking context中使用;因为容器内并没有运行(在 127.0.0.1 地址的)DNS 服务器,因而 DNS 查询都会失败。

“有了!”你可能有了新主意:将 主机的 的 IP 地址用作 DNS 服务器地址,其中这个 IP 地址可以从容器的默认路由default route中获取:

  1. root@79a95170e679:/# ip route
  2. default via 172.17.0.1 dev eth0
  3. 172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2

使用主机 IP 地址真的可行吗?

从默认路由中,我们可以找到主机的 IP 地址 172.17.0.1,进而可以通过手动指定 DNS 服务器的方式进行测试(你也可以升级 /etc/resolv.conf 文件并使用 ping 进行测试;但我觉得这里很适合详情新的 dig 工具及其 @ 参数,后者用于指定需要查询的 DNS 服务器地址):

  1. root@79a95170e679:/# dig @172.17.0.1 google.com | grep -A1 ANSWER.SECTION
  2. ;; ANSWER SECTION:
  3. google.com. 112 IN A 172.217.23.14

但是还有一个问题,这种方式仅适用于主机配置了 dnsmasq 的情况;假如主机没有配置 dnsmasq,主机上并不存在用于查询的 DNS 服务器。

在这个问题上,Docker 的处理方案是忽略所有可能的复杂情况,即无论主机中使用什么 DNS 服务器,容器内都使用 Google 的 DNS 服务器 8.8.8.8 和 8.8.4.4 完成 DNS 查询。

我的经历:在 2013 年,我遇到了使用 Docker 以来的第一个问题,与 Docker 的这种 DNS 处理方案密切相关。我们公司的网络屏蔽了 8.8.8.8 和 8.8.4.4,导致容器无法解析域名。

这就是 Docker 容器的情况,但对于包括 Kubernetes 在内的容器 编排引擎orchestrators,情况又有些不同。

2) Kubernetes 和 DNS

在 Kubernetes 中,最小部署单元是 pod;它是一组相互协作的容器,共享 IP 地址(和其它资源)。

Kubernetes 面临的一个额外的挑战是,将 Kubernetes 服务请求(例如,myservice.kubernetes.io)通过对应的解析器resolver,转发到具体服务地址对应的内网地址private network。这里提到的服务地址被称为归属于“集群域cluster domain”。集群域可由管理员配置,根据配置可以是 cluster.local 或者 myorg.badger 等。

在 Kubernetes 中,你可以为 pod 指定如下四种 pod 内 DNS 查询的方式。

Default

在这种(名称容易让人误会)的方式中,pod 与其所在的主机采用相同的 DNS 查询路径,与前面详情的主机 DNS 查询一致。我们说这种方式的名称容易让人误会,由于该方式并不是默认选项!ClusterFirst 才是默认选项。

假如你希望覆盖 /etc/resolv.conf 中的条目,你可以增加到 kubelet 的配置中。

ClusterFirst

在 ClusterFirst 方式中,遇到 DNS 查询请求会做有选择的转发。根据配置的不同,有以下两种方式:

第一种方式配置相对古老但更简明,即采用一个规则:假如请求的域名不是集群域的子域,那么将其转发到 pod 所在的主机。

第二种方式相对新少量,你可以在内部 DNS 中配置选择性转发。

下面给出示例配置并从 Kubernetes 文档[4]中选取一张图说明流程:

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: kube-dns
  5. namespace: kube-system
  6. data:
  7. stubDomains: |
  8. {“acme.local”: [“1.2.3.4”]}
  9. upstreamNameservers: |
  10. [“8.8.8.8”, “8.8.4.4”]

在 stubDomains 条目中,可以为特定域名指定特定的 DNS 服务器;而 upstreamNameservers 条目则给出,待查询域名不是集群域子域情况下用到的 DNS 服务器。

这是通过在一个 pod 中运行我们熟知的 dnsmasq 实现的。

Linux DNS 查询剖析

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Linux DNS 查询剖析

发表回复