原文連結:https://fuckcloudnative.io/posts/install-coredns-on-macos/
CoreDNS 是 Golang 編寫的一個外掛式 DNS 伺服器,是 Kubernetes 1.13 後所內建的預設 DNS 伺服器。CoreDNS 的目標是成為 cloud-native 環境下的 DNS 伺服器和服務發現解決方案,即:
Our goal is to make CoreDNS the cloud-native DNS server and service discovery solution.
它有以下幾個特性:
-
外掛化(Plugins)
基於 Caddy 伺服器框架,CoreDNS 實現了一個外掛鏈的架構,將大量應用端的邏輯抽象成 plugin 的形式(如 Kubernetes 的 DNS 服務發現,Prometheus 監控等)暴露給使用者。CoreDNS 以預配置的方式將不同的 plugin 串成一條鏈,按序執行 plugin 的邏輯。從編譯層面,使用者選擇所需的 plugin 編譯到最終的可執行檔案中,使得執行效率更高。CoreDNS 採用 Go 編寫,所以從具體程式碼層面來看,每個 plugin 其實都是實現了其定義的 interface 的元件而已。第三方只要按照 CoreDNS Plugin API 去編寫自定義外掛,就可以很方便地整合於 CoreDNS。
-
配置簡單化
引入表達力更強的 DSL,即
Corefile
形式的配置檔案(也是基於 Caddy 框架開發)。 -
一體化的解決方案
區別於
kube-dns
,CoreDNS 編譯出來就是一個單獨的二進位制可執行檔案,內建了 cache,backend storage,health check 等功能,無需第三方元件來輔助實現其他功能,從而使得部署更方便,記憶體管理更為安全。
其實從功能角度來看,CoreDNS 更像是一個通用 DNS 方案(類似於 BIND
),然後通過外掛模式來極大地擴充套件自身功能,從而可以適用於不同的場景(比如 Kubernetes)。正如官方部落格所說:
CoreDNS is powered by plugins.
1. Corefile 介紹
Corefile
是 CoreDNS 的配置檔案(源於 Caddy 框架的配置檔案 Caddyfile),它定義了:
server
以什麼協議監聽在哪個埠(可以同時定義多個 server 監聽不同埠)- server 負責哪個
zone
的權威(authoritative)DNS 解析 - server 將載入哪些外掛
常見地,一個典型的 Corefile 格式如下所示:
ZONE:[PORT] {
[PLUGIN] ...
}
- ZONE : 定義 server 負責的 zone,
PORT
是可選項,預設為 53; - PLUGIN : 定義 server 所要載入的 plugin。每個 plugin 可以有多個引數;
比如:
. {
chaos CoreDNS-001
}
上述配置檔案表達的是:server 負責根域 .
的解析,其中 plugin 是 chaos
且沒有引數。
定義 server
一個最簡單的配置檔案可以為:
.{}
即 server 監聽 53 埠並不使用外掛。如果此時在定義其他 server,要保證監聽埠不衝突;如果是在原來 server 增加 zone,則要保證 zone 之間不衝突,如:
. {}
.:54 {}
另一個 server 執行於 54 埠並負責根域 .
的解析。
又如:
example.org {
whoami
}
org {
whoami
}
同一個 server 但是負責不同 zone 的解析,有不同外掛鏈。
定義 Reverse Zone
跟其他 DNS 伺服器類似,Corefile 也可以定義 Reverse Zone
(反向解析 IP 地址對應的域名):
0.0.10.in-addr.arpa {
whoami
}
或者簡化版本:
10.0.0.0/24 {
whoami
}
可以通過 dig
進行反向查詢:
$ dig -x 10.0.0.1
使用不同的通訊協議
CoreDNS 除了支援 DNS 協議,也支援 TLS
和 gRPC
,即 DNS-over-TLS 和 DNS-over-gRPC 模式:
tls://example.org:1443 {
#...
}
2. 外掛的工作模式
當 CoreDNS 啟動後,它將根據配置檔案啟動不同 server ,每臺 server 都擁有自己的外掛鏈。當有 DNS 請求時,它將依次經歷如下 3 步邏輯:
- 如果有當前請求的 server 有多個 zone,將採用貪心原則選擇最匹配的 zone;
- 一旦找到匹配的 server,按照 plugin.cfg 定義的順序執行外掛鏈上的外掛;
- 每個外掛將判斷當前請求是否應該處理,將有以下幾種可能:
-
請求被當前外掛處理
外掛將生成對應的響應並回給客戶端,此時請求結束,下一個外掛將不會被呼叫,如 whoami 外掛;
-
請求被當前外掛以 Fallthrough 形式處理
如果請求在該外掛處理過程中有可能將跳轉至下一個外掛,該過程稱為 fallthrough,並以關鍵字
fallthrough
來決定是否允許此項操作,例如 host 外掛,當查詢域名未位於 /etc/hosts,則呼叫下一個外掛; -
請求在處理過程被攜帶 Hint
請求被外掛處理,並在其響應中新增了某些資訊(hint)後繼續交由下一個外掛處理。這些額外的資訊將組成對客戶端的最終響應,如
metric
外掛;
3. CoreDNS 如何處理 DNS 請求
如果 Corefile 為:
coredns.io:5300 {
file db.coredns.io
}
example.io:53 {
log
errors
file db.example.io
}
example.net:53 {
file db.example.net
}
.:53 {
kubernetes
proxy . 8.8.8.8
log
health
errors
cache
}
從配置檔案來看,我們定義了兩個 server(儘管有 4 個區塊),分別監聽在 5300
和 53
埠。其邏輯圖可如下所示:
每個進入到某個 server 的請求將按照 plugin.cfg
定義順序執行其已經載入的外掛。
從上圖,我們需要注意以下幾點:
- 儘管在
.:53
配置了health
外掛,但是它併為在上面的邏輯圖中出現,原因是:該外掛並未參與請求相關的邏輯(即並沒有在外掛鏈上),只是修改了 server 配置。更一般地,我們可以將外掛分為兩種:- Normal 外掛:參與請求相關的邏輯,且插入到外掛鏈中;
- 其他外掛:不參與請求相關的邏輯,也不出現在外掛鏈中,只是用於修改 server 的配置,如
health
,tls
等外掛;
4. 配置 CoreDNS
既然 CoreDNS 如此優秀,我用它來抵禦偉大的防火長城豈不美哉?研究了一圈,發現技術上還是可行的,唯一的一個缺點是不支援使用代理,不過你可以通過 proxychians-ng 或 proxifier 來強制使用代理。下面開始折騰。
具體的思路其實非常簡單,就是將國內的域名查詢請求轉發到 114 等國內的公共 DNS 伺服器,將國外的域名查詢請求轉發到 8.8.8.8 等國外的公共 DNS 伺服器。然而 CoreDNS 的外掛鏈有點反直覺,同一個外掛鏈上的每一個外掛只能出現一次,如果只使用 forward
外掛是滿足不了需求的。
CoreDNS 原來還有個外掛叫 proxy
,功能和 forward
類似,目測好像同時利用 proxy
和 forward
外掛就可以實現我們的需求了。但理想與現實的差距總是很大,不知道從什麼時候開始,CoreDNS 官方編譯的二進位制檔案已經沒有 proxy
外掛了,真是氣人。
dnsredir
偶然間發現了一個第三方外掛 dnsredir,目測可以解決我的所有問題。該外掛綜合了 proxy
和 forward
外掛的所有優點,支援 UDP、TCP、DNS-over-TLS 和 DNS-over-HTTPS,也支援多個後端,還具備健康檢查和故障轉移的功能,真是太香了!
它的語法是這樣的:
dnsredir FROM... {
to TO...
}
-
FROM...
是一個檔案列表,包含了匹配的域名和解析該域名的伺服器,說白了就是 dnsmasq 所使用的格式,直接看例子:server=/0-100.com/114.114.114.114 server=/0-100.com/114.114.114.114
為什麼要用這種格式呢?當然是為了方便啦。
為什麼這樣會方便呢?當然是為了可以直接用上 FelixOnMars的大陸區域名列表了。。。FelixOnMars 同時還提供了
Google
和Apple
的域名列表,這在某些地區某些ISP可以得到國內映象的 IP,從而加速訪問,想想就刺激。 -
當然,除了使用檔案列表外,還可以使用
.
,類似於上面所說的根域。這個外掛最大的亮點是可以在外掛鏈中重複使用 dnsredir 外掛,只要FROM...
不重複就行。 -
to TO...
用來將 DNS 解析請求發給上游 DNS 伺服器。支援幾乎所有 DNS 協議,例如:dns://1.1.1.1 8.8.8.8 tcp://9.9.9.9 udp://2606:4700:4700::1111 tls://1.1.1.1@one.one.one.one tls://8.8.8.8 tls://dns.quad9.net doh://cloudflare-dns.com/dns-query json-doh://1.1.1.1/dns-query json-doh://dns.google/resolve ietf-doh://dns.quad9.net/dns-query
增強版 CoreDNS
dnsredir 雖香,但大家別忘了,它是第三方外掛,官方預設的二進位制檔案是不包含該外掛的。你可以選擇自己編譯,但如果經常需要升級怎麼辦?總不能每次都手動編譯吧,也太累了。
好在有位大佬已經通過 CI/CD
流程將所需的第三方外掛都整合編譯進去了,並定期更新,簡直就是我等的福音。大佬的專案地址為:
現在只需要下載對應作業系統的二進位制檔案,到處拷貝,就可以執行了。
下面統統以 MacOS 為例作講解。Openwrt
的玩法也一樣,參考本文的方法論即可,具體本文就不展開了。
直接下載二進位制檔案:
$ wget 'https://appveyorcidatav2.blob.core.windows.net/missdeer-15199/coredns-custom-build/1-7-1-514/idbodwxwywg1xgdg/distrib/coredns-linux-amd64.zip?sv=2015-12-11&sr=c&sig=BhMWcOVtDuaETyz2DcjpOr9GdvkpNVOqoIa7iWFpFNQ%3D&st=2020-12-23T15%3A26%3A19Z&se=2020-12-23T15%3A32%3A19Z&sp=r'
$ $ tar zxf coredns-linux-amd64.zip
$ mv coredns-linux-amd64/coredns /usr/local/bin/
配置
要深入瞭解 CoreDNS,請檢視其文件,及 plugins 的介紹。下面是我的配置檔案:
cat > /usr/local/etc/Corefile <<EOF
# https://coredns.io/plugins/cache/
(global_cache) {
cache {
# [5, 60]
success 65536 3600 300
# [1, 10]
denial 8192 600 60
prefetch 1 60m 10%
}
}
.:7913 {
ads {
default-lists
blacklist https://raw.githubusercontent.com/privacy-protection-tools/anti-AD/master/anti-ad-domains.txt
whitelist https://files.krnl.eu/whitelist.txt
log
auto-update-interval 24h
list-store ads-cache
}
errors
hosts {
fallthrough
}
health
prometheus :9153
import global_cache
template ANY AAAA {
rcode NXDOMAIN
}
dnsredir accelerated-domains.china.conf google.china.conf apple.china.conf mydns.conf {
expire 15s
max_fails 3
health_check 3s
policy round_robin
path_reload 2s
to 114.114.114.114 223.5.5.5 119.29.29.29
}
dnsredir . {
expire 60s
max_fails 5
health_check 5s
policy random
spray
to tls://8.8.8.8@dns.google tls://8.8.4.4@dns.google
to tls://1.1.1.1@1dot1dot1dot1.cloudflare-dns.com tls://1.0.0.1@1dot1dot1dot1.cloudflare-dns.com
# Global TLS server name
# tls_servername cloudflare-dns.com
}
log
loop
reload 6s
}
EOF
- hosts :
hosts
是 CoreDNS 的一個 plugin,這一節的意思是載入/etc/hosts
檔案裡面的解析資訊。hosts 在最前面,則如果一個域名在 hosts 檔案中存在,則優先使用這個資訊返回; - fallthrough : 如果
hosts
中找不到,則進入下一個 plugin 繼續。缺少這一個指令,後面的 plugins 配置就無意義了; - cache : 溯源得到的結果,快取指定時間。類似 TTL 的概念;
- reload : 多久掃描配置檔案一次。如有變更,自動載入;
- errors : 列印/儲存錯誤日誌;
- dnsredir : 這是重點外掛。第一段 dnsredir 配置使用了 4 個檔案列表,均是 FelixOnMars的大陸區域名列表,這裡我還加了一個自定義的檔案列表
mydns.conf
。第二段 dnsredir 配置表示預設的解析配置,可以理解為故障轉移,如果某個域名沒有匹配到任何一個檔案列表,就使用第二段 dnsredir 的上游 DNS 伺服器進行解析。通過這樣的配置方式,就實現了將國內的域名查詢請求轉發到 114 等國內的公共 DNS 伺服器,將國外的域名查詢請求轉發到 8.8.8.8 等國外的公共 DNS 伺服器。
講一下我自己的理解:
- 配置檔案類似於 nginx 配置檔案的格式;
- 最外面一級的大括號,對應『服務』的概念。多個服務可以共用一個埠;
- 往裡面一級的大括號,對應 plugins 的概念,每一個大括號都是一個 plugin。這裡可以看出,plugins 是 CoreDNS 的一等公民;
- 服務之間順序有無關聯沒有感覺,但 plugins 之間是嚴重順序相關的。某些 plugin 必須用
fallthrough
關鍵字流向下一個 plugin; - plugin 內部的配置選項是順序無關的;
- 從 plugins 頁面的介紹看,CoreDNS 的功能還是很強的,既能輕鬆從 bind 遷移,還能相容 old-style dns server 的運維習慣;
- 從 CoreDNS 的效能指標看,適合做大型服務。
注意:該方案的前提是能夠強制讓 CoreDNS 使用代理,或者更精確一點,讓 8.8.8.8 和 8.8.4.4 使用代理。這裡的方法比較複雜一點,本文就不介紹了。如果你實在不知道怎麼辦,可以將 8.8.8.8 這一行刪除,直接使用 Cloudflare 提供的 DNS 服務,雖然響應有點慢,但好在可以訪問。
如果你無法忍受 Cloudflare 的響應速度,可以考慮使用國內的無汙染 DNS:紅魚 DNS。然後直接一勞永逸:
cat > /usr/local/etc/Corefile <<EOF
# https://coredns.io/plugins/cache/
(global_cache) {
cache {
# [5, 60]
success 65536 3600 300
# [1, 10]
denial 8192 600 60
prefetch 1 60m 10%
}
}
.:7913 {
ads {
default-lists
blacklist https://raw.githubusercontent.com/privacy-protection-tools/anti-AD/master/anti-ad-domains.txt
whitelist https://files.krnl.eu/whitelist.txt
log
auto-update-interval 24h
list-store ads-cache
}
errors
hosts {
fallthrough
}
health
prometheus :9153
import global_cache
template ANY AAAA {
rcode NXDOMAIN
}
dnsredir accelerated-domains.china.conf google.china.conf apple.china.conf mydns.conf {
expire 15s
max_fails 3
health_check 3s
policy round_robin
path_reload 2s
to 114.114.114.114 223.5.5.5 119.29.29.29
}
dnsredir . {
expire 60s
max_fails 5
health_check 5s
policy random
spray
to doh://13800000000.rubyfish.cn
}
log
loop
reload 6s
}
EOF
這樣 CoreDNS 就不用擔心走代理的問題了。
定時更新國內域名列表
大陸域名列表每天都會更新,所以還需要寫個指令碼來更新檔案列表。不用檢查檔案是否存在了,直接簡單粗暴無腦更新:
$ cat > /usr/local/bin/update_coredns.sh <<EOF
#!/bin/bash
rm accelerated-domains.china.conf
wget https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf -O /usr/local/etc/accelerated-domains.china.conf
rm apple.china.conf
wget https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf -O /usr/local/etc/apple.china.conf
rm google.china.conf
wget https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/google.china.conf -O /usr/local/etc/google.china.conf
EOF
$ sudo chmod +x /usr/local/bin/update_coredns.sh
先執行一遍該指令碼,更新 Corefile 的配置:
$ /usr/local/bin/update_coredns.sh
然後通過 Crontab
製作定時任務,每隔兩天下午兩點更新域名列表:
$ crontab -l
0 14 */2 * * /usr/local/bin/update_coredns.sh
開機自啟
MacOS 可以使用 launchctl 來管理服務,它可以控制啟動計算機時需要開啟的服務,也可以設定定時執行特定任務的指令碼,就像 Linux crontab 一樣, 通過加裝 *.plist
檔案執行相應命令。Launchd 指令碼儲存在以下位置, 預設需要自己建立個人的 LaunchAgents
目錄:
~/Library/LaunchAgents
: 由使用者自己定義的任務項/Library/LaunchAgents
: 由管理員為使用者定義的任務項/Library/LaunchDaemons
: 由管理員定義的守護程式任務項/System/Library/LaunchAgents
: 由 MacOS 為使用者定義的任務項/System/Library/LaunchDaemons
: 由 MacOS 定義的守護程式任務項
我們選擇在 /Library/LaunchAgents/
目錄下建立 coredns.plist
檔案,內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>coredns</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/coredns</string>
<string>-conf</string>
<string>/usr/local/etc/Corefile</string>
</array>
<key>StandardOutPath</key>
<string>/var/log/coredns.stdout.log</string>
<key>StandardErrorPath</key>
<string>/var/log/coredns.stderr.log</string>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
設定開機自動啟動 coredns:
$ sudo launchctl load -w /Library/LaunchAgents/coredns.plist
檢視服務:
$ sudo launchctl list|grep coredns
61676 0 coredns
$ sudo launchctl list coredns
{
"StandardOutPath" = "/var/log/coredns.stdout.log";
"LimitLoadToSessionType" = "System";
"StandardErrorPath" = "/var/log/coredns.stderr.log";
"Label" = "coredns";
"TimeOut" = 30;
"OnDemand" = false;
"LastExitStatus" = 0;
"PID" = 61676;
"Program" = "/usr/local/bin/coredns";
"ProgramArguments" = (
"/usr/local/bin/coredns";
"-conf";
"/usr/local/etc/Corefile";
);
};
檢視埠號:
$ sudo ps -ef|egrep -v grep|grep coredns
0 81819 1 0 2:54下午 ?? 0:04.70 /usr/local/bin/coredns -conf /usr/local/etc/Corefile
$ sudo lsof -P -p 81819|egrep "TCP|UDP"
coredns 81819 root 5u IPv6 0x1509853aadbdf853 0t0 TCP *:5302 (LISTEN)
coredns 81819 root 6u IPv6 0x1509853acd2f39ab 0t0 UDP *:5302
coredns 81819 root 7u IPv6 0x1509853aadbdc493 0t0 TCP *:53 (LISTEN)
coredns 81819 root 8u IPv6 0x1509853acd2f5a4b 0t0 UDP *:53
coredns 81819 root 9u IPv6 0x1509853ac63bfed3 0t0 TCP *:5301 (LISTEN)
coredns 81819 root 10u IPv6 0x1509853acd2f5d03 0t0 UDP *:5301
大功告成,現在你只需要將系統的 DNS IP 設定為 127.0.0.1
就可以了。
驗證
$ doggo www.youtube.com @udp://127.0.0.1
NAME TYPE CLASS TTL ADDRESS NAMESERVER
www.youtube.com. CNAME IN 293s youtube-ui.l.google.com. 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 172.217.14.110 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 172.217.11.174 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 172.217.5.206 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 172.217.5.78 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 172.217.14.78 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 142.250.72.238 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 216.58.193.206 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 142.250.68.110 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 142.250.68.78 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 172.217.4.142 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 142.250.68.14 127.0.0.1:53
搞定。
什麼?你問我 doggo
是個啥?掃描下方二維碼關注公眾號:
公眾號後臺回覆 doggo 即可獲取你想要的東西?
5. 參考資料
Kubernetes 1.18.2 1.17.5 1.16.9 1.15.12離線安裝包釋出地址http://store.lameleg.com ,歡迎體驗。 使用了最新的sealos v3.3.6版本。 作了主機名解析配置優化,lvscare 掛載/lib/module解決開機啟動ipvs載入問題, 修復lvscare社群netlink與3.10核心不相容問題,sealos生成百年證書等特性。更多特性 https://github.com/fanux/sealos 。歡迎掃描下方的二維碼加入釘釘群 ,釘釘群已經整合sealos的機器人實時可以看到sealos的動態。