所需機器: 一台用作load balance server(簡稱LB), 三台DNS server(簡稱DNS-A, DNS-B, DNS-C), 當然後端要幾台是隨意...
架構: 類似NAT+private ip的網路
架設步驟:
1. 先把LB的NAT功能用pf防火牆設定好, 在此我們先用IPv4的設定:
LB external IP: 140.113.1.1/24
LB internal IP: 192.168.1.14/24
DNS-A: 192.168.1.1/24
DNS-B: 192.168.1.2/24
DNS-C: 192.168.1.3/24
之後的load balance功能於打造時將會用到pf的table, 因此建議用pf去設定NAT, 若用其他種防火牆的話請不要來信問我怎麼做.
pf.conf 如下:
ext_if="em1"
ext_ip="140.113.1.1"
int_if="em0"
int_ip="192.168.1.14"
int_lan="192.168.1.0/24"
table <dns_servers> persist {192.168.1.1,192.168.1.2,192.168.1.3}
set block-policy return
set skip on lo0
scrub in all
nat on $ext_if from $int_lan to !$int_lan -> $ext_ip
rdr on $ext_if proto udp from any to $ext_ip port 53 -> <dns_servers> port 53
pass all
2. 設定好並確認後端三台DNS server都能正常透過LB的NAT出去後, 再來把後端三台DNS service設定起來, 並確認由LB可以查詢得到後端三台DNS, 此部份若不會請自行找尋架設DNS server的資料, 再此不再著述.
3. 理論上, 這時外部向LB的external ip查詢, 應該已經可以正常回應, 此時即完成第一步, 也就是靜態的load balance, 平均分攤流量給後端三台DNS server.
4.接下來要加上監視後端DNS是否還活著, 若死掉就要從table dns_servers 移除, 反之則加入回來. 我的偵測方式是先用icmp ping, 然後用nsping測試, 故先到/usr/ports/dns/nsping裝nsping起來. 然後因為很可能會受到網路流量過大導致nsping卡住等狀況, 故再到/usr/ports/sysutils/timelimit裝timelimit.
5. 以下是自行撰寫的 icmp_ping.sh
#!/bin/sh
export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin:/root/bin
if [ -z "$1" ];
then
echo "usage: $0 <IP or hostname>"
exit
fi
ip=$1
count=3
reply=0
while [ $count -gt 0 ];
do
feedback=`ping -z 10 -c 1 -t 1 -i0.2 $ip 2>&1 |awk '{if($3=="Operation"){print "retry";exit};if($8=="packet"){if($7=="0.0%"){print "ok";exit}else{print "bad";exit}}}'`
if [ $feedback = "retry" ];
then
continue
fi
if [ $feedback = "ok" ];
then
reply=`expr $reply + 1`
fi
count=`expr $count - 1`
done
if [ $reply -gt 2 ];
then
echo "ok"
else
echo "bad"
此範例是去ping看看指定的IP, 會測試3次, 若達2次以上有回應即認定為正常並輸出 "ok", 反之則輸出 "bad". 完成後請自行測試是否能正常運作.
6. 以下是自行撰寫的 ns_ping.sh
#!/bin/sh
export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin:/root/bin
if [ -z "$1" ];
then
echo "usage: $0 <IP or hostname>"
exit
fi
ip=$1
timelimit -t 10 -T 20 nsping -c 3 -h www.nctu.edu.tw $ip | awk '{if(NF==18){if($9 >= "2"){print "ok"}else{print "bad"}}}'
其中要用什麼DN/IP做測試請自行修改, 此範例是去解看看 www.nctu.edu.tw, 會嘗試測試3次, 若達2次以上回應即認定為正常並輸出 "ok", 反之則輸出 "bad". 完成後請自行測試是否能正常運作.
7. 接下來是最重要的, 進行偵測並自動調整 table dns_servers, 以下是dns_detect.sh的程式碼:
#!/bin/sh
export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin:/root/bin
lockfile="/tmp/dns_detect.lock"
pwd="/root/bin/dns_detect"
pfcmd="pfctl -t dns_servers -T"
if [ -z "$1" ];
then
echo "detect dns servers if alive and remove/add to round-robin table"
echo "usage:$0 <IP1> <IP2> ..."
exit
fi
if [ -e $lockfile ];
then
exit
fi
touch $lockfile
while [ "$1" ];
do
if [ "`$pwd/icmp_ping.sh $1`" = "ok" ];
then
if [ "`$pwd/ns_ping.sh $1`" = "ok" ];
then
echo "$1 is alive"
$pfcmd add $1
else
echo "$1 icmp ok, but dns query failed"
$pfcmd delete $1
fi
else
echo "$1 icmp failed"
$pfcmd delete $1
fi
shift
done
rm $lockfile
在寫此程式時考量到可以一次測試所有的back-end DNS server, 故使用 shift 即可將一整排back-end server IPs 以空隔區分, 都當做參數放進來.
因為這個程式執行時可能會持續比較久, 為了避免重複呼叫, 故簡單用個lock file機制避免重複執行. 而此程式主要動作就是: 1. 進行icmp ping看看, 不通就直接跳去刪除該IP的動作, 通的話進行下一步驟. 2.進行ns query測試, 若不通也跳去刪IP動作, 若ok就加入該IP.
整個程式動作就是簡單說就是先ping看看指定的IP, 有反應再測試query, 都pass就把pf的table dns_servers加入該IP, 反之若任何一項測試不過就刪除該IP. 因pf會檢查是否重複, 故不用擔心重複加入或刪除.
8. 什麼? 還有? 是的, crontab只能設定到分鐘, 也就是若不幸真的有故障的back-end DNS server, 最遭的狀況會要一分鐘才會知道這件事. 為了讓偵測頻繁點, 故另寫一個簡單的shell script, 在一分鐘內間隔小一點, 重複檢查幾次再結束.
以下為dns_detect_loop.sh的程式碼:
#!/bin/sh
export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin:/root/bin
dns_servers="192.168.1.1 192.168.1.2 192.168.1.3"
lockfile="/tmp/dns_detect_loop.lock"
pwd="/root/bin/dns_detect"
sleeptime="20"
if [ -z "$1" ];
then
echo "detect dns servers per 10 seconds"
echo "usage:$0 <echo|batch>"
exit
fi
if [ -e $lockfile ];
then
exit
fi
touch $lockfile
timesleft=2
while [ $timesleft -gt 0 ];
do
if [ $1 = "echo" ];
then
$pwd/dns_detect.sh $dns_servers
else
$pwd/dns_detect.sh $dns_servers > /dev/null 2>&1
fi
timesleft=`expr $timesleft - 1`
if [ $timesleft -gt 0 ];
then
sleep $sleeptime
fi
done
rm $lockfile
簡單說就是每次測試2次, 中間暫停20秒, 這樣可以縮短到30秒以內一定偵測到故障發生, 或故障已排除. 可自行調整暫停的時間與要重複幾次, 並請做測試, 要在60秒內完成, 以免延誤到下一分鐘的偵測.
大致上就這樣, 應該可以讓你的DNS負荷分攤掉, 並於單一一台故障停機時不會造成任何問題.
沒有留言:
張貼留言