Envoy DNS Resolver 扩展详解:c-ares、Apple、getaddrinfo 与 Hickory DNS 的配置与原理
2026/9/13 13:30:45 网站建设 项目流程

Envoy DNS Resolver 扩展详解:c-ares、Apple、getaddrinfo 与 Hickory DNS 的配置与原理

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

Envoy 通过可插拔的 DNS resolver 扩展机制为集群服务发现(strict DNS / logical DNS)、动态正向代理(Dynamic Forward Proxy)和 UDP DNS Filter 等组件提供域名解析能力,默认使用 c-ares 库,同时内置 Apple(iOS/macOS)、getaddrinfo 与 Hickory DNS 三套可选实现。本文以docs/root/api-v3/config/dns_resolver/dns_resolver.rst索引所覆盖的四个 DNS resolver 扩展为骨架,结合api/envoy/extensions/network/dns_resolver/下的 proto 定义、source/extensions/network/dns_resolver/下的实现源码与动态正向代理的完整配置示例,系统讲解各解析器的配置字段、默认值、适用场景与运行统计,帮助读者掌握在集群、动态正向代理缓存等场景中定制 DNS 解析行为的完整方法。

一、Envoy 的 DNS 解析扩展机制

Envoy 中多个组件都会触发 DNS 解析:不同集群类型(strict DNS、logical DNS)、动态正向代理系统(由集群与 HTTP Filter 组合而成)、UDP DNS Filter 等。为了统一管理这些场景的解析行为,Envoy 将 DNS 解析抽象为可插拔扩展,每个扩展通过工厂注册表(Registry)注册,配置时使用TypedExtensionConfig按名字加载对应的 typed config。相关架构说明见 DNS Resolution 架构文档。

从源码source/common/network/dns_resolver/dns_factory_util.cc可以确认默认行为:makeDefaultDnsResolverConfig会优先尝试 Apple API(受 runtime 特性envoy.restart_features.use_apple_api_for_dns_lookups控制),否则回退到 c-ares,即c-ares 是 Envoy 的默认 DNS 解析库;在 Apple 系操作系统上,可通过 runtime 特性启用 Apple 原生 API。

1.1 内置的四个 DNS resolver 扩展

Envoy 内置了四个 DNS resolver 扩展,其 proto 定义分别位于:

扩展名(typed_dns_resolver_config.nameproto 定义文件核心特性
envoy.network.dns_resolver.carescares_dns_resolver.proto默认实现,基于 c-ares,功能最丰富
envoy.network.dns_resolver.appleapple_dns_resolver.proto仅 iOS/macOS,基于 Apple 系统 API
envoy.network.dns_resolver.getaddrinfogetaddrinfo_dns_resolver.proto调用系统getaddrinfo(),独立线程执行
envoy.network.dns_resolver.hickoryhickory_dns_resolver.proto纯 Rust 实现,支持 DoT / DoH / DNSSEC

其中 Hickory DNS 是基于 Hickory DNS 的纯 Rust 解析器,通过动态模块框架(dynamic modules framework)集成,运行在独立的 Tokio runtime 线程上,与 Envoy 的事件循环线程隔离,因此 DNS 解析不会阻塞 dispatcher 线程。

二、DNS 解析器的配置入口

DNS resolver 的 typed config 通过typed_dns_resolver_config字段注入,主要出现在三个位置:

  1. Cluster 级别Cluster.typed_dns_resolver_config(替换旧的Cluster.dns_resolution_config),见 dns_cluster.proto 中的说明;
  2. DNS 集群扩展DnsCluster.typed_dns_resolver_config,当集群通过cluster_type使用envoy.clusters.dns扩展时,该字段优先级最高——若它与 Cluster 上的 resolver 配置同时存在,Envoy 会采用此处的配置并忽略 Cluster 中的相关字段;
  3. 动态正向代理 DNS 缓存DnsCacheConfig.typed_dns_resolver_config,Filter 与 Cluster 必须配置相同的 DNS 缓存参数才能协同工作。

2.1 通用解析选项:DnsResolverOptions 与 DnsResolutionConfig

api/envoy/config/core/v3/resolver.proto中定义了两个通用消息:

  • DnsResolverOptions控制解析器的基础行为,包含两个布尔开关:
    • use_tcp_for_dns_lookups:所有 DNS 查询改用 TCP 而非默认的 UDP;
    • no_default_search_domain:不使用系统默认搜索域,仅按主机名原样或别名查询。
  • DnsResolutionConfig是较早期的配置形态,包含resolvers(DNS 解析器地址列表,至少 1 项)与dns_resolver_options。若未指定resolvers,则使用系统默认解析器(如 Unix 下的/etc/resolv.conf)。该字段现已逐渐被typed_dns_resolver_config取代。

三、c-ares 解析器(默认):CaresDnsResolverConfig

c-ares 是 Envoy 的默认解析器,其配置消息CaresDnsResolverConfig定义于 cares_dns_resolver.proto,字段编号已用到 12,属于[#next-free-field: 13]。各字段说明如下:

字段类型默认值说明
resolvers重复的config.core.v3.Address系统默认DNS 解析器地址列表;是否覆盖系统默认值取决于use_resolvers_as_fallback
use_resolvers_as_fallbackboolfalsetrue时,仅在 c-ares 无法从系统(如/etc/resolv.conf)获取 nameserver 时才使用resolvers;否则resolvers将覆盖系统默认解析器
filter_unroutable_familiesboolfalse查询可用网络接口,若某个 IP 族(IPv4/IPv6)没有任何可用接口,则从结果中过滤该族的地址
dns_resolver_optionsconfig.core.v3.DnsResolverOptions复用通用选项(TCP 查询、禁用搜索域)
udp_max_queriesUInt32Value限制基于 UDP 的 DNS 查询数量上限(当前仅 c-ares 适用)
query_timeout_secondsUInt64ValueEnvoy 默认5每个 nameserver 首次应答的超时秒数;注意 c-ares 库默认 2 秒,Envoy 未设置时默认 5 秒,这是为了保持旧行为、避免用户反馈的解析耗时上升。校验规则要求>= 1
query_triesUInt32ValueEnvoy 默认4放弃前的最大查询尝试次数,每次尝试可能使用不同 nameserver;c-ares 库默认 3 次,Envoy 未设置时默认 4 次。校验规则要求>= 1
rotate_nameserversboolfalse开启后按轮询方式选择 nameserver,分散查询负载;关闭(默认)时按配置顺序依次尝试。该设置会覆盖系统对 nameserver 轮转的配置
edns0_max_payload_sizeUInt32Valuec-ares 内部默认(通常 1232)EDNS0 UDP 负载上限(字节)。设置后 c-ares 会在查询中携带 EDNS0,并用该值作为最大 UDP 响应大小。推荐值:1232(安全默认,避免分片)、4096(最大值)。校验规则要求512 <= x <= 4096
max_udp_channel_durationDuration不设置则不刷新若设置,解析器会周期性重新初始化 c-ares channel,避免陈旧 socket 状态、改善 UDP 端口负载分布
reinit_channel_on_timeoutboolfalse当 DNS 查询以ARES_ETIMEOUT失败时重新初始化 c-ares channel,帮助从 UDP socket 不可用等罕见故障中恢复。若超时源于间歇性网络问题,开启可能增加 channel 重建频率,可考虑改用max_udp_channel_duration做周期刷新
qcache_max_ttlUInt32ValueEnvoy 默认0(关闭查询缓存)c-ares 查询缓存的最大 TTL(秒)。设为非 0 时启用缓存,并遵守 DNS 响应中的 TTL(不超过该上限)。c-ares 库默认缓存 1 小时,而 Envoy 默认关闭

3.1 实现细节与统计指标

c-ares 解析器的实现位于 source/extensions/network/dns_resolver/cares/dns_impl.h 与dns_impl.cc。其统计宏ALL_CARES_DNS_RESOLVER_STATS定义了 6 项指标,挂在dns.cares统计树(stats tree)下:

名称类型说明
resolve_totalCounterDNS 查询总数
pending_resolutionsGauge进行中的 DNS 查询数
not_foundCounter返回NXDOMAINNODATA的查询数
get_addr_failureCounter查询期间的一般性失败次数
timeoutsCounter超时查询数
reinitsCounterc-ares channel 重新初始化次数

实现上,DnsResolverImpl的所有调用与回调都发生在创建它的 dispatcher 线程上;c-ares 仅支持 channel 级取消,因此PendingResolution::cancel只是标记cancelled_并跳过回调,网络事件仍会继续(见dns_impl.hPendingResolution的实现注释)。

四、Apple 解析器:AppleDnsResolverConfig

Apple 解析器仅适用于 iOS/macOS 平台,配置消息定义于 apple_dns_resolver.proto,仅有一个字段:

  • include_unroutable_families(bool,默认false):开启后绕过系统"仅返回可路由的 IPv4/IPv6 地址"的启发式逻辑,返回所有可能的地址。该设置在 DNS 查询族被限定为 v4-only 或 v6-only 时会被忽略。绝大多数场景应保持false,但在自行过滤地址(例如实现 Happy Eyeballs)时可能有用。

其统计指标挂在dns.apple统计树下(见 DNS Resolution 架构文档):

名称类型说明
connection_failureCounter连接 DNS 服务器失败的次数
get_addr_failureCounter调用 GetAddrInfo API 时的一般性失败次数
network_failureCounter网络连通性导致的失败次数
processing_failureCounter处理 DNS 服务器返回数据时的失败次数
socket_failureCounter获取连接 DNS 服务器的 socket 文件描述符失败的次数
timeoutCounter超时查询数

在启用 Apple 解析器时需要借助 runtime 特性envoy.restart_features.use_apple_api_for_dns_lookups(参见 dns_factory_util.cc 中tryUseAppleApiForDnsLookups的逻辑)。

五、getaddrinfo 解析器:GetAddrInfoDnsResolverConfig

getaddrinfo 解析器直接调用系统getaddrinfo()函数解析主机名,配置消息定义于 getaddrinfo_dns_resolver.proto:

  • num_retriesUInt32Value):放弃前的重试次数;未指定时解析器会无限重试,直到成功或 DNS 查询超时。
  • num_resolver_threadsUInt32Value):用于解析待处理 DNS 查询的线程数;未指定时使用 1 个线程。

该消息的文档中有两处重要提示:

  1. 解析结果使用硬编码 60 秒 TTL:因为getaddrinfo()API 不提供真实 TTL,目前固定为 60 秒,未来如需可再增加配置;
  2. 实现层面,从 getaddrinfo.h 的注释可见,该解析器在专用解析线程上调用getaddrinfo()目前只适合相对低频率的解析场景(未来可扩展为线程池)。

此外,getaddrinfo 解析器当前不产生任何解析器专属统计指标

六、Hickory DNS 解析器:HickoryDnsResolverConfig

Hickory DNS 是 Envoy 中较新的纯 Rust 解析器,支持标准 DNS(UDP/TCP)、DNS-over-TLS(DoT)、DNS-over-HTTPS(DoH)以及 DNSSEC 校验。配置消息定义于 hickory_dns_resolver.proto:

字段类型默认值说明
resolvers重复的config.core.v3.Address系统配置标准 UDP/TCP 解析的 DNS 地址列表;未指定且use_system_config未显式设为false时,使用系统配置(Unix 下/etc/resolv.conf
dns_over_tlsDnsOverTlsConfigDoT 配置,指定后查询将经 TLS 发送至配置的服务器
dns_over_httpsDnsOverHttpsConfigDoH 配置,指定后查询将经 HTTPS 发送至配置的端点
enable_dnssecboolfalse启用 DNSSEC 校验,验证签名并拒绝校验失败的响应
cache_sizeUInt32Value1024DNS 响应缓存条目上限,LRU 淘汰策略,支持负缓存(缓存NXDOMAIN/NODATA响应)
num_resolver_threadsUInt32Value2异步解析所用 Tokio runtime 线程数,最大16,每个解析器实例运行自己的 Tokio runtime
use_system_configBoolValue未配置resolvers/dns_over_tls/dns_over_https时为true是否读取系统 DNS 配置(nameserver 与搜索域);同时指定resolvers时以resolvers优先
query_timeoutDuration5单次查询尝试的超时时间,校验要求>= 1ms
query_triesUInt32Value3放弃前的最大查询尝试次数,每次可能使用不同 nameserver,校验要求>= 1

其中两个子消息:

  • DnsOverTlsConfigservers(DoT 服务器地址列表,端口通常为 853)+tls_server_name(TLS 校验使用的 SNI 主机名,指定servers时必填,至少 1 个字符);
  • DnsOverHttpsConfigserver_urls(DoH 端点 URL 列表,如https://dns.google/dns-query,每个 URL 至少 1 个字符)。

Hickory 解析器的统计指标挂在dns.hickory统计树下,包括resolve_total(完成的查询数)、pending_resolutions(进行中查询数)、not_foundget_addr_failuretimeouts。其实现位于 source/extensions/network/dns_resolver/hickory/hickory_dns_impl.cc。

七、完整配置示例:动态正向代理中的 typed_dns_resolver_config

官方文档 dynamic_forward_proxy_filter.rst 给出了将 c-ares 解析器配置到动态正向代理 DNS 缓存的完整示例(源文件为 dns-cache-circuit-breaker.yaml)。其中 Filter 与 Cluster 通过同名的dns_cache_configdynamic_forward_proxy_cache_config)关联,并在typed_dns_resolver_config中指定envoy.network.dns_resolver.cares及其参数:

admin: address: socket_address: protocol: TCP address: 127.0.0.1 port_value: 9901 static_resources: listeners: - name: listener_0 address: socket_address: protocol: TCP address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: prefix: "/force-host-rewrite" route: cluster: dynamic_forward_proxy_cluster typed_per_filter_config: envoy.filters.http.dynamic_forward_proxy: "@type": type.googleapis.com/envoy.extensions.filters.http.dynamic_forward_proxy.v3.PerRouteConfig host_rewrite_literal: www.example.org - match: prefix: "/" route: cluster: dynamic_forward_proxy_cluster http_filters: - name: envoy.filters.http.dynamic_forward_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig dns_cache_config: name: dynamic_forward_proxy_cache_config dns_lookup_family: V4_ONLY dns_cache_circuit_breaker: max_pending_requests: 1024 typed_dns_resolver_config: name: envoy.network.dns_resolver.cares typed_config: "@type": type.googleapis.com/envoy.extensions.network.dns_resolver.cares.v3.CaresDnsResolverConfig resolvers: - socket_address: address: "8.8.8.8" port_value: 53 dns_resolver_options: use_tcp_for_dns_lookups: true no_default_search_domain: true - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: dynamic_forward_proxy_cluster lb_policy: CLUSTER_PROVIDED cluster_type: name: envoy.clusters.dynamic_forward_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig dns_cache_config: name: dynamic_forward_proxy_cache_config dns_lookup_family: V4_ONLY dns_cache_circuit_breaker: max_pending_requests: 1024 typed_dns_resolver_config: name: envoy.network.dns_resolver.cares typed_config: "@type": type.googleapis.com/envoy.extensions.network.dns_resolver.cares.v3.CaresDnsResolverConfig resolvers: - socket_address: address: "8.8.8.8" port_value: 53 dns_resolver_options: use_tcp_for_dns_lookups: true no_default_search_domain: true transport_socket: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext common_tls_context: validation_context: trusted_ca: {filename: /etc/ssl/certs/ca-certificates.crt}

该示例同时展示了两个层面的要点:

  • Filter 与 Cluster 必须成对配置,并指向同一套 DNS 缓存参数;示例中还包含 DNS 缓存级熔断(dns_cache_circuit_breaker.max_pending_requests: 1024)与标准集群熔断(circuit_breakers),前者限制待处理 DNS 请求数、防止压垮解析器,后者限制到上游主机的连接/请求/重试等;
  • 地址族策略dns_lookup_family: V4_ONLY限定仅解析 IPv4;该字段的完整取值定义于 dns_cluster.proto 引用的common.dns.v3.DnsLookupFamilyAUTO为默认值)。

如需在 iOS/macOS 上改用 Apple 解析器,只需将typed_dns_resolver_config替换为以下形态(参见 dns-cache-circuit-breaker-apple.yaml):

typed_dns_resolver_config: name: envoy.network.dns_resolver.apple typed_config: "@type": type.googleapis.com/envoy.extensions.network.dns_resolver.apple.v3.AppleDnsResolverConfig

八、DNS 集群(DnsCluster)中的解析器配置与刷新语义

除了动态正向代理,DNS 发现型集群(envoy.clusters.dns扩展)同样通过typed_dns_resolver_config接入自定义解析器。结合 dns_cluster.proto 的字段说明,其关键刷新参数与解析器配置协同工作:

  • dns_refresh_rate:集群 DNS 刷新间隔,未设置时默认5000ms,最小 1ms;
  • dns_failure_refresh_rate:查询失败时的刷新间隔(含base_interval与可选的max_interval,后者默认是 base 的 10 倍);
  • respect_dns_ttl:为true时按 DNS 资源记录 TTL 设置刷新速率;配合dns_min_refresh_rate可设置 TTL 下限(至少 1 秒,TTL 更短的记录按该下限刷新);
  • dns_jitter:为刷新加入随机延迟(上限为配置值),避免大量请求同时触发 DNS 的"惊群"效应;
  • dns_lookup_family:解析地址族,默认AUTO
  • all_addresses_in_single_endpoint:所有返回地址视为单个端点(logical DNS 语义),否则每个地址视为独立端点(strict DNS 语义)。

该消息的注释还明确说明了配置优先级:当DnsCluster.typed_dns_resolver_configCluster.typed_dns_resolver_configCluster.dns_resolution_config同时存在时,若集群通过cluster_type使用DnsCluster扩展,Envoy 采用DnsCluster.typed_dns_resolver_config并忽略 Cluster 中的解析器相关字段;否则回退到Cluster.typed_dns_resolver_config

九、实践建议与注意事项

  • 默认行为无需配置:绝大多数场景直接使用默认的 c-ares 解析器即可,系统 nameserver 会自动从/etc/resolv.conf读取;
  • 自定义 nameserver 时注意 fallback 语义CaresDnsResolverConfigresolvers默认会覆盖系统配置,如需保留系统配置作为兜底,应显式设置use_resolvers_as_fallback: true
  • 超时与重试的默认值差异:c-ares 解析器的query_timeout_seconds(Envoy 默认 5s vs 库默认 2s)与query_tries(Envoy 默认 4 vs 库默认 3)都做了调整,以保持 Envoy 旧版行为,避免解析耗时上升;配置时需知悉这一默认值差异;
  • EDNS0 负载建议edns0_max_payload_size推荐使用 1232 字节的安全默认值以避免 UDP 分片,最大允许 4096;
  • 缓存策略差异:c-ares 的qcache_max_ttl在 Envoy 中默认关闭(0),而库默认 1 小时;Hickory 默认开启 1024 条目 LRU 缓存并支持负缓存,且自带 DoT/DoH/DNSSEC 能力,适合对解析安全性与协议有更高要求的场景;
  • getaddrinfo 的适用限制:固定 60 秒 TTL、专用线程执行、无解析器专属统计,仅适合低频率解析场景;
  • 统计观测:c-ares、Apple、Hickory 分别挂载于dns.caresdns.appledns.hickory统计树,可用于监控解析量、超时率、失败率与 channel 重建次数(reinits)。

如需深入了解 DNS 解析器在各类集群与过滤器中的完整接入方式,可继续阅读 dns_resolution.rst、dynamic_forward_proxy_filter.rst 以及四个解析器扩展的源码实现(cares、apple、getaddrinfo、hickory)。

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询