利用 DNS SRV 記錄為 Postfix 提供負(fù)載平衡
2011 年 3 月,蘋果公司提出 RFC 6186,描述了如何利用域名系統(tǒng)服務(wù)(DNS SRV)記錄來查找電子郵件的提交以及訪問服務(wù)。
DNS SRV 記錄的形態(tài)
DNS SRV 記錄定義在 RFC 2782 中,它指定在區(qū)域文件中,并且包含了服務(wù)名稱、傳輸協(xié)議規(guī)范、優(yōu)先級(jí)、權(quán)重、端口,以及提供該服務(wù)的主機(jī)。
_submission._tcp SRV 5 10 50 bruce.my-domain.com.
字段 | 值 | 意義 |
服務(wù)名稱 |
| 服務(wù)名為 submission |
傳輸協(xié)議規(guī)范 |
| 本服務(wù)使用 TCP 協(xié)議 |
優(yōu)先級(jí) |
| 服務(wù)器優(yōu)先級(jí)設(shè)為 5(數(shù)值越小,優(yōu)先級(jí)越高) |
權(quán)重 |
| 服務(wù)器應(yīng)承擔(dān)的負(fù)載部分 |
端口 |
| 服務(wù)器監(jiān)聽連接的端口 |
目標(biāo) |
| 提供此服務(wù)的服務(wù)器名稱 |
記錄解釋
服務(wù)器選擇算法
客戶端應(yīng)該按照 RFC 2782 中描述的方式解析 SRV 記錄。這意味著,首先嘗試聯(lián)系擁有最高優(yōu)先級(jí)(最小的優(yōu)先級(jí)數(shù)字)的服務(wù)器。如果該服務(wù)器無回應(yīng),那么重試聯(lián)系擁有同樣或者更低優(yōu)先級(jí)的下一臺(tái)服務(wù)器。當(dāng)有多臺(tái)服務(wù)器擁有同樣優(yōu)先級(jí)的時(shí)候,應(yīng)隨機(jī)選擇其中一臺(tái),但是必須確保選擇記錄的概率符合下列公式:
其中 i
是 SRV 記錄的標(biāo)識(shí),k
是具有相同優(yōu)先級(jí)的 SRV 記錄的數(shù)量。
在現(xiàn)實(shí)中,這意味著如果你有兩臺(tái)服務(wù)器,其中一臺(tái)的處理能力是另一臺(tái)的三倍,那么你應(yīng)該給第一臺(tái)服務(wù)器的權(quán)重賦于另一臺(tái)三倍的值。這樣就能保證更強(qiáng)大的服務(wù)器會(huì)接收到大約 75% 的客戶端請(qǐng)求,而另一臺(tái)接收大約 25% 的請(qǐng)求。
這些原則使得 SRV 記錄能夠同時(shí)作為客戶端自動(dòng)配置及在服務(wù)器之間分配工作負(fù)載的工具。
看看以下這個(gè)記錄的例子:
_submission._tcp SRV 0 0 2525 server-one
_submission._tcp SRV 1 75 2625 server-two
_submission._tcp SRV 1 25 2625 server-three
這里 server-one
總是會(huì)被首選來進(jìn)行聯(lián)系。如果 server-one
無回應(yīng),客戶端就會(huì)將剩下優(yōu)先級(jí)為 1 的兩個(gè)記錄順序打亂,生成一個(gè)從 0 到 100 的隨機(jī)數(shù),如果第一條記錄的運(yùn)行總和大于或者等于這個(gè)隨機(jī)數(shù),它就會(huì)嘗試去聯(lián)系這個(gè)記錄。否則,客戶端會(huì)倒序聯(lián)系所有服務(wù)器。注意,客戶端會(huì)向它優(yōu)先成功連接的服務(wù)器發(fā)送請(qǐng)求。
示例配置
請(qǐng)考慮以下這種情況。你想為大量的電腦配置 Postfix,使其通過公司的郵件服務(wù)器利用 SRV 記錄轉(zhuǎn)發(fā)外部電郵。為了達(dá)到這個(gè)目標(biāo),你需要在每臺(tái)電腦的 Postfix 中配置 relayhost
參數(shù),即郵件用戶代理(MUA)。如果將 relayhost
參數(shù)的值設(shè)置為 $mydomain
,你的機(jī)器將開始為你的域名查找
MX 記錄,并嘗試按照它們的優(yōu)先級(jí)順序發(fā)送郵件。這種方法雖然有效,但是可能會(huì)遇到負(fù)載平衡問題。Postfix
會(huì)使用優(yōu)先級(jí)最高的服務(wù)器,直到其變?yōu)闊o響應(yīng)才會(huì)聯(lián)系其他備用服務(wù)器。此外,如果你在環(huán)境中使用了動(dòng)態(tài)分配的端口,你無法指明哪個(gè)端口正在被特定的服務(wù)器使用。使用
SRV 記錄,你可以應(yīng)對(duì)這些挑戰(zhàn),并在需要改變服務(wù)器端口的時(shí)候維持服務(wù)器的平滑運(yùn)行。
區(qū)域文件
為了使得 DNS 服務(wù)器提供信息給客戶端,可以參考以下使用服務(wù)器 server-one
、server-two
、server-three
作為中繼,并把服務(wù)器 server-four
配置為接收測(cè)試郵件的區(qū)域文件示例。
$TTL 3600
@ IN SOA example-domain.com. root.example-domain.com. (
1571655122 ; 區(qū)域文件的序列號(hào)
1200 ; 刷新時(shí)間
180 ; 發(fā)生問題時(shí)的重試時(shí)間
1209600 ; 過期時(shí)間
10800 ) ; 查詢失敗時(shí)的最大緩存時(shí)間
;
IN NS ns1
IN A 192.168.2.0
;
ns1 IN A 192.168.2.2
server-one IN A 192.168.2.4
server-two IN A 192.168.2.5
server-three IN A 192.168.2.6
server-four IN A 192.168.2.7
_submission._tcp SRV 0 0 2525 server-one
_submission._tcp SRV 1 50 2625 server-two
_submission._tcp SRV 1 50 2625 server-three
@ MX 0 server-four
Postfix MUA 配置
設(shè)置客戶端機(jī)器去查找 SRV 記錄:
use_srv_lookup = submission
relayhost = example-domain.com:submission
通過這個(gè)配置,你的客戶端機(jī)器上的 Postfix 實(shí)例會(huì)聯(lián)絡(luò)到 example-domain
的 DNS 服務(wù)器,然后獲取郵件提交的 SRV 記錄。在這個(gè)例子中,server-one
有最高的優(yōu)先級(jí),Postfix
會(huì)先試圖連接它。然后,Postfix 隨機(jī)的選擇剩下的兩個(gè)服務(wù)器其中一個(gè)去嘗試連接。這個(gè)配置確保了大約有 50%
的機(jī)會(huì)會(huì)優(yōu)先聯(lián)系到服務(wù)器一。注意,SRV 記錄的權(quán)重值并不等同于百分比。你也可以用 1 和 1 這樣的值達(dá)到同樣的目標(biāo)。
同時(shí),Postfix 也知道 server-one
在監(jiān)聽 2525 端口,而 server-two
在監(jiān)聽 2625 端口。如果你正在緩存檢索到的 DNS 記錄,并且你動(dòng)態(tài)改變 SRV 記錄,那么設(shè)置一個(gè)低的生存時(shí)間(TTL)對(duì)你的記錄是很重要的。
整套設(shè)置
你可以通過下面的方式嘗試這個(gè)配置,包含 podman 和在此處提供的 compose 文件:
$ git clone https://github.com/TomasKorbar/srv_article
$ cd srv_article/environment
$ podman-compose up
$ podman exec -it article_client /bin/bash
root@client # ./senddummy.sh
root@client # exit
完成配置之后,你可以檢查日志,查看郵件是否經(jīng)過 server-one
并最終投遞到 server-four
。
$ podman stop article_server1
$ podman exec -it article_client /bin/bash
root@client # ./senddummy.sh
root@client # ./senddummy.sh
root@client # ./senddummy.sh
root@client # ./senddummy.sh
root@client # ./senddummy.sh
root@client # ./senddummy.sh
root@client # exit
現(xiàn)在 server-one
已經(jīng)關(guān)閉了,這六封郵件將會(huì)由 server-two
或者 server-three
中轉(zhuǎn)發(fā)出去。
仔細(xì)看一下 Dockerfiles 以更深地理解這個(gè)配置。
通過執(zhí)行:$ podman-compose down
完成示例的操作。