AWSでLVSをつかってロードバランシングしてみる


構成

Webサーバの負荷分散ならELBで良いですがUDPにも対応しているのでLVS(Linux Virtual Server)で構築してみたいと思います。

LVSもWebサーバも同じサブネット(10.0.0.0/24)に設置します。
Webを閲覧する人はVPCの外側にいます。

まずは、インスタンス作成時にAWS Marketplace で「CentOS7」を検索して選択してください。LVSもWebサーバもCentOS7で作成します。

バージョン確認

# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)

設定 Webサーバ1

障害調査のためtcpdumpを入れておく。

# yum install -y tcpdump

Webサーバにするのでhttpdをインストールする。

# yum install -y httpd

# systemctl enable httpd

# systemctl start httpd

webページを作成する

/var/www/html/index.html
<html>Web1</html>

この時点でブラウザーにwebサーバのグローバルIPアドレスを指定して作成したwebサイトが表示されることを確認します。

デフォルトゲートウェイをLVSにします。
SSHで直接ログインできなくなります。以後、LVSを踏み台にしてログインすることになります。やり方は ssh -i とかで検索してください。

/etc/sysconfig/network
~
~
GATEWAY=10.0.0.127
# systemctl restart network

LVS経由で外部とつながるようにするためElastic IPを外します。

設定 Webサーバ2

障害調査のためtcpdumpを入れておく。(Webサーバ1と同じ)
Webサーバにするのでhttpdをインストールする。(Webサーバ1と同じ)
webページを作成する(Webサーバ1とは異なる)

/var/www/html/index.html
<html>Web2</html>

LVS経由で外部とつながるようにするためElastic IPを外します。

デフォルトゲートウェイをLVSにします。(Webサーバ1と同じ)
SSHで直接ログインできなくなります。以後、LVSを踏み台にしてログインすることになります。やり方は ssh -i とかで検索してください。

設定 LVS 【NAT方式】

障害調査のためtcpdumpを入れておく。(Webサーバ1と同じ)

実験なのでSELinuxは無効にしておく。

/etc/selinux/config
SELINUX=disabled

AWSマネジメントコンソール画面から送信元/送信先チェックをFalseにしておく

LVSの管理用にipvsadmをインストール

# yum install -y ipvsadm

# echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf

# sysctl -p 

# touch /etc/sysconfig/ipvsadm 

# systemctl enable ipvsadm

# systemctl start ipvsadm

LVSの設定を行います。
分散方式にはNAT方式とDSR方式とトンネル方式の3つあります。
DSR方式とトンネル方式は行きはLVSを経由しますが帰りはLVS経由しないで直接レスポンスを返す方式です。LVSの負荷が低くなるのですがインターネット側とは疎通がとれませんでしたのでNAT方式で設定します。
(DSR方式だとAWSにおいてIPスプーフィングに該当する行為とみなされてパケットをdropされてしまうのだと予想しています。トンネル方式はチャレンジしたことはありません。)

# ipvsadm -C
 仮想サービスを登録
# [ipvsadm -A -t (サービス用IP:ポート) -s (分散方式)]
# ipvsadm -A -t 10.0.0.127:80 -s rr 
 バックエンドサーバーを登録
  [ipvsadm -a -t (サービス用IP:ポート) -r (実サーバのIP:ポート) -m] (m で masquerading (NAT))
# ipvsadm -a -t 10.0.0.127:80 -r 10.0.0.185:80 -m 
# ipvsadm -a -t 10.0.0.127:80 -r 10.0.0.86:80 -m 
 テーブル確認
# ipvsadm -l 
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  ip-10-0-0-127.ap-northeast-1 rr
  -> 10.0.0.86:http               Masq    1      0          0
  -> 10.0.0.185:http              Masq    1      0          0

(ipvsadmのインストールとipvsadmによるLVSの設定方法はServer World様 https://www.server-world.info/query?os=CentOS_7&p=lvs より引用しました)

結果

LVSのグローバルIPアドレスをブラウザーで指定すると作成したweb画面が最初の1回目は見れますがリロードするとみれません。数分放置してからリロードすると見れますがもう一度リロードするとまた見れなくなります。
これでは使い物になりません。

不具合調査

まずは振り分けをやめてwebサーバ1だけにアクセスするように変更します。

# ipvsadm -C
# ipvsadm -A -t 10.0.0.127:80 -s rr 
# ipvsadm -a -t 10.0.0.127:80 -r 10.0.0.185:80 -m 
# ipvsadm -l 
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  ip-10-0-0-127.ap-northeast-1 rr
  -> 10.0.0.185:http              Masq    1      0          0

状況は変わらず最初の1回目は見れますがその後はだめです。

tcpdumpを使ってみる

-e オプションを使えば宛先MACアドレスと送信元MACアドレスが表示されるのでパケットがどこに行ってしまったのか分るはず。
まずはLVSとWebSVのMACアドレスを調べておきます。

LVS
# ip a
~

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 06:68:6c:b6:09:7a brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.127/24 brd 10.0.0.255 scope global dynamic eth0
~
WebSV1
# ip a
~

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 06:ac:e5:1b:cd:bc brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.185/24 brd 10.0.0.255 scope global dynamic eth0
~

LVSとWebSV1の両方でtcpdumpしてみます。

# tcpdump -i eth0 -e  port 80
WebSV
[root@ip-10-0-0-185 ~]# tcpdump -i eth0 -e  port 80
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
06:08:49.089890 06:68:6c:b6:09:7a (oui Unknown) > 06:ac:e5:1b:cd:bc (oui Unknown), ethertype IPv4 (0x0800), length 66: KD036012062116.au-net.ne.jp.58288 > ip-10-0-0-185.ap-northeast-1.compute.internal.http: Flags [S], seq 2389359173, win 64240, options [mss 1360,nop,wscale 8,nop,nop,sackOK], length 0
06:08:49.089940 06:ac:e5:1b:cd:bc (oui Unknown) > 06:d5:df:b7:0b:e8 (oui Unknown), ethertype IPv4 (0x0800), length 66: ip-10-0-0-185.ap-northeast-1.compute.internal.http > KD036012062116.au-net.ne.jp.58288: Flags [S.], seq 1871335019, ack 2389359174, win 26883, options [mss 8961,nop,nop,sackOK,nop,wscale 7], length 0

上手く行くときは
LVS(06-68-6c-b6-09-7a)からWebSV1(06-ac-e5-1b-cd-bc)にパケットが送られていて次に
WebSV1(06-ac-e5-1b-cd-bc)から LVS(06-68-6c-b6-09-7a)に戻りパケットが送られています。

上手く行かないときは
LVS(06-68-6c-b6-09-7a)からWebSV1(06-ac-e5-1b-cd-bc)にパケットが送られていて次に
WebSV1(06-ac-e5-1b-cd-bc)から 謎の装置(06-d5-df-b7-0b-e8)に戻りパケットが送られています。
ip neigh でarpテーブルを確認してみます。

WebSV
# ip neigh
10.0.0.1 dev eth0 lladdr 06:d5:df:b7:0b:e8 STALE
10.0.0.2 dev eth0 lladdr 06:d5:df:b7:0b:e8 STALE
10.0.0.127 dev eth0 lladdr 06:68:6c:b6:09:7a REACHABLE

謎の装置はVPCで予約されているIP(10.0.0.0~10.0.0.3,10.0.0.255)なのでVPCルータですね。
ルータに直接パケットを送るとおそらくIPスプーフィングとみなされてパケットは捨てられてしまうと思われます。不安定なのは戻りパケットをLVSに送ったり、ルータに送ったりしているからなんでしょう。

安定化を目指して次回はこれらを試してみようと思います。
1.デフォルトゲートウェイの設定方法を変えてみる。
2.LVSのネットワークインタフェースを2枚にして、Webサーバを別サブネットに設置する。