LinuxでL4のロードバランサを簡単に作る手順


ロードバランサは高いので、Linuxで比較的簡単にL4の負荷分散を行えるLVSは使いドコロが色々あり結構便利。
久々に作った時のメモがわりとして、今回は、LVSの構築手順と簡単なテスト結果を順に書いてみた。

## 構成
### ちょっと特殊な要件があり、負荷分散行うLVSも実際にレスポンス返すアプリも同じ筐体で動かしたいという前提で作った。
※最初の投稿だと、同一筐体で動かした時にBACKUP STATEのLVSがARP応答する設定になっていたので、加筆！

LVS単体サーバとしても下記の手順で同じように作れる

サーバはaa,bbの2台(vagrant上の仮想マシンとした)

* 同じ役割を持つサーバaa,bbをLVSを使って負荷分散したい
* LVS自身も冗長構成にして、aa,bbにやらせたい
* リクエスト元は、同一セグメントなのでDSR(Direct Server Return)を使う 
※NATは、戻す時に直接LVS介さずにリクエスト元に返してしまうから。通常LBのNATを使う時は、GatewayをLBにすることで通信を無理やりLB経由にしてる)

## LVSインストール
CentOS5系時代はパッケージがリポジトリ管理されていなかったので、ソースからインストールしていたがCentOS6になりリポジトリに入ったので簡単になった。
簡単、素晴らしい。

- 2015/08/26 追記
Ansibleの簡単なPlaybookでインストールと設定はするようにしてみた、詳細は[こちら](http://qiita.com/nagais/items/4d35af55a80c0b5a784d)

```
リポジトリから対象パッケージ確認
[vagrant@vag-LVSA network-scripts]$ yum list ipvsadm keepalived
ipvsadm.x86_64                                                     1.26-4.el6                                                     @base
keepalived.x86_64                                                  1.2.13-4.el6                                                   @base

インストール
[vagrant@vag-LVSA network-scripts]$ sudo yum install -y ipvsadm keepalived

バージョン確認
[vagrant@vag-LVSA network-scripts]$ sudo ipvsadm -v
ipvsadm v1.26 2008/5/15 (compiled with popt and IPVS v1.2.1)

[vagrant@vag-LVSA network-scripts]$ sudo keepalived --version
Keepalived v1.2.13 (10/15,2014)

```

自動起動設定もしておく

```
$ sudo chkconfig keepalived on
$ sudo chkconfig --list keepalived
keepalived      0:off   1:off   2:on    3:on    4:on    5:on    6:off
```


## LVSの設定

IPv4の転送を許可する

```
[vagrant@vag-LVSA network-scripts]$ sudo vim /etc/sysctl.conf
net.ipv4.ip_forward = 1  (0→1へ)

反映
[vagrant@vag-LVSA network-scripts]$ sudo sysctl -p
net.ipv4.ip_forward = 1
```

keepalivedは、ipvsadmをラップしているので設定はipvsadmコマンドは使わずに、keepalived.confで管理

###keepalived.confにベタ書きしてもいいんだけど、可読性を意識して下記のようなディレクトリ構成でincludeするよう構成。

```ディレクトリ構成
/etc/keepalived/keepalived.conf(VRRPの設定と下記2つをinclude)
　           |-/VirtualServers(バーチャルサーバの設定)
             |-/Nodes(ノードの設定)
```
##試験環境のconfigサンプル


```keepalievd.conf
! Configuration File for keepalived

global_defs {
   router_id LVS_TEST
}

vrrp_instance  VRRP_1{
    !priorityでMasterの判定をさせる為両方BACKUP
　　 state BACKUP
    !VRRPのインターフェースを指定
    interface eth1
    !VRRPのID(master_slaveで合わせる)
    virtual_router_id 51
    !master判定時の優先度(masterの値を大きくする)
    priority 100
    !VRRPの送信間隔
    advert_int 3
    !フェイルバックしない
    nopreempt
    !フェイルーバー時にコネクションテーブルを同期
    lvs_sync_daemon_interface bond0
    virtual_ipaddress {
        192.168.50.100
    }
    !下記2行は、同一筐体で動かす場合のみ必要になる(内容は下に記載するがVIPに対するarp応答をmaster on/backup offで切り替えている)
    notify_master "/bin/sh /etc/keepalived/lvs_master.sh"
    notify_backup "/bin/sh /etc/keepalived/lvs_backup.sh"
}
!読み込むバーチャルサーバを指定
include VirtualServers/*.conf
```

```/VirtualServers/abc_vs.conf
!VirtualServer毎の設定

!solrgrp_virrtual serverの指定
 virtual_server_group abcgrp {
  192.168.50.100 8888
 }
 !virtual serverの内容設定
 virtual_server group abcgrp {
     !スケジューリングアルゴリズム
     lvs_sched  wrr
     !ルーティング方式
     lvs_method  DR
     !対象プロトコル
     protocol    TCP
     !ヘルスチェック間隔
     delay_loop 5
     !real_serverのヘルスチェックに全て失敗した時にリクエストを振り向ける
     !sorry_server

include ../Nodes/aa.conf
include ../Nodes/bb.conf

}

```

```/Nodes/aa.conf
! SOLR1
real_server 192.168.50.10 8888 {
    weight 1
    !weight 0
! ヘルスチェックに失敗時、削除ではなくweightをゼロに
    inhibit_on_failure

    TCP_CHECK {
       connect_port 8888
       connect_timeout 10
    }
}

```

## Node側の設定(実際の振り分け先)

DSR構成にする為には、Linuxだと2パターンあるらしいが同一筐体の場合は②しか動かない。

#### ①iptablesのNATでPREROUTINGしてVIPあての通信を処理
```
$ sudo iptables -t nat -I PREROUTING -d 192.168.50.100 -j REDIRECT
```
→同一筐体だとLVSMaster機が初回に処理した時にARP登録され、次回以降のリクエストがLVS通らず直接ARP登録されたインターフェースへいくのでダメ。。

#### ②lo:0にループバックアドレス指定し、loはARP応答しない設定にする

②-1 VIPに応答する為のループバックインターフェースを追加 

```
[vagrant@vag-LVSA network-scripts]$ sudo vim /etc/sysconfig/network-scripts/ifcfg-lo:0
DEVICE=lo:0
IPADDR=192.168.50.100
NETMASK=255.255.255.255
ONBOOT=yes

networkサービス再起動
[vagrant@vag-LVSA network-scripts]$ sudo /etc/init.d/network restart

lo:0が追加されていることを確認
[vagrant@vag-LVSA network-scripts]$ ifconfig
lo:0      Link encap:Local Loopback
          inet addr:192.168.50.100  Mask:255.255.255.255
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
```

②-2 ループバックインターフェースのARP応答無効化()

#####A:リクエスト元サーバとリアルサーバ(LVSにぶら下がる)が同じセグメントにいる場合に必要

```
[vagrant@vag-LVSA network-scripts]$ sudo vim /etc/sysctl.conf 
追加
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2

反映
[vagrant@vag-LVSA network-scripts]$ sudo sysctl -p
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
```

#####B:リクエスト元サーバが同一セグメント且つLVSとリアルサーバが同一筐体の場合に必要
keepalived.confの[notify_master]と[notify_backup]を使い下記を現在のLVS STATEにより切り替える

- master機の時は、VIPに対するarp応答をする
- slave機の時は、VIPに対するarp応答をしない

MASTER時用

```/etc/keepalived/lvs_master.sh
#!/bin/sh
#lvs master起動時にARP応答する

/bin/sed -i -e 's/net.ipv4.conf.all.arp_ignore = 1//g;s/net.ipv4.conf.all.arp_announce = 2//g' /etc/sysctl.conf

/sbin/sysctl -p
```

BACKUP時用

```/etc/keepalived/lvs_backup.sh
#!/bin/sh
#lvs backup起動時にARP応答を無効化する

/bin/echo 'net.ipv4.conf.all.arp_ignore = 1'|/usr/bin/tee -a /etc/sysctl.conf
/bin/echo 'net.ipv4.conf.all.arp_announce = 2'|/usr/bin/tee -a /etc/sysctl.conf

/sbin/sysctl -p

```

振り分け試験の為に、httpdインストールして、Listen port 8888にして、DocumentRootにindex.htmを置く(書くまでもないので省略)

index.htmは下記のような感じで、どちらのサーバがレスポンス返してるかわかるようにしとく
LVSA機:LVSA
LVSB機:LVSB

## 試験結果
概ね問題なく動いている。
フェイルオーバやノードダウン時の切り離しも問題なし。(2年前くらいに作ったドキュメントに詳しくレポートは書いてるので省略)

今回は、LVSA,LVSB共にローカルのvagrant上に立てたので自分のマシンからVIPのindex.htmを叩きバランシングが聞いている事を確認する

```curlテスト
$ curl -XGET http://192.168.50.100:8888/index.htm
LVSB
$ curl -XGET http://192.168.50.100:8888/index.htm
LVSA
$ curl -XGET http://192.168.50.100:8888/index.htm
LVSB
$ curl -XGET http://192.168.50.100:8888/index.htm
LVSA
$ curl -XGET http://192.168.50.100:8888/index.htm
LVSB
```

たまにレスポンス返ってこなくて、コネクションテーブル見るとSYN_RECVになってるものがある。。
これが、vagrant上の問題なのかを切り分ける為にやはり実機での検証が必要となった。。。

### 上記問題は、Vagrant固有の問題で、この設定で実機で動くことは確認出来た。

```LVSのコネクションテーブル 
IPVS connection entries
pro expire state       source             virtual            destination
TCP 01:00  SYN_RECV    192.168.50.1:53017 192.168.50.100:8888 192.168.50.11:8888
TCP 01:43  FIN_WAIT    192.168.50.1:53011 192.168.50.100:8888 192.168.50.10:8888
TCP 01:00  SYN_RECV    192.168.50.1:52964 192.168.50.100:8888 192.168.50.11:8888
TCP 01:00  SYN_RECV    192.168.50.1:53021 192.168.50.100:8888 192.168.50.11:8888
TCP 01:50  FIN_WAIT    192.168.50.1:53015 192.168.50.100:8888 192.168.50.10:8888
TCP 01:53  FIN_WAIT    192.168.50.1:53019 192.168.50.100:8888 192.168.50.10:8888
TCP 01:00  SYN_RECV    192.168.50.1:53013 192.168.50.100:8888 192.168.50.11:8888
```

一通り作った後に、Serverspecでテストを流すとgood!