开拓新领域 - Istio 中的智能 DNS 代理
工作负载本地 DNS 解析,简化虚拟机集成、多集群等。
DNS 解析是 Kubernetes 上任何应用程序基础架构的重要组成部分。当您的应用程序代码尝试访问 Kubernetes 集群中的其他服务,甚至互联网上的服务时,它必须首先查找与服务主机名对应的 IP 地址,然后才能启动与该服务的连接。此名称查找过程通常称为**服务发现**。在 Kubernetes 中,集群 DNS 服务器(无论是kube-dns
还是 CoreDNS)会将服务的域名解析为唯一的不可路由虚拟 IP(VIP),如果服务类型为clusterIP
。每个节点上的kube-proxy
将此 VIP 映射到服务的一组 Pod,并将流量转发到其中随机选择的一个 Pod。当使用服务网格时,sidecar 在流量转发方面的工作方式与kube-proxy
类似。
下图描述了 DNS 在当今的角色
DNS 带来的问题
虽然 DNS 在服务网格中的作用可能看起来微不足道,但它一直阻碍着将网格扩展到虚拟机并实现无缝的多集群访问。
虚拟机访问 Kubernetes 服务
考虑一个带有 sidecar 的虚拟机的情况。如下所示,虚拟机上的应用程序查找 Kubernetes 集群内部服务的 IP 地址,因为它们通常无法访问集群的 DNS 服务器。
如果愿意使用一些涉及dnsmasq
和使用NodePort
服务外部公开kube-dns
的复杂变通方法,则从技术上讲可以在虚拟机上使用kube-dns
作为名称服务器:假设您设法说服您的集群管理员这样做。即使这样,您也打开了通往一系列安全问题的大门。归根结底,这些是针对那些组织能力和领域专业知识有限的人员而言通常超出范围的点解决方案。
没有 VIP 的外部 TCP 服务
不仅网格中的虚拟机受到 DNS 问题的影响。为了使 sidecar 能够准确地区分网格外两个不同 TCP 服务之间的流量,这些服务必须位于不同的端口上,或者它们需要拥有全局唯一的 VIP,就像分配给 Kubernetes 服务的clusterIP
一样。但如果没有 VIP 怎么办?云托管服务(如托管数据库)通常没有 VIP。相反,提供商的 DNS 服务器会返回可以由应用程序直接访问的实例 IP 之一。例如,考虑以下两个服务条目,它们指向两个不同的 AWS RDS 服务
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: db1
namespace: ns1
spec:
hosts:
- mysql-instance1.us-east-1.rds.amazonaws.com
ports:
- name: mysql
number: 3306
protocol: TCP
resolution: DNS
---
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: db2
namespace: ns1
spec:
hosts:
- mysql-instance2.us-east-1.rds.amazonaws.com
ports:
- name: mysql
number: 3306
protocol: TCP
resolution: DNS
sidecar 在0.0.0.0:3306
上有一个监听器,它从公共 DNS 服务器查找mysql-instance1.us-east1.rds.amazonaws.com
的 IP 地址并将其转发到该地址。它无法将流量路由到db2
,因为它无法区分到达0.0.0.0:3306
的流量是发往db1
还是db2
。完成此操作的唯一方法是将解析设置为NONE
,导致 sidecar盲目转发端口3306
上的任何流量到应用程序请求的原始 IP。这类似于在防火墙上打一个洞,允许所有流量通过端口3306
,而不管目标 IP 是什么。为了使流量流动,您现在被迫牺牲系统的安全态势。
解析远程集群中服务的 DNS
多集群网格的 DNS 限制众所周知。一个集群中的服务无法查找其他集群中服务的 IP 地址,除非使用笨拙的变通方法,例如在调用者命名空间中创建存根服务。
控制 DNS
总而言之,DNS 一直是 Istio 中一个棘手的问题。是时候解决这个难题了。我们(Istio 网络团队)决定一劳永逸地解决这个问题,以一种对您(最终用户)完全透明的方式。我们的第一次尝试涉及利用 Envoy 的 DNS 代理。事实证明,它非常不可靠,而且由于 Envoy 使用的 c-ares DNS 库普遍缺乏复杂性,因此总体上令人失望。决心解决这个问题,我们决定在用 Go 编写的 Istio sidecar 代理中实现 DNS 代理。我们能够优化实现以处理我们想要解决的所有场景,而不会影响可扩展性和稳定性。我们使用的 Go DNS 库与 CoreDNS、Consul、Mesos 等可扩展 DNS 实现使用的库相同。它已在生产环境中经过实战检验,具有可扩展性和稳定性。
从 Istio 1.8 开始,sidecar 上的 Istio 代理将附带一个缓存 DNS 代理,由 Istiod 动态编程。Istiod 会根据集群中的 Kubernetes 服务和服务条目推送应用程序可能访问的所有服务的域名到 IP 地址映射。应用程序的 DNS 查询会被透明地拦截并由 Pod 或虚拟机中的 Istio 代理提供服务。如果查询针对网格内的服务,无论服务位于哪个集群,代理都会直接响应应用程序。如果不是,它会将查询转发到/etc/resolv.conf
中定义的上游名称服务器。下图描述了当应用程序尝试使用其主机名访问服务时发生的交互。
正如您将在以下部分中看到的,DNS 代理功能对 Istio 的许多方面产生了巨大的影响。
减少 DNS 服务器负载并加速解析
集群的 Kubernetes DNS 服务器的负载会大幅下降,因为几乎所有 DNS 查询都由 Istio 在 Pod 内解析。网格在集群上的占用空间越大,DNS 服务器的负载就越小。在 Istio 代理中实现我们自己的 DNS 代理使我们能够实现诸如CoreDNS 自动路径之类的酷炫优化,而无需 CoreDNS 目前面临的正确性问题。
为了理解此优化的影响,让我们以一个简单的 DNS 查询场景为例,在一个标准的 Kubernetes 集群中,没有为 Pod 进行任何自定义 DNS 设置,即在/etc/resolv.conf
中使用默认设置ndots:5
。当您的应用程序启动对productpage.ns1.svc.cluster.local
的 DNS 查询时,它会将/etc/resolv.conf
中的 DNS 搜索命名空间(例如,ns1.svc.cluster.local
)作为 DNS 查询的一部分附加,然后按原样查询主机。结果,实际发出的第一个 DNS 查询将类似于productpage.ns1.svc.cluster.local.ns1.svc.cluster.local
,当 Istio 未参与时,这将不可避免地导致 DNS 解析失败。如果您的/etc/resolv.conf
有 5 个搜索命名空间,则应用程序将为每个搜索命名空间发送两个 DNS 查询,一个用于 IPv4 的A
记录,另一个用于 IPv6 的AAAA
记录,然后使用代码中使用的精确主机名发送最后一对查询。在建立连接之前,应用程序对每个主机执行 12 次 DNS 查询!
使用 Istio 实现的 CoreDNS 样式自动路径技术,sidecar 代理将在第一个查询中检测到正在查询的真实主机名,并作为此 DNS 响应的一部分将cname
记录返回到productpage.ns1.svc.cluster.local
,以及productpage.ns1.svc.cluster.local
的A/AAAA
记录。接收此响应的应用程序现在可以立即提取 IP 地址并继续建立到该 IP 的 TCP 连接。Istio 代理中的智能 DNS 代理将 DNS 查询的数量从 12 个大幅减少到仅 2 个!
虚拟机到 Kubernetes 集成
由于 Istio 代理对网格内的服务执行本地 DNS 解析,因此虚拟机对 Kubernetes 服务的 DNS 查询现在将成功,而无需为在集群外部公开kube-dns
而使用笨拙的变通方法。现在,能够无缝解析集群中的内部服务将简化您的单体到微服务的迁移过程,因为虚拟机上的单体现在可以访问 Kubernetes 上的微服务,而无需通过 API 网关进行额外的间接访问。
自动分配 VIP(如果可能)
您可能会问,代理中的此 DNS 功能如何解决在同一端口上区分多个没有 VIP 的外部 TCP 服务的问题?
受到 Kubernetes 的启发,Istio 现在将自动为这些服务分配不可路由 VIP(来自 E 类子网),只要它们不使用通配符主机即可。sidecar 上的 Istio 代理将使用 VIP 作为对应用程序 DNS 查询的响应。Envoy 现在可以清楚地区分发往每个外部 TCP 服务的流量并将其转发到正确的目标。随着 DNS 代理的引入,您将不再需要为非通配符 TCP 服务使用resolution: NONE
,从而改善您的整体安全态势。Istio 对通配符外部服务(例如,*.us-east1.rds.amazonaws.com
)无能为力。您必须采用 NONE 解析模式来处理此类服务。
多集群 DNS 查询
对于那些冒险尝试构建多集群网格的人来说,应用程序直接调用远程集群中命名空间的内部服务,DNS 代理功能非常有用。您的应用程序可以解析任何集群中任何命名空间中的 Kubernetes 服务,而无需在每个集群中创建存根 Kubernetes 服务。
DNS 代理的好处超出了 Istio 目前描述的多集群模型。在 Tetrate,我们在客户的多集群部署中广泛使用此机制,以使 sidecar 能够解析网格中所有集群的入口网关公开的主机的 DNS,并通过双向 TLS 访问它们。
总结
当涉及到跨多个集群、不同环境构建网格以及集成外部服务时,缺乏对 DNS 的控制所带来的问题常常被忽视和完全忽略。在 Istio sidecar 代理中引入缓存 DNS 代理解决了这些问题。控制应用程序的 DNS 解析允许 Istio 准确识别流量绑定的目标服务,并在集群内部和跨集群增强 Istio 的整体安全、路由和遥测态势。
Istio 1.8 的preview
配置文件中启用了智能 DNS 代理。请试一试!