共享 VPN 容器的网络命名空间,会让应用获得隧道中的接口和路由。但它 不会让应用获得隧道的解析器,也不会获得宿主机上其他任何东西的合并副本。这个缺口会造成一种非常具体的故障:公网域名能解析,内部域名不能,而失败的应用看起来像是在使用错误的 DNS 服务器,因为它确实如此。修复通常比诊断所暗示的更小。

第 1 步:从应用内部测试,而不是从宿主机

在有问题的容器内部执行查询,不要在宿主机上,也不要在浏览器里。 docker exec app-container getent hosts db.internal 只回答唯一重要的问题:这个进程的解析器返回什么。

如果宿主机能解析该名称而容器不能,你就找到了故障点。接下来的一切都是选择一种受支持的修复方式。

第 2 步:阅读运行时支持的模型

Docker 的网络文档 记录了共享另一个容器网络模式的容器在 DNS 标志方面的限制。在 Compose 中,network_mode: service:vpn 会把应用绑定到 VPN 容器的命名空间,而按服务设置的 dns 配置行为与普通服务不同。 你得到的解析结果,就是共享命名空间所拥有的那个。

用户定义的桥接网络使用 Docker 内嵌 DNS 的方式也与默认桥接不同。因此,从可用的桥接示例中复制来的 dns 配置段在共享栈中可能完全不起作用。复制之前请先确认部署结构。

环回地址始终指向相应的网络上下文。在共享命名空间中,127.0.0.1 并不是宿主机的解析器。

第 3 步:追踪查询的接收方

先读应用的解析器配置,再读应用自带的、特定于应用的加密 DNS,因为运行自有解析器的应用会忽略你编辑过的文件。然后检查所选解析器在 VPN 的路由和防火墙策略下是否可达。如果命名空间没有通往隧道另一侧解析器的路由,那个地址就毫无用处。编辑任何文件之前,先确认接收方。

浏览器里的 IP 检查页面无法告诉你域名查询去了哪里。它只能告诉你一条 HTTP 连接去了哪里。

第 4 步:分别修复两类故障

如果公网域名正常而服务名失败,问题在于容器网络内部的服务发现,而不是公网解析器列表。改掉所有公网 DNS 地址,也不会创建网络发现机制从未被要求创建的记录。

如果两类都失败,先从解析器的可达性入手。如果只有一类失败,先从发现机制入手。两者的归属不同,所以一次修复永远覆盖不了两者。

第 5 步:重建后重新测试

通过运行时支持的机制修改配置,然后重建容器,而不是在运行中的容器里改文件。测试一个新的私有名称、一个新的公网名称,以及查询之后的应用程序请求。/etc/resolv.conf 容器内的改动会在重建时消失,并让你误以为修复生效了。

把解析器的归属和预期命名空间记录在共享网络定义旁边。下一个人两者都需要。

相关文章