vagrantでロードバランサーの実験環境作成

勉強がてらubuntulvsを構築してみたので手順やハマったところをメモ

実験用の環境はvagrantで構築
nodeをたくさん立てる必要があるためdocker使ってメモリ節約したかったけど
dockerだと通信周りの設定でハマりそう&その部分は今回の趣旨と違うため次のステップとしとく

構築したい環境はこんな感じ

name ip:port vip:port 備考
lb0 192.168.0.11:80 192.168.0.2:80 IPVS
web0 192.168.0.3:80 real server 1 (nginx)
web1 192.168.0.4:80 real server 2 (nginx)
cli0 192.168.0.100 アクセス確認用クライアント

今回はDRで設定することにする
(lbにて振り分けたパケットが送信元へ返る際、lbは通らずに直接送信元へ返る)

lb0設定

virtual ipを設定する

$ sudo ip addr add 192.168.0.2/32 dev eth1 label eth1:0
# 確認
$ sudo ip addr

ipvsadmを入れる

$ sudo apt-get install ipvsadm

ipvsadmはlinux kernelに含まれるIPVSという機能を利用してアクセス振り分けを設定するためのツール。設定は意外と単純でわかりやすい。

設定はコマンドでinteractiveに行うこともできるが今回はファイルに残すことにする
ipvsadm.rulesというファイル名にしておく

$ vi ipvsadm.rules
-C
-A -t 192.168.0.2:80 -s rr
-a -t 192.168.0.2:80 -r 192.168.0.3:80 -g
-a -t 192.168.0.2:80 -r 192.168.0.4:80 -g
$ sudo ipvsadm --restore < ipvsadm.rules
# 確認
$ sudo ipvsadm
IP Virtual Server version 1.2.1 (size=4096)                            
Prot LocalAddress:Port Scheduler Flags                                 
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn  
TCP  192.168.0.2:80 wlc                                                
  -> 192.168.0.3:80               Route   1      0          0          
  -> 192.168.0.4:80               Route   1      0          0          

まず-Aで振り分けたいアクセスを指定
-s rrでラウンドロビンによる振り分けになる
-aで上記のアクセスの振り分け先を追加する
-gとすることでDRによる転送を行う

real server設定(web0で説明)

まずnginxを入れて設定

$ sudo apt-get install nginx
$ echo "web0" | sudo tee /usr/share/nginx/html/index
# 確認
$ curl http://192.168.0.3
web0

192.168.0.2宛のアクセスを受け入れるための設定
/etc/network/interfacesに以下の設定を追記

auto lo:0
iface lo:0 inet static
      address 192.168.0.2
      netmask 255.255.255.255

設定を反映

$ sudo ifup lo:0
# 確認
$ ip addr

192.168.0.2に対するarpのリクエストに返答しないよう設定
/etc/sysctl.confに以下を追記

net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2

設定を反映

$ sudo sysctl -p

確認

cli0からlb0のvipにアクセスしてweb0とweb1が交互に帰ってくればOK

$ while true; do curl http://192.168.0.2; sleep 1; done
web0
web1
web0
...

卜ラブルシューティング

最後の確認時に想定した応答が返ってこない場合のチェックポイント

lb0からreal serverへhttpアクセスできるか

lb0にてcurlを使って正しい応答が返ってくるか確認

$ curl http://192.168.0.3
web0
$ curl http://192.168.0.4
web1

返ってこない場合はreal serverにてnginxのプロセスが起動しているか確認

cli0からのアクセスがlb0を経由しているか

real server側での設定がうまくいっていないとcli0から直接web0などにアクセスがいってしまう。
cli0にて以下を確認

cli0 $ arp -a
? (192.168.0.3) at 08:00:27:98:d7:cd [ether] on eth1
? (192.168.0.4) at 08:00:27:d1:3c:2e [ether] on eth1
? (192.168.0.11) at 08:00:27:85:8a:96 [ether] on eth1
? (10.0.2.2) at 52:54:00:12:35:02 [ether] on eth0
? (10.0.2.3) at 52:54:00:12:35:03 [ether] on eth0
? (192.168.0.2) at 08:00:27:85:8a:96 [ether] on eth1

ここでvip(192.168.0.2)とreal serverのipに紐づくmacアドレスが一致している場合がある。real server側でarpに応答しない設定を入れたが

  • うまく設定できていない場合
  • 設定前にcli0からvipへのアクセスを行っていた場合

にvipに対するmacアドレスがreal serverのもので設定されてしまう。その場合はarpのキャッシュを消す

$ sudo arp -d 192.168.0.2
# 消えたこと確認
$ sudo arp -a | grep "0.2"
? (192.168.0.2) at <incomplete> on eth1

この状態でreal serverのarpに応答しない設定を入れて再度curlでアクセス
それぞれのipアドレスに対して正しいmacアドレスが設定されていればOK
正しいmacアドレスは各サーバにてip addrとすることで確認できる

web0, web1が192.168.0.2へのパケットに応答しているか

lo:0へ192.168.0.2を割り当てる設定がうまくいっていない場合、アクセスは来るが応答はしない状態になる。これを確認するにはtcpdumpを用いる。
例えばweb0にて

$ sudo tcpdump net 192.168.0.0/24 -i eth1 -n

これでcli0からcurlで192.168.0.2へアクセスすると、web0へアクセスが振られた場合にログが出てくるはず。例えばこんなの

04:56:31.674022 IP 192.168.0.100.50853 > 192.168.0.3.80: Flags [S], seq 4034527911, win 29200, options [mss 1460,sackOK,TS val 235469 ecr 0,nop,wscale 5], length 0

見るのはここだけでOK

192.168.0.100.50853 > 192.168.0.3.80: Flags [S]

これはsynパケットなので、応答が返る場合にはこの直後に以下のようなログが出てackが返るはず

192.168.0.3:80 > 192.168.0.100.50853: Flags [S.]

lo:0の設定がうまくいっていない場合、synパケットが自分宛だと認識できないため捨ててしまい、それに対する応答は当然返さない。その場合はsynパケットが2回くらい続いてから接続が切断されているはず(自分のところではそうだった)

lo:0が正しく設定されているかは各real serverにてip addrしてみて設定が入っているか確認する。また、/etc/network/interfacesへの設定が間違っていないかなど確認する(書式とか)

サーバのipアドレスに192.168.0.1を使っている

今回の趣旨から外れるところだが、自分が一番ハマったのはこれ。
vagrant環境ではvagrant host(自分のPC)のアドレスとして.1が設定される。PC側の設定を見ると例えばこんな感じになっている

host$ ifconfig
vboxnet4: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> 
        ether 0a:00:27:00:00:04 
        inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255

そのため、192.168.0.1を別のサーバのipアドレスに設定していた場合は衝突してうまく通信ができなくなる。自分はlb0のアドレスとして最初にこれを設定していてハマった。このときはcli0でのarpキャッシュにて、192.168.0.1と192.168.0.2が異なっていることでなんとか気づけた。
だがその後よく見ると、vagrant up時に「.1となるアドレスは衝突するため設定するな」というwarningが出ていた。。

それでもまだうまくいかない

tcpdumpでログを見つつどこでパケットが途切れているか確認する。また、設定の途中でいろいろ試しているとipアドレスmacアドレスの対応がうまくいっていないことも多くあったのでその辺りを確認する。また、振り分け時の方法としてnatが指定されている場合は今回の設定とは別の設定が必要になるので注意。