Traefik網關DNS解析超時問題優化

1、背景

在生產環境使用 Traefik 網關時出現了偶發的 DNS 解析超時導致網關與后端服務建立連接異常的情況。通過調用鏈埋點數據觀察發現,該部署環境中 Traefik 的 DNS 解析性能較差,耗時通常在 4ms 以上(正常應該是 1ms 以內)

初步分析:

Traefik 基于 Go 語言的 net/http 實現連接池,net/http 中僅支持連接復用未實現 DNS 緩存機制,所以每次新建連接時都會進行 DNS 解析。當網絡不穩定情況下(DNS 解析使用 UDP 協議,如果網絡丟包時只能依賴客戶端超時)時就容易出現 DNS 解析超時情況

2、DNS 域名解析優化

1)、DNS 域名解析參數配置

DNS 解析策略、次數和 /etc/resolv.conf 文件中的 ndots、search domain(搜索域) 和 nameserver(DNS 服務器) 配置強相關:

  1. 默認情況下 DNS 查詢會同時嘗試解析 IPv4(A 記錄) 和 IPv6 地址(AAAA 記錄),無論環境是否支持 IPv6

  2. ndots 和待解析的域名決定要不要優先使用 search domain,如果你的域名中,點的個數比配置的 ndots 小,則會優先拼接 search domain 后去解析

    比如有如下配置時:

    search default.svc.cluster.local svc.cluster.local cluster.local
    options ndots:3
    

    如果要解析的域名是 apportal.rehearsal.com,ndots 配置的是3,待解析域名中的點數小于 ndots,所以會優先拼接搜索域名去解析,解析順序如下:

    • apportal.rehearsal.com.default.svc.cluster.local.
    • apportal.rehearsal.com.svc.cluster.local.
    • apportal.rehearsal.com.cluster.local.
    • apportal.rehearsal.com.

    如果 ndots 配置的是2(待解析域名中的點數等于ndots),則解析順序如下:

    • apportal.rehearsal.com.
    • apportal.rehearsal.com.default.svc.cluster.local.
    • apportal.rehearsal.com.svc.cluster.local.
    • apportal.rehearsal.com.cluster.local.
  3. serach domain 和 nameserver 決定了 DNS 最多查詢的次數,即 DNS 最多查詢的次數等于搜素域的數量+1乘以 dnsserver 的數量

    比如有以下配置:

    search default.svc.cluster.local svc.cluster.local cluster.local
    nameserver 169.254.20.10
    nameserver 172.16.0.10
    options ndots:5
    

    當解析 apportal.rehearsal.com 域名時,解析順序如下:

    解析域名查詢類型dns server
    apportal.rehearsal.com.default.svc.cluster.local.A(IPv4)169.254.20.10
    apportal.rehearsal.com.default.svc.cluster.local.A172.16.0.10
    apportal.rehearsal.com.default.svc.cluster.local.AAAA(IPv6)169.254.20.10
    apportal.rehearsal.com.default.svc.cluster.local.AAAA172.16.0.10
    apportal.rehearsal.com.svc.cluster.local.A169.254.20.10
    apportal.rehearsal.com.svc.cluster.local.A172.16.0.10
    apportal.rehearsal.com.svc.cluster.local.AAAA169.254.20.10
    apportal.rehearsal.com.svc.cluster.local.AAAA172.16.0.10
    apportal.rehearsal.com.cluster.local.A169.254.20.10
    apportal.rehearsal.com.cluster.local.A172.16.0.10
    apportal.rehearsal.com.cluster.local.AAAA169.254.20.10
    apportal.rehearsal.com.cluster.local.AAAA172.16.0.10
    apportal.rehearsal.com.A169.254.20.10
    apportal.rehearsal.com.A172.16.0.10
    apportal.rehearsal.com.AAAA169.254.20.10
    apportal.rehearsal.com.AAAA172.16.0.10
2)、Kubernetes 中搜索域和ndots 默認值

1)搜索域

Kubernetes 為方便 Service 訪問,默認配置 3 個搜索域:nsSvcDomain、svcDomain、clusterDomain

default namespace下的搜索域默認為:

search default.svc.cluster.local svc.cluster.local cluster.local
  • default.svc.cluster.local:同 namespace 內可直接用 Service 名稱訪問(比如通過 B 訪問同命名空間的 Service B)
  • svc.cluster.local:跨 namespace 可通過 ${service name}.${namespace name} 訪問
  • cluster.local:支持非 Kubernetes 上的域名訪問,例如設置 clusterDomain 為 rehearsal.com,那么對于 apportal.rehearsal.com 域名,可以使用 apportal 來訪問

2)ndots

Kubernetes 中 ndots 默認值為5(Kubernetes官方解釋),官方這樣設置的初衷是優先匹配 Kubernetes 集群內域名,但可能對外部域名解析造成冗余

3)、ndots 參數優化

在我們的部署環境下都是通過三級域名(例如:apportal.rehearsal.com)來訪問 Service 的,ndots 默認值為5

default namespace Pod 的 /etc/resolv.conf 文件

# cat resolv.conf 
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 192.168.0.10
options ndots:5

apportal.rehearsal.com 解析情況如下,需要解析8次才能解析到對應域名:(如果 nameserver 有兩個時需要解析16次)

在這里插入圖片描述

這里我們在 Deployment 中將 ndots 參數調整為2,調整后只需要2次就能解析到對應域名

在這里插入圖片描述

優化依據:三級域名(含2個點)在 ndots:2 配置下,會直接解析原域名,減少不必要的搜索域拼接步驟,顯著降低解析次數和耗時

3、Java DNS 緩存機制分析

在減少 DNS 解析次數后,我們還希望在 Traefik 中添加 DNS 緩存機制來減少每次新建連接時 DNS 解析的耗時,這里我們調研了 Java DNS 緩存機制來作為參考。以下從 JDK 底層緩存和 Http 客戶端緩存兩方面展開分析:

1)、JDK 中的 DNS 緩存

Java 通過 java.net.InetAddress 類處理域名解析,并將結果緩存以避免重復查詢

緩存查詢流程

解析入口方法為 InetAddress.getAllByName(String host),其優先從緩存獲取結果:

// java.net.InetAddressprivate static InetAddress[] getAllByName0 (String host, InetAddress reqAddr, boolean check)throws UnknownHostException  {/* If it gets here it is presumed to be a hostname *//* Cache.get can return: null, unknownAddress, or InetAddress[] *//* make sure the connection to the host is allowed, before we* give out a hostname*/if (check) {SecurityManager security = System.getSecurityManager();if (security != null) {security.checkConnect(host, -1);}}// 先從緩存中獲取InetAddress[] addresses = getCachedAddresses(host);/* If no entry in cache, then do the host lookup */if (addresses == null) {// 緩存中數據為空,進行dns lookupaddresses = getAddressesFromNameService(host, reqAddr);}if (addresses == unknown_array)throw new UnknownHostException(host);return addresses.clone();}private static Cache addressCache = new Cache(Cache.Type.Positive);private static InetAddress[] getCachedAddresses(String hostname) {hostname = hostname.toLowerCase();// search both positive & negative cachessynchronized (addressCache) {cacheInitIfNeeded();// 使用addressCache進行緩存CacheEntry entry = addressCache.get(hostname);if (entry == null) {entry = negativeCache.get(hostname);}if (entry != null) {return entry.addresses;}}// not foundreturn null;}

緩存策略控制

sun.net.InetAddressCachePolicy 中定義了緩存的三個策略值:

  • FOREVER = -1:永久緩存(默認值)
  • NEVER = 0:不緩存
  • 大于0,緩存多少秒后過期

Cache addressCache = new Cache(Cache.Type.Positive) 這里定義的 type 為 Type.Positive

        private int getPolicy() {if (type == Type.Positive) {return InetAddressCachePolicy.get();} else {return InetAddressCachePolicy.getNegative();}}
public final class InetAddressCachePolicy {private static int cachePolicy = -1;public static synchronized int get() {return cachePolicy;}

默認值為永久緩存,不過期,也不會刷新 DNS 數據

2)、Http 客戶端緩存實現

在 Java Http 客戶端層面,不同的 Http 客戶端實現的緩存機制不同,以 Apache HttpClient 4.5.6 版本為例,核心代碼如下:

// org.apache.http.impl.conn.DefaultHttpClientConnectionOperator
public class DefaultHttpClientConnectionOperator implements HttpClientConnectionOperator {public DefaultHttpClientConnectionOperator(final Lookup<ConnectionSocketFactory> socketFactoryRegistry,final SchemePortResolver schemePortResolver,final DnsResolver dnsResolver) {super();Args.notNull(socketFactoryRegistry, "Socket factory registry");this.socketFactoryRegistry = socketFactoryRegistry;this.schemePortResolver = schemePortResolver != null ? schemePortResolver :DefaultSchemePortResolver.INSTANCE;// 初始化 dnsResolverthis.dnsResolver = dnsResolver != null ? dnsResolver :SystemDefaultDnsResolver.INSTANCE;}@Overridepublic void connect(final ManagedHttpClientConnection conn,final HttpHost host,final InetSocketAddress localAddress,final int connectTimeout,final SocketConfig socketConfig,final HttpContext context) throws IOException {final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context);final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());if (sf == null) {throw new UnsupportedSchemeException(host.getSchemeName() +" protocol is not supported");}// 使用dnsResolver進行dns解析final InetAddress[] addresses = host.getAddress() != null ?new InetAddress[] { host.getAddress() } : this.dnsResolver.resolve(host.getHostName());...}

Apache HttpClient 中默認 DnsResolver 為 Java DNS 緩存邏輯,也就是 DNS 永久不刷新

//org.apache.http.impl.conn.SystemDefaultDnsResolver
public class SystemDefaultDnsResolver implements DnsResolver {public static final SystemDefaultDnsResolver INSTANCE = new SystemDefaultDnsResolver();@Overridepublic InetAddress[] resolve(final String host) throws UnknownHostException {return InetAddress.getAllByName(host);}}

而對應 OkHttp 來說,OkHttp 內部維護了一個 DNS 緩存,具有以下特點:

  • 成功解析緩存:默認緩存時間為5分鐘(300 秒),與 DNS 服務器返回的 TTL 無關
  • 失敗解析緩存:默認緩存10秒,避免頻繁重試無效域名
  • 緩存實現:通過連接池(RealConnectionPool)中的 RouteDatabase 關聯域名與解析結果,隨連接池管理自動失效

4、Traefik 添加 DNS 緩存機制

1)、方案一:net/http替換為fasthttp

可以參考最新的 PR https://github.com/traefik/traefik/pull/11122,將 net/http 替換為 fasthttp,fasthttp 支持 DNS 緩存且性能更好

但目前此功能官方標記為實驗性功能,不推薦生產使用,而且這塊改動相對較大,所以未采用該方案

2)、方案二:引入 DNS 緩存庫

我們選擇集成 go-dnscache 庫實現 DNS 緩存功能,go-dnscache 庫刷新 DNS 緩存有兩個額外的配置選項如下:

				r := &Resolver{}options := dnscache.ResolverRefreshOptions{ClearUnused:      true, // 默認為true,清除未使用的 dns 條目PersistOnFailure: false,  // 默認為false,刷新失敗時清理舊數據}resolver.RefreshWithOptions(options)

使用的 DNS 緩存策略如下:

  • 緩存有效期設置為10分鐘,每10分鐘異步刷新 DNS 緩存
  • 刷新時清除未使用的 DNS 條目(ClearUnused=true),緩存刷新失敗時自動保留舊數據,(PersistOnFailure=true,防止因集中刷新時 DNS 解析異常影響后續程序訪問)

代碼改動示例:

// pkg/server/service/roundtripper.go
func NewRoundTripperManager(spiffeX509Source SpiffeX509Source) *RoundTripperManager {resolver := &dnscache.Resolver{}go func() {logger := log.Ctx(context.Background()).With().Str(logs.ComponentName, "dns-cache-refresher").Logger()t := time.NewTicker(10 * time.Minute)defer t.Stop()for range t.C {// 每隔10分鐘刷新一次 dns 緩存start := time.Now()options := dnscache.ResolverRefreshOptions{ClearUnused:      false, // 不清除未使用的 dns 條目PersistOnFailure: true,  // 刷新失敗時保留舊數據}resolver.RefreshWithOptions(options)logger.Info().Dur("duration", time.Since(start)).Msg("DNS cache refreshed successfully")}}()return &RoundTripperManager{roundTrippers:    make(map[string]http.RoundTripper),configs:          make(map[string]*dynamic.ServersTransport),spiffeX509Source: spiffeX509Source,dnsResolver:      resolver,}
}func (r *RoundTripperManager) createRoundTripper(cfg *dynamic.ServersTransport) (http.RoundTripper, error) {...// 創建使用 dns 緩存的 DialContextcustomDialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {host, port, err := net.SplitHostPort(addr)if err != nil {return nil, err}...transport := &http.Transport{Proxy:                 http.ProxyFromEnvironment,DialContext:           utils.CachedDialContext(dialer), // 使用帶 dns 緩存的 DialContextMaxIdleConnsPerHost:   cfg.MaxIdleConnsPerHost,IdleConnTimeout:       90 * time.Second,TLSHandshakeTimeout:   10 * time.Second,ExpectContinueTimeout: 1 * time.Second,ReadBufferSize:        64 * 1024,WriteBufferSize:       64 * 1024,}

參考:

go dns解析原理及調優

阿里云DNS最佳實踐

kubernetes容器中域名解析優化

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93720.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93720.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93720.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

從0到1掌握 Spring Security(第三篇):三種認證方式,按配置一鍵切換

> 本文是Spring Security系列第三篇,將帶你實現內存、JDBC和自定義三種認證方式的無縫切換,只需修改配置文件即可完成認證策略變更! ## 一、為什么需要多種認證方式? 在軟件開發的不同階段,我們需要不同的認證策略: - **開發階段**:使用內存認證,快速配置測試賬號…

阿里云國際站云防火墻:如何利用阿里云云防火墻實現細粒度的訪問控制?

利用阿里云云防火墻實現細粒度的訪問控制&#xff0c;可以從分層策略、精確匹配、動態調整三個方面著手&#xff0c;讓不同業務、用戶和資源的訪問權限清晰可控。一、明確控制目標業務隔離&#xff1a;不同業務系統、部門或環境&#xff08;生產/測試&#xff09;之間互不干擾。…

rom定制系列------小米cc9機型 原生安卓15系統 雙版線刷root 定制修改功能項

小米 9 Lite/CC9 機型代碼;pyxis.搭載驍龍710處理器.適用于以下型號的小米機型&#xff1a;M1904F3BG, M1904F3BC. 刷寫前提; 需要當前機型已經解鎖bl的狀態下進入fast模式刷寫。此機型可以正常官方解鎖與強解bl鎖。效果都是一樣的。在fast模式下裝好聯機驅動。使用官方平臺刷…

解讀60頁全面認識大數據基礎知識培訓【附全文閱讀】

該培訓課件適用于對大數據知識感興趣的初學者、企業管理人員、相關技術從業者等。內容圍繞大數據展開,先介紹其基本概念,包括定義、數據級別、來源、類型、價值挖掘等,還闡述了 5 個 “V” 特征及與傳統數據的區別。接著講述大數據的發展演進,涵蓋國際國內發展歷程、發展階…

Prompt engineering(PE) —— prompt 優化如何進行?

從新手到高手&#xff1a;Prompt最佳實踐全解析 一、引言&#xff1a;開啟 Prompt 的神秘大門在這個人工智能飛速發展的時代&#xff0c;AI 已經悄然融入我們生活的方方面面。你是否有過這樣的經歷&#xff1a; 當你對著智能音箱詢問 “明天天氣如何” 時&#xff0c;它能迅速給…

云服務器的優缺點都有哪些?

云服務器作為一種有著高度靈活性的服務器類型&#xff0c;能夠根據用戶的需求來調整資源&#xff0c;有著很強的優勢&#xff0c;但是云服務器還是有著一定的缺點的&#xff0c;本文就來共同探討一下云服務器的優缺點都有哪些吧&#xff01;首先&#xff0c;云服務器能根據業務…

宋紅康 JVM 筆記 Day05|運行時數據區內部結構、JVM中的線程說明、程序計數器

一、今日視頻區間 P39-P43 二、一句話總結 運行時數據區內部結構&#xff1b;JVM中的線程說明&#xff1b;程序計數器&#xff08;PC寄存器&#xff09;&#xff1b; 三、關鍵圖/命令 3.1 運行時數據區內部結構3.2 JVM中的線程說明3.3 程序計數器&#xff08;PC寄存器&#xff…

Java增強for循環(小白友好版)

前言&#xff1a;為什么需要增強for循環&#xff1f;作為Java初學者&#xff0c;你或許已經學會使用傳統for循環來遍歷數組或集合&#xff1a;for (int i 0; i < array.length; i) {System.out.println(array[i]); }這種寫法需要手動維護索引變量i&#xff0c;對于集合還需…

【OLAP】trino安裝和基本使用

目錄 ?一、概述 1.1Trino不是什么 1.2Trino是什么 二、Trino特點 三、Trino架構 3.1架構和服務節點 3.2Trino數據模型 四、Trino安裝部署 4.1配置JDK 4.2單機版&#xff08;Coordinator和Worker同進程&#xff09; 4.2.1啟動服務 4.2.2下載客戶端 五、配置HTTPS&…

如何寫出更清晰易讀的布爾邏輯判斷?

列編碼技巧和規范&#xff0c;來降低邏輯的“認知負荷”。成功的實踐&#xff0c;必須系統性地涵蓋五大關鍵策略&#xff1a;采用有意義的變量名進行封裝、將復雜的判斷拆解為獨立的函數、優先使用“肯定式”而非“否定式”邏輯、利用括號明確運算的優先級、以及運用德摩根定律…

新手向:Java方向講解

從諾基亞塞班到阿里雙11&#xff0c;從安卓應用到華爾街交易&#xff0c;Java用一行System.out.println()征服了數字世界1998年&#xff0c;諾基亞在塞班系統上首次采用Java ME技術&#xff0c;讓手機具備了運行應用程序的能力&#xff0c;開啟了移動互聯網的序幕。當時的Java開…

視覺圖像界面設計【QT-creator高級編程 - 01】圖像顯如何保證跟隨主窗口變化,且保留必要的設定窗口

前言&#xff1a;問題&#xff0c;顯示圖像的時候&#xff0c;按最大窗口&#xff0c;圖片窗口不跟著變大&#xff0c;還有&#xff0c;右邊那些設置控件都沒有動解決&#xff1a;步驟1&#xff1a;1?? 讓 graphicsView 自動占滿在 Qt Creator 中選中 graphicsView_7 / 12 / …

pair之于vector、queue(vector<pair<int,int>>)

1、vector&#xff1c;pair&#xff1c;int,int&#xff1e;&#xff1e; 和 Map 的異同點map&#xff1a;會對插入的元素按鍵Key&#xff0c;自動排序&#xff0c;而且鍵Key不允許重復&#xff1b;vector&#xff1a;的這種用法不會自動排序&#xff0c;而且允許重復。2、queu…

從合規到卓越:全星QMS如何成為制造企業的質量戰略引擎

從合規到卓越&#xff1a;全星質量管理QMS軟件系統如何成為制造企業的質量戰略引擎 全星質量管理QMS軟件系統憑借其高度定制化、智能化、全流程覆蓋等核心優勢&#xff0c;已在汽車制造、電子、醫療、航空航天等多個高端制造領域實現領先性應用&#xff0c;顯著提升了企業的質…

按鍵及消抖

方法一&#xff1a;延時阻塞key.c:#include "key.h" #include "delay.h"//初始化GPIO void key_init(void) {GPIO_InitTypeDef gpio_initstruct;//打開時鐘__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA時鐘//調用GPIO初始化函數…

什么是接口?PHP如何使用 SessionHandlerInterface 接口實現Session自定義會話數據存儲

在面向對象編程中&#xff0c;接口&#xff08;Interface&#xff09;作為類與類之間的契約規范&#xff0c;定義了實現類必須遵守的方法簽名集合&#xff0c;卻不包含具體實現細節。這種抽象機制通過強制統一的方法命名和參數結構&#xff0c;實現了代碼的解耦與多態性&#x…

健身房預約系統SSM+Mybatis-plus實現(二、增刪改查的具體實現)

文章目錄一、環境搭建二、用戶管理頁面&#xff08;純展示無事件操作&#xff09;0.三步走1.查詢表單&#xff08;1&#xff09;書寫頁面代碼 &#xff1a;&#xff08;2&#xff09;對應的js部分創建對象數據模型的綁定部分&#xff1a;&#xff08;3&#xff09;引入需要的庫…

在IAR Embedded Workbench for Arm中實現NXP S32K3安全調試

隨著汽車電子系統變得越來越智能&#xff0c;對功能安全&#xff08;Safety&#xff09;的要求越來越高&#xff0c;同時信息安全&#xff08;Security&#xff09;也越來越被關注&#xff0c;安全調試&#xff08;Secure Debug&#xff09;機制已成為一個重要的信息安全特性。…

Vue實例中的其他屬性【5】

目錄1.計算屬性&#xff1a;1.概述&#xff1a;2.語法特點&#xff1a;3.案例&#xff1a;案例1&#xff1a;案例2&#xff1a;案例3&#xff1a;4.總結&#xff1a;5.get函數什么時候執行&#xff1f;6.注意:2.監視屬性&#xff1a;1.概述&#xff1a; 2.用法&#xff1a;1.監…

C++入門自學Day11-- String, Vector, List 復習

往期內容回顧 List類型的自實現 List類型&#xff08;初識&#xff09; Vector類的自實現 Vector類&#xff08;注意事項&#xff09; 初識Vector String類的自實現 String類的使用&#xff08;續&#xff09; String類&#xff08;續&#xff09; String類&#xff08;初識&…