Edited at

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

More than 1 year has passed since last update.

ロードバランサは高いので、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でインストールと設定はするようにしてみた、詳細はこちら

リポジトリから対象パッケージ確認

[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 -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固有の問題で、この設定で実機で動くことは確認出来た。

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!