
1. 項(xiàng)目概述為什么域名解析是Linux網(wǎng)絡(luò)里最常被忽略、卻最不能出錯的一環(huán)“Linux網(wǎng)絡(luò)基礎(chǔ)3域名解析服務(wù)”這個標(biāo)題看起來平平無奇像是教科書目錄里一個不起眼的章節(jié)編號。但如果你在某次深夜排查線上服務(wù)連不上API、某臺新部署的服務(wù)器死活ping不通內(nèi)網(wǎng)域名、或者CI/CD流水線突然卡在apt update那一步——十有八九問題就藏在這“第三講”里。我?guī)н^不少剛從學(xué)校出來的新人他們能手寫iptables規(guī)則、能調(diào)通Docker網(wǎng)絡(luò)、甚至能搭起K8s集群可一遇到curl: (6) Could not resolve host: api.internal這種報錯第一反應(yīng)還是去查防火墻、看端口、翻日志最后折騰兩小時才發(fā)現(xiàn)/etc/resolv.conf里寫的DNS服務(wù)器早就宕了或者systemd-resolved和dnsmasq在后臺偷偷打架。域名解析服務(wù)說白了就是把“www.example.com”翻譯成“192.0.2.42”的翻譯官。它不直接處理業(yè)務(wù)邏輯不承載流量不參與加密握手但它一旦失職整個網(wǎng)絡(luò)通信就卡在起點(diǎn)——就像你拿著一張寫滿地址的快遞單卻找不到郵局在哪。Linux系統(tǒng)里這層翻譯機(jī)制遠(yuǎn)比Windows“自動獲取DNS”復(fù)雜得多它不是單一配置文件的事而是/etc/hosts、/etc/resolv.conf、systemd-resolved、nsswitch.conf、dnsmasq、甚至容器運(yùn)行時自帶的DNS代理多層疊加、相互覆蓋、優(yōu)先級隱晦。更麻煩的是不同發(fā)行版默認(rèn)策略差異極大Ubuntu 22.04默認(rèn)啟用systemd-resolved并接管53端口CentOS 7則長期依賴NetworkManager生成resolv.conf而Alpine鏡像里壓根沒裝systemd全靠BusyBox的nslookup硬扛。這些細(xì)節(jié)不會寫在面試題里但會真實(shí)出現(xiàn)在你凌晨三點(diǎn)收到的告警頁面上。這篇文章不講RFC 1034那種理論推導(dǎo)也不堆砌dig trace的冗長輸出。我會以一個真實(shí)運(yùn)維現(xiàn)場的視角帶你一層層剝開Linux域名解析的洋蔥結(jié)構(gòu)從最底層的/etc/hosts靜態(tài)映射如何被繞過到resolv.conf里options timeout:1 attempts:2這些參數(shù)怎么決定你的服務(wù)啟動慢3秒還是快3秒從systemd-resolved的StubListener模式為何讓nslookup和dig結(jié)果不一致到容器內(nèi)/etc/resolv.conf為何總是指向127.0.0.11——以及你該不該改它。所有內(nèi)容都基于我過去三年在多個生產(chǎn)環(huán)境踩坑、復(fù)現(xiàn)、抓包、源碼翻查的真實(shí)記錄每一步操作都有明確意圖每個參數(shù)都有實(shí)測依據(jù)。適合正在搭建跳板機(jī)的運(yùn)維、調(diào)試K8s CoreDNS的SRE、寫自動化腳本總被DNS超時打斷的DevOps或者只是想搞懂“為什么我的Linux機(jī)器有時能上網(wǎng)有時不能”的技術(shù)愛好者。你不需要背命令只需要理解數(shù)據(jù)包從getaddrinfo()調(diào)用開始到底經(jīng)過了幾道門、被誰改寫了、又在哪個環(huán)節(jié)悄悄失敗了。2. 域名解析的完整鏈路拆解從應(yīng)用調(diào)用到網(wǎng)絡(luò)響應(yīng)的七層穿透要真正掌控Linux的DNS行為必須跳出“改個resolv.conf就完事”的思維定式。整個解析過程是一條貫穿用戶空間到內(nèi)核空間、橫跨多個子系統(tǒng)的數(shù)據(jù)流任何一環(huán)出問題都會導(dǎo)致看似相同的命令產(chǎn)生截然不同的結(jié)果。我把它拆解為七個關(guān)鍵階段每個階段都對應(yīng)一個可驗(yàn)證、可干預(yù)的控制點(diǎn)。2.1 階段一應(yīng)用層發(fā)起解析請求glibc的getaddrinfo幾乎所有Linux應(yīng)用curl、wget、python requests、nodejs http模塊都不直接調(diào)用gethostbyname這種老接口而是走POSIX標(biāo)準(zhǔn)的getaddrinfo()。這個函數(shù)本身不發(fā)網(wǎng)絡(luò)包它只做一件事根據(jù)/etc/nsswitch.conf的配置決定接下來該找誰要IP地址。比如你執(zhí)行curl https://github.comglibc會先讀nsswitch.conf里hosts:這一行看到files dns就明白先查/etc/hosts查不到再走DNS協(xié)議。這里有個極易被忽視的細(xì)節(jié)getaddrinfo默認(rèn)會同時查詢A記錄IPv4和AAAA記錄IPv6如果DNS服務(wù)器對AAAA響應(yīng)慢或超時整個解析就會被拖慢——哪怕你根本不需要IPv6。實(shí)測中某云廠商的公共DNS對AAAA查詢平均耗時800ms而A記錄只要20ms這就導(dǎo)致curl啟動延遲明顯。解決方案不是禁用IPv6那會引發(fā)更多兼容性問題而是在/etc/gai.conf里添加precedence ::ffff:0:0/96 100強(qiáng)制IPv4優(yōu)先或者用curl --ipv4顯式指定。2.2 階段二名稱服務(wù)切換器nsswitch.conf的決策中樞/etc/nsswitch.conf是整個解析鏈路的“交通指揮中心”。它的hosts:行決定了查詢順序和來源。常見配置有三種典型組合hosts: files dns最經(jīng)典先查/etc/hosts失敗后走DNS。安全團(tuán)隊(duì)常在此處加mdns4_minimal支持mDNS如.local域名但要注意mdns4_minimal會攔截所有.local查詢導(dǎo)致無法解析真正的公網(wǎng)xxx.local域名。hosts: files resolve [!UNAVAILreturn] dnssystemd-resolved場景resolve代表調(diào)用systemd-resolved的D-Bus接口[!UNAVAILreturn]是關(guān)鍵——如果systemd-resolved服務(wù)未運(yùn)行立即返回失敗不再嘗試后面的dns。這意味著你systemctl stop systemd-resolved后ping可能直接報Unknown host而不是fallback到resolv.conf。hosts: dns [NOTFOUNDreturn] files反向順序先DNS查不到才查/etc/hosts。這在某些需要動態(tài)域名管理的場景有用但會完全繞過/etc/hosts的本地調(diào)試能力不推薦日常使用。提示修改nsswitch.conf后無需重啟服務(wù)glibc在每次getaddrinfo調(diào)用時實(shí)時讀取。但務(wù)必用getent hosts github.com驗(yàn)證效果ping或curl可能因緩存掩蓋問題。2.3 階段三本地靜態(tài)映射/etc/hosts的精確制導(dǎo)/etc/hosts是解析鏈路上唯一不發(fā)網(wǎng)絡(luò)包的環(huán)節(jié)也是最可靠的調(diào)試工具。它的格式極其簡單IP地址 主機(jī)名 別名但有三個魔鬼細(xì)節(jié)注釋與空行#開頭的行和純空行會被忽略但127.0.0.1 example.com #comment這種寫法#后面的內(nèi)容會被當(dāng)作文本而非注釋可能導(dǎo)致解析失敗IPv6地址格式::1必須寫全不能簡寫為127.0.0.1否則IPv6應(yīng)用如某些Go程序會忽略主機(jī)名匹配邏輯192.168.1.100 server1 server1.internal中server1和server1.internal是兩個獨(dú)立條目ping server1.internal不會自動fallback到server1。我曾在一個K8s集群調(diào)試中發(fā)現(xiàn)coredns的Pod IP被錯誤寫入/etc/hosts導(dǎo)致所有節(jié)點(diǎn)上的kubectl get nodes都返回Connection refused——因?yàn)閗ubectl默認(rèn)連接https://kubernetes.default.svc.cluster.local而/etc/hosts里這條記錄把域名指向了已銷毀的舊Pod IP。解決方法不是刪掉/etc/hosts而是用sed -i /kubernetes\.default\.svc\.cluster\.local/d /etc/hosts精準(zhǔn)刪除避免誤傷其他條目。2.4 階段四DNS客戶端配置resolv.conf的明面戰(zhàn)場/etc/resolv.conf是大家最熟悉的配置文件但它早已不是“最終判決書”?,F(xiàn)代Linux中它常是其他服務(wù)NetworkManager、systemd-resolved、dhclient的輸出產(chǎn)物。其核心指令只有三條nameserver 1.1.1.1最多允許3個按順序使用前一個超時才試下一個search example.com internal.net當(dāng)解析db時自動嘗試db.example.com、db.internal.netoptions timeout:2 attempts:3 rotatetimeout是單次查詢等待秒數(shù)attempts是重試次數(shù)rotate開啟輪詢避免單點(diǎn)DNS壓力過大。關(guān)鍵陷阱在于timeout和attempts的乘積。默認(rèn)timeout:5 attempts:2意味著單域名最大阻塞10秒。在微服務(wù)架構(gòu)中一個服務(wù)啟動要解析10個依賴域名最壞情況就是100秒啟動延遲。我們線上將timeout壓到1attempts設(shè)為2配合rotate實(shí)測平均解析時間從3.2秒降至0.8秒且失敗率未上升——因?yàn)楝F(xiàn)代DNS服務(wù)器基本能在1秒內(nèi)響應(yīng)超時更多是網(wǎng)絡(luò)抖動或中間設(shè)備丟包所致。2.5 階段五本地DNS代理systemd-resolved的雙面性systemd-resolved是systemd生態(tài)的DNS管家它監(jiān)聽127.0.0.53:53Stub Listener對外提供DNS服務(wù)對內(nèi)聚合多種上游。它的存在讓resolv.conf變得“不可信”當(dāng)你看到nameserver 127.0.0.53實(shí)際流量并未發(fā)往53端口而是被resolved進(jìn)程截獲再根據(jù)/run/systemd/resolve/resolv.conf它的上游配置轉(zhuǎn)發(fā)。resolved的強(qiáng)項(xiàng)在于緩存TTL內(nèi)零延遲、DNSSEC驗(yàn)證、LLMNR/mDNS支持弱點(diǎn)在于調(diào)試?yán)щy——dig 127.0.0.53 github.com能通但nslookup github.com卻失敗因?yàn)閚slookup默認(rèn)用/etc/resolv.conf而dig可以指定服務(wù)器。要徹底掌控resolved必須理解它的三層配置/etc/systemd/resolved.conf主配置DNS定義全局上游Domains~.表示對所有域名走此DNS禁用search域/run/systemd/resolve/resolv.confresolved實(shí)際使用的上游列表由NetworkManager等服務(wù)動態(tài)更新/etc/resolv.conf通常軟鏈接到/run/systemd/resolve/stub-resolv.conf指向127.0.0.53。注意systemctl restart systemd-resolved不會重新加載/etc/systemd/resolved.conf必須systemctl daemon-reload后再重啟否則配置無效。2.6 階段六網(wǎng)絡(luò)管理器介入NetworkManager的自動覆蓋NetworkManager是桌面版Linux的DNS事實(shí)標(biāo)準(zhǔn)。它會在/etc/resolv.conf上打上# Generated by NetworkManager標(biāo)記并根據(jù)當(dāng)前連接Wi-Fi/以太網(wǎng)的DHCP選項(xiàng)或手動設(shè)置動態(tài)生成DNS服務(wù)器。問題在于它默認(rèn)不讀取/etc/systemd/resolved.conf而是把自己的配置硬寫進(jìn)/run/systemd/resolve/resolv.conf導(dǎo)致resolved的全局DNS被覆蓋。解決方案有兩個徹底禁用NM的DNS管理在/etc/NetworkManager/NetworkManager.conf中添加[main] dnsnone然后nmcli dev set eth0 managed no讓NM配合resolved在/etc/NetworkManager/conf.d/99-resolved.conf中寫[main] dnssystemd-resolved這樣NM會把DHCP獲取的DNS寫入resolved的上游而非直接覆蓋resolv.conf。我在線上服務(wù)器一律采用第一種方案因?yàn)榉?wù)器網(wǎng)絡(luò)配置穩(wěn)定不需要NM的動態(tài)能力反而要杜絕它對DNS的“好心辦壞事”。2.7 階段七容器與虛擬化環(huán)境隔離網(wǎng)絡(luò)下的DNS迷宮容器是DNS問題的高發(fā)區(qū)。Docker默認(rèn)將宿主機(jī)的/etc/resolv.conf復(fù)制進(jìn)容器但如果宿主機(jī)用systemd-resolved容器內(nèi)nameserver 127.0.0.53就完全失效容器網(wǎng)絡(luò)命名空間里沒有resolved服務(wù)。Kubernetes更進(jìn)一步kubelet會為每個Pod生成/etc/resolv.confnameserver指向CoreDNS Service IP如10.96.0.10并注入search域。但若CoreDNS異常Pod內(nèi)所有域名解析即刻癱瘓且nslookup會顯示connection timed out而非server failed讓人誤以為是網(wǎng)絡(luò)連通性問題。實(shí)操中我堅(jiān)持兩條鐵律絕不信任容器內(nèi)的resolv.conf自動生成Docker啟動時用--dns1.1.1.1 --dns-searchinternal.net顯式指定K8s中為關(guān)鍵Pod添加dnsPolicy: Default繼承Node DNS或dnsPolicy: None完全自定義避免被集群DNS故障波及。曾有一個支付服務(wù)因CoreDNS滾動升級失敗所有Pod解析超時而dnsPolicy: Default的監(jiān)控Agent Pod仍能正常上報幫我們快速定位到CoreDNS而非業(yè)務(wù)代碼。3. 核心實(shí)操五類高頻場景的逐行診斷與修復(fù)方案光知道鏈路不夠得有能立刻上手的診斷武器庫。下面五個場景全部來自我處理過的生產(chǎn)事故每個都附帶3步定位法和1行修復(fù)命令確保你下次遇到同類問題5分鐘內(nèi)解決。3.1 場景一ping通但curl失敗——glibc與DNS協(xié)議的隱性沖突現(xiàn)象ping github.com返回IP并通但curl https://github.com報Could not resolve host。根因分析ping用gethostbyname僅查A記錄curl用getaddrinfo查AAAAA。若DNS服務(wù)器對AAAA記錄響應(yīng)異常如返回SERVFAIL或超時getaddrinfo會整體失敗而ping不受影響。3步定位法dig github.com A和dig github.com AAAA對比響應(yīng)時間與狀態(tài)碼getent ahosts github.comahosts強(qiáng)制走getaddrinfo路徑驗(yàn)證是否真失敗strace -e traceconnect,sendto,recvfrom curl -v https://github.com 21 | grep -A5 -B5 getaddrinfo看系統(tǒng)調(diào)用級失敗點(diǎn)。1行修復(fù)命令echo options single-request-reopen | sudo tee -a /etc/resolv.confsingle-request-reopen讓glibc對A和AAAA查詢使用獨(dú)立socket避免AAAA阻塞A。實(shí)測后curl成功率從42%升至99.8%且無需重啟任何服務(wù)。3.2 場景二systemd-resolved與dnsmasq端口沖突——53端口的爭奪戰(zhàn)現(xiàn)象systemctl status systemd-resolved顯示active但sudo ss -tuln | grep :53看不到監(jiān)聽dig 127.0.0.53超時。根因分析dnsmasq常被用作本地DNS緩存默認(rèn)監(jiān)聽0.0.0.0:53搶占了systemd-resolved的127.0.0.53:53。resolved檢測到53端口被占自動降級為127.0.0.54但resolv.conf仍指向127.0.0.53導(dǎo)致斷連。3步定位法sudo ss -tuln | grep :53查看哪個進(jìn)程占著53sudo systemctl list-units | grep dns檢查dnsmasq是否啟用journalctl -u systemd-resolved -n 50 --no-pager | grep -i port.*53看resolved日志中的端口警告。1行修復(fù)命令sudo sed -i s/bind-interfaces/#bind-interfaces/ /etc/dnsmasq.conf sudo systemctl restart dnsmasq注釋bind-interfaces后dnsmasq只監(jiān)聽127.0.0.1:53resolved可安全使用127.0.0.53。若需dnsmasq監(jiān)聽所有接口則改resolved端口sudo mkdir -p /etc/systemd/resolved.conf.d echo -e [Resolve]\nDNSStubListenerno | sudo tee /etc/systemd/resolved.conf.d/no-stub.conf再sudo systemctl restart systemd-resolved。3.3 場景三容器內(nèi)/etc/resolv.conf被覆蓋——Docker的靜默篡改現(xiàn)象Docker容器內(nèi)cat /etc/resolv.conf顯示nameserver 127.0.0.11但dig 127.0.0.11 google.com超時宿主機(jī)DNS正常。根因分析Docker守護(hù)進(jìn)程內(nèi)置DNS服務(wù)器dockerd的embedded DNS監(jiān)聽127.0.0.11負(fù)責(zé)解析容器間服務(wù)名如redis。但它不處理外部域名需轉(zhuǎn)發(fā)給上游DNS。若宿主機(jī)/etc/resolv.conf為空或無效dockerd無法獲取上游導(dǎo)致外部解析失敗。3步定位法docker info | grep -i name resolution確認(rèn)Docker DNS狀態(tài)cat /etc/resolv.conf宿主機(jī)檢查是否有有效nameserverdocker run --rm alpine nslookup google.com 127.0.0.11測試Docker DNS轉(zhuǎn)發(fā)能力。1行修復(fù)命令echo nameserver 1.1.1.1 | sudo tee /etc/resolv.conf sudo systemctl restart docker強(qiáng)制宿主機(jī)resolv.conf有可用DNSDocker啟動時會自動讀取并設(shè)為上游。注意不要在容器內(nèi)改resolv.confDocker會覆蓋。3.4 場景四search域?qū)е陆馕霰ā蛎唇拥碾[形炸彈現(xiàn)象curl http://api失敗但curl http://api.internal.net成功nslookup api返回api.internal.net的IP而nslookup api.internal.net卻失敗。根因分析/etc/resolv.conf中search internal.netnslookup api實(shí)際查詢api.internal.net但api.internal.net在DNS中不存在nslookup顯示NXDOMAIN而curl http://api因HTTP Host頭是api后端Nginx按server_name api;匹配所以能通——這是應(yīng)用層與DNS層的錯位。3步定位法grep search /etc/resolv.conf查看search域host -v api顯示詳細(xì)查詢過程看是否拼接了search域curl -v http://api 21 | grep Host:確認(rèn)HTTP請求頭是否含Host: api。1行修復(fù)命令sudo sed -i s/search.*/search ~./ /etc/resolv.confsearch ~.是特殊語法表示“不為任何域名拼接search域”徹底關(guān)閉拼接。若需保留部分search用search internal.net ~example.com表示example.com及其子域不拼接。3.5 場景五/etc/hosts條目被忽略——nsswitch.conf的權(quán)限陷阱現(xiàn)象/etc/hosts里寫了192.168.1.100 db-prod但ping db-prod報Unknown hostgetent hosts db-prod無輸出。根因分析nsswitch.conf中hosts:行漏掉了files如寫成hosts: dns則完全跳過/etc/hosts。或者/etc/hosts權(quán)限錯誤非644glibc會拒絕讀取。3步定位法cat /etc/nsswitch.conf | grep hosts檢查files是否存在ls -l /etc/hosts確認(rèn)權(quán)限為-rw-r--r--strace -e traceopenat ping db-prod 21 | grep hosts看ping是否嘗試打開/etc/hosts。1行修復(fù)命令sudo chmod 644 /etc/hosts sudo sed -i s/hosts:.*/hosts: files dns/ /etc/nsswitch.conf權(quán)限修正強(qiáng)制files優(yōu)先雙保險。注意nsswitch.conf修改后立即生效無需重啟。4. 深度避坑指南那些文檔里不會寫的12個致命細(xì)節(jié)以下是我三年來在數(shù)十個生產(chǎn)環(huán)境反復(fù)驗(yàn)證的“血淚教訓(xùn)”每一條都對應(yīng)一個曾讓我加班到凌晨的故障。它們不寫在man page里但比任何命令都重要。4.1resolv.conf的符號鏈接陷阱/etc/resolv.conf可能是軟鏈但內(nèi)容未必可信很多發(fā)行版如Ubuntu將/etc/resolv.conf軟鏈接到/run/systemd/resolve/stub-resolv.conf而后者由systemd-resolved生成。但resolved有個隱藏行為當(dāng)它檢測到上游DNS不可達(dá)時會主動清空/run/systemd/resolve/resolv.conf里的nameserver導(dǎo)致stub-resolv.conf也變空。此時cat /etc/resolv.conf顯示空白但ping仍能工作——因?yàn)閚sswitch.conf的files還在/etc/hosts條目生效。這種“表面故障實(shí)則正?!钡臓顟B(tài)會讓新手瘋狂重裝resolved。正確做法是永遠(yuǎn)用readlink -f /etc/resolv.conf確認(rèn)真實(shí)路徑再檢查該路徑內(nèi)容若為空直接sudo systemd-resolve --status看上游狀態(tài)而非盲目重啟服務(wù)。4.2systemd-resolved的緩存污染TTL過期前錯誤記錄永久生效systemd-resolved緩存遵循DNS TTL但有個例外當(dāng)它收到SERVFAIL響應(yīng)時會緩存該錯誤長達(dá)30秒硬編碼不可配。這意味著若某次DNS查詢因臨時網(wǎng)絡(luò)抖動返回SERVFAIL接下來30秒內(nèi)所有相同域名查詢都會直接失敗無論網(wǎng)絡(luò)是否已恢復(fù)。實(shí)測中我們用sudo systemd-resolve --flush-caches清緩存后故障立即消失。但更治本的方法是在/etc/systemd/resolved.conf中添加Cacheyes確保開啟和DNSStubListeneryes并監(jiān)控ResolvedCacheHit指標(biāo)發(fā)現(xiàn)緩存命中率異常高時主動flush-caches。4.3 容器DNS的--dns-opt參數(shù)ndots:5讓K8s服務(wù)發(fā)現(xiàn)變慢5倍Docker的--dns-opt ndots:N控制域名中“點(diǎn)號”數(shù)量閾值。默認(rèn)ndots:5意味著redis0個點(diǎn)會被當(dāng)作相對域名拼接search域查詢redis.default.svc.cluster.local而K8s的search域有4層default.svc.cluster.local svc.cluster.local cluster.local導(dǎo)致最多4次DNS查詢才能得到結(jié)果。線上我們將ndots設(shè)為1docker run --dns-opt ndots:1 ...這樣redis直接查A記錄redis.default.svc.cluster.local才走search拼接解析耗時從平均120ms降至22ms。4.4getent命令的隱藏開關(guān)-s參數(shù)可繞過nsswitch.confgetent hosts github.com按nsswitch.conf順序查但getent -s files hosts github.com強(qiáng)制只查/etc/hostsgetent -s dns hosts github.com強(qiáng)制只走DNS。這在調(diào)試nsswitch.conf配置是否生效時極有用——比如你懷疑files被跳過直接getent -s files hosts db-prod若返回IP說明/etc/hosts本身沒問題問題在nsswitch.conf或files模塊加載失敗。4.5dig與nslookup的本質(zhì)區(qū)別一個走系統(tǒng)配置一個走自定義服務(wù)器nslookup github.com默認(rèn)讀/etc/resolv.confdig github.com默認(rèn)走系統(tǒng)DNS即getaddrinfo路徑。所以當(dāng)systemd-resolved啟用時nslookup可能失敗因resolv.conf指向127.0.0.53而resolved異常但dig仍成功因dig內(nèi)部實(shí)現(xiàn)繞過了resolv.conf直連resolved的D-Bus。因此DNS調(diào)試必須用dig 127.0.0.53 github.com指定服務(wù)器和getent ahosts github.com走系統(tǒng)路徑雙驗(yàn)證單用nslookup會得出錯誤結(jié)論。4.6chattr i鎖死/etc/resolv.conf安全加固的反效果有些安全規(guī)范要求chattr i /etc/resolv.conf防止篡改但這會導(dǎo)致NetworkManager、systemd-resolved等服務(wù)無法更新DNS所有動態(tài)網(wǎng)絡(luò)如DHCP失效。線上環(huán)境應(yīng)避免i改用chmod 444 /etc/resolv.conf只讀chown root:root既防誤改又允許可信服務(wù)如systemd-resolved通過chown臨時修改權(quán)限再寫入。4.7nsswitch.conf的compat源號引入的NIS風(fēng)險hosts: files dns是安全的但hosts: compat會啟用NIS網(wǎng)絡(luò)信息服務(wù)/etc/hosts中開頭的行如netgroup會觸發(fā)NIS查詢。若NIS服務(wù)器不可達(dá)getaddrinfo會阻塞數(shù)秒。生產(chǎn)環(huán)境必須禁用compatgrep -r /etc/hosts清理所有行nsswitch.conf中hosts行嚴(yán)禁出現(xiàn)compat。4.8resolv.conf的options edns0EDNS協(xié)議擴(kuò)展的雙刃劍options edns0啟用EDNS0協(xié)議支持大于512字節(jié)的DNS響應(yīng)如DNSSEC。但某些老舊防火墻會丟棄EDNS0包導(dǎo)致查詢超時。若遇到dig short google.com成功但dig short google.com 8.8.8.8失敗大概率是EDNS0被攔截。臨時解決echo options no-edns0 | sudo tee -a /etc/resolv.conf。4.9systemd-resolved的LLMNR局域網(wǎng)內(nèi).local域名的隱形殺手LLMNR鏈路本地多播DNS默認(rèn)開啟會響應(yīng).local域名查詢。但若網(wǎng)絡(luò)中有Apple設(shè)備用Bonjour它們也響應(yīng).local導(dǎo)致resolved和Bonjour搶答解析結(jié)果隨機(jī)。企業(yè)內(nèi)網(wǎng)應(yīng)禁用sudo mkdir -p /etc/systemd/resolved.conf.d echo -e [Resolve]\nLLMNRno | sudo tee /etc/systemd/resolved.conf.d/no-llmnr.conf。4.10getaddrinfo的AI_ADDRCONFIG標(biāo)志IPv6環(huán)境下的連接黑洞glibc的getaddrinfo默認(rèn)帶AI_ADDRCONFIG標(biāo)志意思是“只返回與本機(jī)配置匹配的地址族”。若機(jī)器有IPv6地址但網(wǎng)絡(luò)不支持IPv6路由getaddrinfo會過濾掉所有AAAA記錄但某些應(yīng)用如舊版Java會因AI_ADDRCONFIG缺失而嘗試連接IPv6地址導(dǎo)致超時。統(tǒng)一方案在/etc/gai.conf末尾加precedence ::ffff:0:0/96 100強(qiáng)制IPv4優(yōu)先比改應(yīng)用代碼更可靠。4.11resolv.conf的rotate與timeout組合輪詢不等于負(fù)載均衡options rotate讓nameserver輪詢但timeout是單次查詢時限。若nameserver 1.1.1.1超時1秒nameserver 8.8.8.8立即被嘗試這看起來是負(fù)載均衡。但若1.1.1.1偶爾慢如200ms8.8.8.8永遠(yuǎn)得不到機(jī)會因?yàn)閞otate只在超時后觸發(fā)。真正均衡的做法是options timeout:1 attempts:1 rotate每次只試1次超時就輪到下一個確保每個DNS服務(wù)器都有均等機(jī)會。4.12systemd-resolved的DNSSEC驗(yàn)證安全與可用性的終極權(quán)衡DNSSEC開啟DNSSECallow-downgrade能防DNS劫持但會增加解析延遲需驗(yàn)證簽名且某些CDN如Cloudflare的DNSSEC配置錯誤會導(dǎo)致SERVFAIL。線上我們設(shè)為DNSSECallow-downgrade當(dāng)上游支持DNSSEC時驗(yàn)證不支持時降級為普通查詢兼顧安全與可用性。驗(yàn)證命令systemd-resolve --statistics | grep DNSSEC。5. 實(shí)戰(zhàn)案例復(fù)盤一次跨機(jī)房DNS故障的72小時攻堅(jiān)最后分享一個完整案例它濃縮了前述所有知識點(diǎn)。這不是理論推演而是我在某金融客戶現(xiàn)場的真實(shí)記錄。5.1 故障初現(xiàn)告警風(fēng)暴與表象迷惑周三上午10:23監(jiān)控平臺突現(xiàn)告警所有跨機(jī)房服務(wù)調(diào)用失敗錯誤日志清一色java.net.UnknownHostException: api.prod.internal。第一反應(yīng)是網(wǎng)絡(luò)中斷但ping各機(jī)房網(wǎng)關(guān)IP全通telnet api.prod.internal 443卻報Connection refused——等等telnet需要IP它怎么知道api.prod.internal的IP原來telnet也走getaddrinfo所以問題在DNS不在網(wǎng)絡(luò)層。我們立刻登錄跳板機(jī)執(zhí)行dig api.prod.internal 10.10.1.1主DNS返回NOERRORIP正確但getent ahosts api.prod.internal無輸出。矛盾出現(xiàn)了dig能查getent不能查。5.2 鏈路分段七層穿透式排查按前述七層鏈路我們逐層驗(yàn)證階段一應(yīng)用層strace -e tracegetaddrinfo telnet api.prod.internal 443確認(rèn)getaddrinfo被調(diào)用階段二nsswitchcat /etc/nsswitch.conf | grep hosts顯示hosts: files dns正常階段三/etc/hostsgrep api.prod.internal /etc/hosts為空排除階段四resolv.confcat /etc/resolv.conf顯示nameserver 10.10.1.1且search prod.internal正常階段五systemd-resolvedsystemctl status systemd-resolvedactive但sudo ss -tuln | grep :53無監(jiān)聽——resolved沒起來journalctl -u systemd-resolved | tail -20顯示Failed to bind to 127.0.0.53:53: Address already in use。查ss -tuln | grep :53發(fā)現(xiàn)dnsmasq在監(jiān)聽0.0.0.0:53。這就是場景二的端口沖突5.3 根因鎖定dnsmasq的bind-interfaces失效我們以為dnsmasq配置了bind-interfaces應(yīng)只監(jiān)聽127.0.0.1。但cat /etc/dnsmasq.conf | grep bind發(fā)現(xiàn)#bind-interfaces被注釋了原因是上周安全掃描工具自動備份并修改了配置文件但未重啟dnsmasq所以舊進(jìn)程仍在運(yùn)行新配置未生效。kill -9舊進(jìn)程后dnsmasq按新配置啟動釋放53端口systemd-resolved自動監(jiān)聽127.0.0.53getent ahosts api.prod.internal立即返回IP。5.4 深度復(fù)盤為什么故障持續(xù)72小時問題解決了但為什么從周三上午到周五下午才定位因?yàn)閐nsmasq的bind-interfaces注釋是掃描工具所為而dnsmasq