2013年2月6日水曜日

LVS(DSR方式)でロードバランサー

■LVSとは
LVS(Linux Virtual Server)は、Linuxサーバにおいて、
HPC、HA、ロードバランサの機能を動作させるソフトウェア群とのことです。

・IPアドレスに届いたパケットを必要に応じて、リアルサーバーに転送する。
・転送先のリアルサーバーを複数指定できる。
・パケットの転送では、各リアルサーバーに均等に分散することや、重み付けをして分散も出来る。
 転送方法には、NAT(Network Address Translation)、ダイレクト・ルーティング(DSR方式)、IP-IPカプセル化(トンネリング)がある。

■パケットの転送方法の違いについて
・NAT(Newtwork Address Translation)
 エンドユーザーからパケットを受け取ると、送信先とIPアドレスが、選択されたリアルサーバーのものに置き換えられる。
 返信パケットが通る際にも置き換えられる?
 LVSサーバーを必ず通るのでLVSサーバーの負荷が上がる。
 Apacheなどに残るIPアドレスは接続元のIPアドレスになる。

・ダイレクト・ルーティング(DR)方式
 エンドユーザーからのパケットを直接リアルサーバーに転送する。IPアドレスなどは変更されないので、
 リアルサーバーはエンドユーザーに直接応答を送信する。
 LVSサーバーの負荷は下がる。
 リアルサーバーも直接クライアントと通信が出来ないとダメなため、リアルサーバー全部にグローバルIPが必要となる。
(ただし、ローカルループバックを使えばリアルサーバーもグローバルIPはいらない?)
 DSR(Direct Server Return)とも呼ぶ。  経路が1つしかないので帰りもLVSを通過しますがIPアドレスの書き換えを行わないのでApacheなどのアクセスログに残るのはLVSのアドレスになる。
 負荷分散を行う機器とサービスを提供するサーバは、同一のネットワーク上に置かなければならないという制限がある。

・IP-IPカプセル化(トンネリング)
 あるIPアドレス宛のパケットを、同じネットワークまたは別のネットワーク上のほかのアドレスにリダイレクトできる。

LVSを行うためには、IPVS対応のkernelと、ipvsadmが必要になりますので、
インストールしていきたいと思います。

その前に、ipvsadmをインストールする際の注意点として次の点があります。

■注意点
・ipvsadmはkernelのIPVSのバージョンに依存するのでipvsのインストールをする必要がある。
 ※CentOS 5 系では既に kernel に機能としては組み込まれていますが制御するインタフェースが無い。
・ipvsが対応しているkernelのバージョンが必要になるため、まずは、kerneのバージョンを確認する。
・ipvsadmをコンパイルしたときのIPVSのバージョンと、
 インストールされているipvsのバージョンが異なっていると期待した動作をしないことがあるのであわせる必要がある。

■テストを行ったサーバー構成
また、今回のサーバー構成は次のようになっています。
社内のローカルのネットワークで行っています。

サーバーA(ipvsadmをインストールする)
・IPアドレス(192.168.10.220)
・バーチャルIPアドレス(VIP)(192.168.10.223)
・ipvsadmをインストールする

サーバーB(webサーバー)
・IPアドレス(192.168.10.221)

サーバーC(webサーバー)
・IPアドレス(192.168.10.222)


それでは、サーバーAでLVSのインストール&設定を行っていきます。
■手順1)必要なものをインストールする。
#
yum install kernel-devel libnl* popt* make gcc openssl-devel bridge-utils
■手順2)ipvsのバージョンを確認する
OSのリリース番号を出力する
#
uname -r
2.6.18-194.11.3.el5
/usr/src/kernels/にOSのリリース番号と一致したディレクトリがあるので、
そのディレクトリと一致するディレクトリにあるinclude/net/ip_vs.hを利用する
grep IP_VS_VERSION_CODE /usr/src/kernels/2.6.18-194.11.1.el5-x86_64/include/net/ip_vs.h
#define IP_VS_VERSION_CODE      0x010201
0x010201と表示された場合には、ipvsのバージョンは「1.2.1」になる。
■手順3)ipvsadmのインストール
#
yum install ipvsadm

===============================================================================================================================================================================
 Package                                   Arch                                     Version                                       Repository                              Size
===============================================================================================================================================================================
Installing:
 ipvsadm                                   x86_64                                   1.24-13.el5                                   base                                    34 k

Transaction Summary
===============================================================================================================================================================================
Install       1 Package(s)
Upgrade       0 Package(s)

Total download size: 34 k
Is this ok [y/N]: y
Downloading Packages:
ipvsadm-1.24-13.el5.x86_64.rpm                                                                                                                          |  34 kB     00:00
Running rpm_check_debug
Running Transaction Test
Finished Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing     : ipvsadm                                                                                                                                                 1/1

Installed:
  ipvsadm.x86_64 0:1.24-13.el5
インストールされたipvsadmのバージョンを確認する。
# ipvsadm -v
fipvsadm v1.24 2003/06/07 (compiled with popt and IPVS v1.2.0)
IPVS v1.2.0となっているので、 手順2で現在のkernelで利用されているipvsのバージョンと一致しない・・・orz
#削除
yum remove ipvsadm
仕方ないのでソースからコンパイルを行うことに・・・orz
ipvsの公式サイトから最新のものをダウンロードする
#ダウンロード
wget http://www.linux-vs.org/software/kernel-2.6/ipvsadm-1.26.tar.gz

#解凍
tar zxf ipvsadm-1.26.tar.gz

#/root/ipvsadm-1.26に移動
cd ipvsadm-1.26
IPVS は Redhat(CentOS)系で利用されている/usr/src/kernelを見てくれないとのことなので、
/usr/src/linuxディレクトリを作成し、シンボリックリンクを作成する。
#ディレクトリ作成
mkdir -p /usr/src/linux

#シンボリックリンクの作成
#2.6.18-194.11.1.el5-x86_64の部分は各OSのkernelのバージョン番号にあわせること。
#ls -alt /usr/src/kernels/などで表示する
ln -s /usr/src/kernels/2.6.18-194.11.1.el5-x86_64/include /usr/src/linux/include
※削除する場合にはシンボリックリンクを削除すればよい。(rm /usr/src/linux/include)

makeを行う
#/root/ipvsadm-1.26/
# make
make -C libipvs
make[1]: ディレクトリ `/root/ipvsadm-1.26/libipvs' に入ります
gcc -Wall -Wunused -Wstrict-prototypes -g -fPIC -DLIBIPVS_USE_NL  -DHAVE_NET_IP_VS_H -c -o libipvs.o libipvs.c
libipvs.h:13 から include されたファイル中,
                 libipvs.c:23 から:
ip_vs.h:16:31: error: netlink/genl/genl.h: そのようなファイルやディレクトリはありません
ip_vs.h:17:31: error: netlink/genl/ctrl.h: そのようなファイルやディレクトリはありません
In file included from libipvs.h:13,
                 from libipvs.c:23:
ip_vs.h:520: error: 配列の型が不完全要素型を持っています
ip_vs.h:521: error: 配列の型が不完全要素型を持っています
ip_vs.h:522: error: 配列の型が不完全要素型を持っています
ip_vs.h:523: error: 配列の型が不完全要素型を持っています
ip_vs.h:524: error: 配列の型が不完全要素型を持っています
ip_vs.h:525: error: 配列の型が不完全要素型を持っています
libipvs.c: In function ‘ipvs_nl_message’:
libipvs.c:57: 警告: implicit declaration of function ‘nlmsg_alloc’
libipvs.c:57: 警告: assignment makes pointer from integer without a cast
libipvs.c:61: 警告: implicit declaration of function ‘genlmsg_put’
libipvs.c:61: error: ‘NL_AUTO_PID’ undeclared (first use in this function)
libipvs.c:61: error: (Each undeclared identifier is reported only once
libipvs.c:61: error: for each function it appears in.)
libipvs.c:61: error: ‘NL_AUTO_SEQ’ undeclared (first use in this function)
libipvs.c: In function ‘ipvs_nl_noop_cb’:
libipvs.c:69: error: ‘NL_OK’ undeclared (first use in this function)
libipvs.c:70: 警告: control reaches end of non-void function
libipvs.c: In function ‘ipvs_nl_send_message’:
libipvs.c:78: 警告: implicit declaration of function ‘nlmsg_free’
libipvs.c:82: 警告: implicit declaration of function ‘genl_connect’
libipvs.c:85: 警告: implicit declaration of function ‘genl_ctrl_resolve’
libipvs.c:96: 警告: implicit declaration of function ‘nl_socket_modify_cb’
libipvs.c:99: 警告: passing argument 2 of ‘nl_send_auto_complete’ from incompatible pointer type
libipvs.c:102: 警告: implicit declaration of function ‘nl_recvmsgs_default’
libipvs.c: In function ‘ipvs_getinfo_parse_cb’:
libipvs.c:149: 警告: implicit declaration of function ‘nlmsg_hdr’
libipvs.c:149: 警告: initialization makes pointer from integer without a cast
libipvs.c:152: 警告: implicit declaration of function ‘genlmsg_parse’
libipvs.c:159: 警告: implicit declaration of function ‘nla_get_u32’
libipvs.c:162: error: ‘NL_OK’ undeclared (first use in this function)
libipvs.c:163: 警告: control reaches end of non-void function
libipvs.c: In function ‘ipvs_getinfo’:
libipvs.c:176: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_flush’:
libipvs.c:199: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_nl_fill_service_attr’:
libipvs.c:215: 警告: implicit declaration of function ‘nla_nest_start’
libipvs.c:215: 警告: assignment makes pointer from integer without a cast
libipvs.c:219: 警告: implicit declaration of function ‘NLA_PUT_U16’
libipvs.c:222: 警告: implicit declaration of function ‘NLA_PUT_U32’
libipvs.c:225: 警告: implicit declaration of function ‘NLA_PUT’
libipvs.c:229: 警告: implicit declaration of function ‘NLA_PUT_STRING’
libipvs.c:236: 警告: implicit declaration of function ‘nla_nest_end’
libipvs.c:239: 警告: label ‘nla_put_failure’ defined but not used
libipvs.c: In function ‘ipvs_add_service’:
libipvs.c:255: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_update_service’:
libipvs.c:276: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_del_service’:
libipvs.c:296: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_zero_service’:
libipvs.c:321: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_nl_fill_dest_attr’:
libipvs.c:334: 警告: assignment makes pointer from integer without a cast
libipvs.c:348: 警告: label ‘nla_put_failure’ defined but not used
libipvs.c: In function ‘ipvs_add_dest’:
libipvs.c:366: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_update_dest’:
libipvs.c:396: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_del_dest’:
libipvs.c:425: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_set_timeout’:
libipvs.c:452: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c:454: 警告: label ‘nla_put_failure’ defined but not used
libipvs.c: In function ‘ipvs_start_daemon’:
libipvs.c:473: 警告: assignment makes pointer from integer without a cast
libipvs.c:483: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_stop_daemon’:
libipvs.c:504: 警告: assignment makes pointer from integer without a cast
libipvs.c:514: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_parse_stats’:
libipvs.c:530: 警告: implicit declaration of function ‘nla_parse_nested’
libipvs.c:548: 警告: implicit declaration of function ‘nla_get_u64’
libipvs.c: In function ‘ipvs_services_parse_cb’:
libipvs.c:562: 警告: initialization makes pointer from integer without a cast
libipvs.c:592: 警告: implicit declaration of function ‘nla_get_u16’
libipvs.c:598: 警告: implicit declaration of function ‘nla_data’
libipvs.c:599: 警告: passing argument 2 of ‘memcpy’ makes pointer from integer without a cast
libipvs.c:604: 警告: implicit declaration of function ‘nla_get_string’
libipvs.c:605: 警告: passing argument 2 of ‘strncpy’ makes pointer from integer without a cast
libipvs.c:610: 警告: passing argument 2 of ‘strncpy’ makes pointer from integer without a cast
libipvs.c:614: 警告: implicit declaration of function ‘nla_memcpy’
libipvs.c: In function ‘ipvs_get_services’:
libipvs.c:650: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_dests_parse_cb’:
libipvs.c:728: 警告: initialization makes pointer from integer without a cast
libipvs.c:759: 警告: passing argument 2 of ‘memcpy’ makes pointer from integer without a cast
libipvs.c: In function ‘ipvs_get_dests’:
libipvs.c:813: 警告: assignment makes pointer from integer without a cast
libipvs.c:829: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_get_service’:
libipvs.c:939: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_timeout_parse_cb’:
libipvs.c:972: 警告: initialization makes pointer from integer without a cast
libipvs.c:986: error: ‘NL_OK’ undeclared (first use in this function)
libipvs.c:987: 警告: control reaches end of non-void function
libipvs.c: In function ‘ipvs_get_timeout’:
libipvs.c:1005: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
libipvs.c: In function ‘ipvs_daemon_parse_cb’:
libipvs.c:1023: 警告: initialization makes pointer from integer without a cast
libipvs.c:1048: 警告: passing argument 2 of ‘strncpy’ makes pointer from integer without a cast
libipvs.c:1051: error: ‘NL_OK’ undeclared (first use in this function)
libipvs.c:1052: 警告: control reaches end of non-void function
libipvs.c: In function ‘ipvs_get_daemon’:
libipvs.c:1072: 警告: passing argument 2 of ‘ipvs_nl_send_message’ from incompatible pointer type
make[1]: *** [libipvs.o] エラー 1
make[1]: ディレクトリ `/root/ipvsadm-1.26/libipvs' から出ます
make: *** [libs] エラー 2
エラー・・・ガ━━(;゚Д゚)━━ン!!

それじゃ、1.25で試してみる・・・。
再度実行する前に、makeの中間ファイルを削除する。「make clean」を実行しておく。
#/root/ipvsadm-1.26/でmake cleanする(中間ファイルを削除しておく)
# make clean
rm -f ipvsadm ipvsadm.spec ipvsadm-1.26.tar.gz
rm -rf debian/tmp
find . -name '*.[ao]' -o -name "*~" -o -name "*.orig" \
                  -o -name "*.rej" -o -name core | xargs rm -f
make -C libipvs clean
make[1]: ディレクトリ `/root/ipvsadm-1.26/libipvs' に入ります
rm -f *.[ao] *~ *.orig *.rej core *.so
make[1]: ディレクトリ `/root/ipvsadm-1.26/libipvs' から出ます

ipvsadm-1.25.tar.gzをダウンロード。
#ダウンロード
wget http://www.linux-vs.org/software/kernel-2.6/ipvsadm-1.25.tar.gz

#解凍
# tar zxf ipvsadm-1.25.tar.gz

#/root/ipvsadm-1.25に移動する
cd ipvsadm-1.25

#/root/ipvsadm-1.25でmake
make
make -C libipvs
make[1]: ディレクトリ `/root/ipvsadm-1.25/libipvs' に入ります
gcc -Wall -Wunused -Wstrict-prototypes -g -fPIC -DLIBIPVS_USE_NL  -DHAVE_NET_IP_VS_H -c -o libipvs.o libipvs.c
libipvs.h:13 から include されたファイル中,
                 libipvs.c:23 から:
ip_vs.h:16:31: error: netlink/genl/genl.h: そのようなファイルやディレクトリはありません
ip_vs.h:17:31: error: netlink/genl/ctrl.h: そのようなファイルやディレクトリはありません
In file included from libipvs.h:13,
                 from libipvs.c:23:
ip_vs.h:510: error: 配列の型が不完全要素型を持っています
ip_vs.h:511: error: 配列の型が不完全要素型を持っています
ip_vs.h:512: error: 配列の型が不完全要素型を持っています
ip_vs.h:513: error: 配列の型が不完全要素型を持っています

:
省略
:

make[1]: *** [libipvs.o] エラー 1
make[1]: ディレクトリ `/root/ipvsadm-1.25/libipvs' から出ます
make: *** [libs] エラー 2
またえr(略

公式を良く見てみたら、1.25,1.26はkernel 2.6.28-rc3 or later以降用みたいなので、
今回のkernel2.6,18は使えない?感じでした・・・orz

なので、大人しく1.24を利用することにしました(`・ω・´)シャキーン
1.24はkernelが2.6.10~2.6.27.4が対象になる。

ipvsadm-1.24-6.src.rpm (for kernel between 2.6.10 and 2.6.27.4) - December 10, 2005

#ダウンロード
wget http://www.linux-vs.org/software/kernel-2.6/ipvsadm-1.24.tar.gz

#解凍
tar zxf ipvsadm-1.24.tar.gz

#/root/ipvsadm-1.24に移動する
cd ipvsadm-1.24

#/root/ipvsadm-1.24でmake
make
make -C libipvs
make[1]: ディレクトリ `/root/ipvsadm-1.24/libipvs' に入ります
gcc -Wall -Wunused -Wstrict-prototypes -g -O2 -I/usr/src/linux/include  -DHAVE_NET_IP_VS_H -c -o libipvs.o libipvs.c
ar rv libipvs.a libipvs.o
ar: creating libipvs.a
a - libipvs.o
make[1]: ディレクトリ `/root/ipvsadm-1.24/libipvs' から出ます
gcc -Wall -Wunused -Wstrict-prototypes -g -O2 -I/usr/src/linux/include -I.. -I. -DVERSION=\"1.24\" -DSCHEDULERS=\""rr|wrr|lc|wlc|lblc|lblcr|dh|sh|sed|nq"\" -DHAVE_POPT -DHAVE_NET_IP_VS_H -c -o ipvsadm.o ipvsadm.c
ipvsadm.c: In function ‘print_largenum’:
ipvsadm.c:1564: 警告: フィールド幅 should have type ‘int’, but argument 2 has type ‘size_t’
gcc -Wall -Wunused -Wstrict-prototypes -g -O2 -I/usr/src/linux/include -I.. -I. -DVERSION=\"1.24\" -DSCHEDULERS=\""rr|wrr|lc|wlc|lblc|lblcr|dh|sh|sed|nq"\" -DHAVE_POPT -DHAVE_NET_IP_VS_H -c -o config_stream.o config_stream.c
gcc -Wall -Wunused -Wstrict-prototypes -g -O2 -I/usr/src/linux/include -I.. -I. -DVERSION=\"1.24\" -DSCHEDULERS=\""rr|wrr|lc|wlc|lblc|lblcr|dh|sh|sed|nq"\" -DHAVE_POPT -DHAVE_NET_IP_VS_H -c -o dynamic_array.o dynamic_array.c
gcc -Wall -Wunused -Wstrict-prototypes -g -O2 -o ipvsadm ipvsadm.o config_stream.o dynamic_array.o libipvs/libipvs.a -L/usr/lib -lpopt
/usr/bin/ld: skipping incompatible /usr/lib/libpopt.so when searching for -lpopt
/usr/bin/ld: skipping incompatible /usr/lib/libpopt.a when searching for -lpopt
/usr/bin/ld: skipping incompatible /usr/lib/libc.so when searching for -lc
/usr/bin/ld: skipping incompatible /usr/lib/libc.a when searching for -lc
次のようなエラーが出る場合には、/usr/src/linux/includeにシンボリックリンクが正しく張られていない可能性があります。
CentOSだと、make時に正しくkernelソースを見に行ってくれないのでシンボリックリンクを張る必要がある。
libipvs.c:23 から include されたファイル中:
libipvs.h:14:23: error: net/ip_vs.h: そのようなファイルやディレクトリはありません
In file included from libipvs.c:23:
問題なさそうなので/usr/localにインストールを行う。
#
#/usr/local 下にインストールする
# make -e BUILD_ROOT=/usr/local install
make -C libipvs
make[1]: ディレクトリ `/root/ipvsadm-1.24/libipvs' に入ります
make[1]: `all' に対して行うべき事はありません.
make[1]: ディレクトリ `/root/ipvsadm-1.24/libipvs' から出ます
if [ ! -d /usr/local/sbin ]; then mkdir -p /usr/local/sbin; fi
install -m 0755 -s ipvsadm /usr/local/sbin
install -m 0755 ipvsadm-save /usr/local/sbin
install -m 0755 ipvsadm-restore /usr/local/sbin
[ -d /usr/local/usr/man/man8 ] || mkdir -p /usr/local/usr/man/man8
install -m 0644 ipvsadm.8 /usr/local/usr/man/man8
install -m 0644 ipvsadm-save.8 /usr/local/usr/man/man8
install -m 0644 ipvsadm-restore.8 /usr/local/usr/man/man8
if [ -d /usr/local/etc/rc.d/init.d ]; then \
                  install -m 0755 ipvsadm.sh /usr/local/etc/rc.d/init.d/ipvsadm; \
                fi
(゚∀゚)キタコレ!!

■手順4)ipvsadmのパスをあわせる。 /usr/local/sbinにインストールしたので、パスを合わせておく。
パスを合わせない場合には、フルパスでコマンドを実行する。
とりあえず、ホームに戻る。
cd ~/
.bash_profileの編集
#
vi .bash_profile

#10行目辺りの行を下のように変更する
PATH=$PATH:$HOME/bin
↓
PATH=$PATH:$HOME/bin:/usr/local/sbin
一旦ログアウトなどをしてログインしなおしたりして先程のパスを反映させる。
ipvsadm -v
ipvsadm v1.24 2005/12/10 (compiled with popt and IPVS v1.2.1)
上記の設定を行わない場合にはフルパス用で実行するようにする。
# which ipvsadm
/usr/local/sbin/ipvsadm

#フルパスで、
# /usr/local/sbin/ipvsadm -v
ipvsadm v1.24 2005/12/10 (compiled with popt and IPVS v1.2.1)
IPVS v1.2.1と表記されていれば問題ないです!( ´∀`)bグッ! 設定をリセットしておく。
ipvsadm -C
■手順5)IPフォワードの設定
この設定によりバーチャルサーバでは他のマシンからのリクエストを転送することが可能になります。
vi /etc/sysctl.conf
#7行目辺りを下のように修正する
net.ipv4.ip_forward = 0
↓
net.ipv4.ip_forward = 1
設定の反映
sysctl -p
設定の確認
cat /proc/sys/net/ipv4/ip_forward
1
rp_filterも無効にした方が良い?(今回は未設定) LVSで実現するロードバランサ では、「rp_filterを無効にする」とも書かれていました。
Linuxには送信元アドレスを偽装したパケットを排除する機能(rp_filter)があります。
後述するDSR構成では、応答パケットがrp_filterに引っかかってしまって通信できなくなるので、
あらかじめ無効にしておきます。ただし、無効にするのはeth1(内部セグメント側)のrp_filterのみです。
eth0(外部セグメント側)のrp_filterは無効にしないで下さい。
あくまでも「中から外に出ていくパケット」に対するrp_filterを無効にするだけですのでご注意下さい。
中から外に出て行くパケットのみ無効にする
net.ipv4.conf.eth1.rp_filter=0
■手順6)ネットワークインターフェイスの設定(サーバーA)
現在のeth0の設定ファイルの確認。
192.168.10.220が設定されている事を確認する。
#
vi /etc/sysconfig/network-scripts/ifcfg-eth0
# Broadcom Corporation NetXtreme BCM5754 Gigabit Ethernet PCI Express
DEVICE=eth0
BOOTPROTO=static
BROADCAST=192.168.10.255
HWADDR=00:1A:A0:53:4A:34
IPADDR=192.168.10.220
NETMASK=255.255.255.0
NETWORK=192.168.10.0
ONBOOT=yes
サーバーAに192.168.10.223でバーチャルIPアドレス(VIP)を設定します。
ユーザーにはこのIPアドレスにアクセスさせます。
vi /etc/sysconfig/network-scripts/ifcfg-eth0:0
# Broadcom Corporation NetXtreme BCM5754 Gigabit Ethernet PCI Express
TYPE=Ethernet
DEVICE=eth0:0
BOOTPROTO=none
NETMASK=255.255.255.255
IPADDR=192.168.10.223
USERCTL=no
IPV6INIT=no
ONPARENT=yes
PEERDNS=yes
NAME=eth0:0
■手順7)ipvsadmの設定
何も登録されていないことを確認する。
ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
ポート80、ラウンドロビン(rr)でリアルサーバにバランスさせます。
ipvsadm -A -t 192.168.10.223:80 -s rr
追加された事を確認すること。
# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.10.224:80 rr
次は、192.168.10.223で接続されたものを、次のサーバーへ転送する設定を行う
#192.168.10.223からリアルサーバのIPアドレスの192.168.10.221を追加します。
ipvsadm -a -t 192.168.10.223:80 -r 192.168.10.221 -g

#192.168.10.223からリアルサーバのIPアドレスの192.168.10.132を追加します。
ipvsadm -a -t 192.168.10.223:80 -r 192.168.10.132 -g
次のようなエラーが出る場合には、コマンドのオプションなどがおかしい、
または、1つ前の「ipvsadm -A -t ...」コマンドが失敗している可能性があるので見直すこと。
Service not defined
パケットの転送設定が追加されたことを確認する
# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.10.223:80 rr
  -> 192.168.10.132:80            Route   1      0          0
  -> 192.168.10.221:80            Route   1      0          0
OSが再起動されたときに消えないように保存する。
# ipvsadm -S
-A -t 192.168.10.223:http -s rr
-a -t 192.168.10.223:http -r 192.168.10.132:http -g -w 1
-a -t 192.168.10.223:http -r 192.168.10.221:http -g -w 1
■手順8)ネットワークインターフェイスの再起動&確認
/etc/init.d/network restart
ネットワークインターフェイスの確認
# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:1A:A0:53:4A:34
          inet addr:192.168.10.220  Bcast:192.168.10.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:315 errors:0 dropped:0 overruns:0 frame:0
          TX packets:26 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:63687 (62.1 KiB)  TX bytes:6179 (6.0 KiB)
          Interrupt:16 Memory:dfaf0000-dfb00000

eth0:0    Link encap:Ethernet  HWaddr 00:1A:A0:53:4A:34
          inet addr:192.168.10.223  Bcast:192.168.10.223  Mask:255.255.255.255
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Interrupt:16 Memory:dfaf0000-dfb00000

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:1585 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1585 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:2596740 (2.4 MiB)  TX bytes:2596740 (2.4 MiB)

次に、リアルサーバーのWebサーバーの設定になります。
サーバーB、サーバーCで同じ設定を行ってください。
■手順9)ネットワークインターフェイスの設定(リアルサーバーのサーバーBの設定)
サーバーBのIPアドレス「192.168.10.221」をIPADDRに設定する
#
vi /etc/sysconfig/network-scripts/ifcfg-eth0
# Intel Corporation 82566DM-2 Gigabit Network Connection
DEVICE=eth0
BOOTPROTO=static
BROADCAST=192.168.10.255
HWADDR=00:1E:4F:55:6C:6A
IPADDR=192.168.10.221
NETMASK=255.255.255.0
NETWORK=192.168.10.0
ONBOOT=yes
ローカルループバックアドレスにVIPアドレスを設定する。

今回クライアントから見た場合、仮想IPである「192.168.10.223」にアクセスするとリアルサーバーへ転送されます。
その際、リアルサーバーは「192.168.10.223」へのアクセスが自分自身へのアクセスであると認識させる必要があるため、
ループバックインターフェースに仮想IPに設定した「192.168.10.223」を設定する必要がある。
vi /etc/sysconfig/network-scripts/ifcfg-lo:0
DEVICE=lo:0
IPADDR=192.168.10.223
NETMASK=255.255.255.255
ONBOOT=yes
■手順10)/etc/sysctl.confの編集(リアルサーバーのサーバーBの設定)
arp_ignoreとarp_announceを追加する。
ロードバランサーを用いてサーバの不可分散をする際にから一部抜粋
arpに応答させないのは、MACアドレスを覚えさせないようにする為です。
arpに返答してしまうと、MACアドレスを覚えてしまい、ipとmacアドレスが関係付けられてしまい
負荷分散させたときに、送りたい機器へ通信がいかなくなります。
(ipがなんであれ、通信は下位層のmacアドレスで行われていますので)
vi /etc/sysctl.conf
#
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.lo.arp_announce = 2
#
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
以下も同じ記事から抜粋mなどになります。

・arp_ignore = 1について
arpのリクエスト元IPアドレスが、それを受診したインターフェースのipアドレスだった場合のみ
arpリプライを返すように設定する。
だから負荷分散のvip宛てarpリクエストに対してarpリプライを返さない。

・arp_announce = 2について
arpリクエストを送信場合、送信元のipアドレスとして、
送信先に一番近いインターフェースに設定されているipアドレスを使うように設定する。
だから負荷分散のvip側を基点として(送信元アドレス)通信する場合、通信開始時に送信される
arpリクエストの送信元ipとして、vipではなく、インターフェースのipアドレスを使う

allすべてのinterfaceを指します。ですので、言われています
「eth0」や「lo0」も含まれます。
ただ、どういう用途に使うのか、物理interface数、仮想interface数等
を考えた上で、arpをどう動作させたいのか定義されたほうがよいと思います。
やみくもに、allで定義してしまうと、想定されていた動作と違う動きになるかもしれませんよ。

編集した内容を反映させる
sysctl -p
■手順11)ネットワークインターフェイスの確認(リアルサーバーのサーバーB)
ネットワークの再起動
/etc/init.d/network restart
ネットワークインターフェイスの確認
# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:1E:4F:55:6C:6A
          inet addr:192.168.10.221  Bcast:192.168.10.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:12332 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2261 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:2394484 (2.2 MiB)  TX bytes:221748 (216.5 KiB)
          Interrupt:21 Memory:fedc0000-fede0000

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:1327 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1327 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:2354919 (2.2 MiB)  TX bytes:2354919 (2.2 MiB)

lo:0      Link encap:Local Loopback
          inet addr:192.168.10.223  Mask:255.255.255.255
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
■手順12)apacheの設定
サーバーBにブラウザなどからIPアドレスでアクセスした場合に、
表示させるページを作成します。

httpdがインストールされていない場合にはインストールを行ってください。
yum install httpd
デフォルトではサーバーのドキュメントルートは「/var/www/html」に設定されているので、
/var/www/htmlにindex.htmlを作成します。

LVSでラウンドロビンされて表示する時にどっちのサーバーにアクセスされたかわかりやすいように。
サーバーのIPアドレスである「192.168.10.221」のみを記述します。
vi /var/www/html/index.html
192.168.10.221
ブラウザまたはcurlで「http://192.168.10.221」にアクセスして「192.168.10.221」が表示されるか確認する。
# curl 'http://192.168.10.221'
192.168.10.221
アクセスしても表示されない場合には、
apacheが起動しているか、iptablesで80ポートが開放されているかなど確認してください。
#apacheが起動しているかプロセスの確認
ps axu|grep httpd

#apacheの起動
apachectl start

#iptablesの確認
iptables -L

#iptablesの停止
/etc/init.d/iptables stop

次に、サーバーCの設定になります。
基本的には、IPアドレスの部分以外はサーバーBと同じ事を行います。(手順9~12)

■手順13)ネットワークインターフェイスの設定(リアルサーバーのサーバーCの設定)
サーバーBのIPアドレス「192.168.10.132」をIPADDRに設定する
#
vi /etc/sysconfig/network-scripts/ifcfg-eth0
# Intel Corporation 82566DM-2 Gigabit Network Connection
DEVICE=eth0
BOOTPROTO=static
BROADCAST=192.168.10.255
HWADDR=00:1E:4F:55:6C:6A
IPADDR=192.168.10.132
NETMASK=255.255.255.0
NETWORK=192.168.10.0
ONBOOT=yes
ローカルループバックアドレスにVIPアドレスを設定(サーバーBと同じ設定)
vi /etc/sysconfig/network-scripts/ifcfg-lo:0
DEVICE=lo:0
IPADDR=192.168.10.223
NETMASK=255.255.255.255
ONBOOT=yes
■手順14)/etc/sysctl.confの編集(リアルサーバーのサーバーCの設定) サーバーBで設定したものと同じものを設定する。
vi /etc/sysctl.conf
#
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.lo.arp_announce = 2
#
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
編集した内容を反映させる
sysctl -p
■手順15)ネットワークインターフェイスの確認(リアルサーバーのサーバーC)
ネットワークの再起動
/etc/init.d/network restart
ネットワークインターフェイスの確認
# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:1E:4F:54:59:F3
          inet addr:192.168.10.132  Bcast:192.168.10.255  Mask:255.255.255.0
          inet6 addr: fe80::21e:4fff:fe54:59f3/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:393310 errors:0 dropped:0 overruns:0 frame:0
          TX packets:25206 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:78959700 (75.3 MiB)  TX bytes:3035562 (2.8 MiB)
          Interrupt:50 Memory:fe9e0000-fea00000

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:1294 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1294 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:2310164 (2.2 MiB)  TX bytes:2310164 (2.2 MiB)

lo:0      Link encap:Local Loopback
          inet addr:192.168.10.223  Mask:255.255.255.255
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
■手順16)apacheの設定(リアルサーバーのサーバーCの設定)
サーバーCにブラウザなどからIPアドレスでアクセスした場合に、
表示させるページを作成します。

httpdがインストールされていない場合にはインストールを行ってください。
yum install httpd
デフォルトではサーバーのドキュメントルートは「/var/www/html」に設定されているので、
/var/www/htmlにindex.htmlを作成します。

LVSでラウンドロビンされて表示する時にどっちのサーバーにアクセスされたかわかりやすいように。
サーバーのIPアドレスである「192.168.10.132」のみを記述します。
vi /var/www/html/index.html
192.168.10.132
ブラウザまたはcurlで「http://192.168.10.132」にアクセスして「192.168.10.132」が表示されるか確認する。
# curl 'http://192.168.10.132'
192.168.10.132
アクセスしても表示されない場合には、
apacheが起動しているか、iptablesで80ポートが開放されているかなど確認してください。


以上で、各サーバーの設定は完了になります。
次に動作確認を行います。


■手順17)LVSのラウンドロビンの確認
サーバーAに設定したVIPの「192.168.10.223」のアドレスにブラウザでアクセスしてみると、
先程リアルサーバーのサーバーB、サーバーCで設定したページのどちらかの画面が評されると思います+(0゚・∀・) + ワクテカ +

何度かリロードして、サーバーB、サーバーCのページが交互に表示されたら、
正常にラウンドロビンが行われていることになりますキタ――(゚∀゚)――!!
# curl 'http://192.168.10.223'
192.168.10.221
# curl 'http://192.168.10.223'
192.168.10.132
# curl 'http://192.168.10.223'
192.168.10.221
# curl 'http://192.168.10.223'
192.168.10.132

また、次のコマンドで各サーバーの統計情報を見ることができます。 ※LVSを通過したデータ量の情報が表示される。
# ipvsadm -Ln --stats
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port               Conns   InPkts  OutPkts  InBytes OutBytes
  -> RemoteAddress:Port
TCP  192.168.10.223:80                 797     3985        0   491906        0
  -> 192.168.10.132:80                 399     1995        0   223381        0
  -> 192.168.10.221:80                 398     1990        0   268525        0
・Conns
 接続数
・InPkts
 受信パケット
・OutPkts
 送信パケット(DSRのダイレクト・ルーティングでは0になる)
・InBytes
 受信パケットバイト数
・OutBytes
 送信パケットバイト数(DSRのダイレクト・ルーティングでは0になる)

他には、1秒あたりの接続数、送受信パケット数、および送受信バイト数が実サーバなどが見れる、ipvsadm -Ln --rate
# ipvsadm -Ln --rate
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port                 CPS    InPPS   OutPPS    InBPS   OutBPS
  -> RemoteAddress:Port
TCP  192.168.10.223:80                   0        0        0        0        0
  -> 192.168.10.132:80                   0        0        0        0        0
  -> 192.168.10.221:80                   0        0        0        0        0
現在のそれぞれの接続の概要を表示など、色々コマンドはあるみたいです。
# ipvsadm -Lnc
IPVS connection entries
pro expire state       source             virtual            destination
TCP 01:43  FIN_WAIT    192.168.10.30:4987 192.168.10.223:80  192.168.10.221:80
TCP 01:43  FIN_WAIT    192.168.10.30:4983 192.168.10.223:80  192.168.10.132:80
コマンドなどのオプションなどはこちらの記事などを見てみると良いかもです(・∀・)イイネ!!
ipvsadm(8) 日本語訳
[LPIC 304 勉強メモ] LVS(Linux Virtual Server)

■手順18)LVSのラウンドロビンからの対象から外す
バランスアルゴリズムによってはリアルサーバごとに重みづけすることができるので、
その値を0にすることで、IPVSの設定上はリアルサーバーが登録されていても対象外にする事が可能です。

LVSサーバー「192.168.10.223」に設定されているサーバーCの「192.168.10.132」の重みを0にすることで対象外にするコマンド
※リアルサーバーなどを追加すると、統計情報がリセットされてしまうので、weightを0にする方が良いかも?
#
ipvsadm -e -t 192.168.10.223:80 -r 192.168.10.132 -g -w 0
weightが0になったことを確認する。
# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.10.223:80 rr
  -> 192.168.10.132:80            Route   0      0          0
  -> 192.168.10.221:80            Route   1      0          0
weightを0にするのではなくて、一覧からそもそも消してしまう場合には、次のコマンドを利用します。
※リアルサーバーなどを追加すると、統計情報がリセットされてしまう?
LVSサーバー「192.168.10.223」に設定されているサーバーCの「192.168.10.132」を対象外にするコマンド
# ipvsadm -d -t 192.168.10.223:80 -r 192.168.10.132
一覧から消えているか確認
# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.10.223:80 rr
  -> 192.168.10.221:80            Route   1      0          2
■手順19)正しく除外されてた状態でラウンドロビンが行われているか確認する
# curl 'http://192.168.10.223'
192.168.10.221
# curl 'http://192.168.10.223'
192.168.10.221
# curl 'http://192.168.10.223'
192.168.10.221
# curl 'http://192.168.10.223'
192.168.10.221
# curl 'http://192.168.10.223'
192.168.10.221
# curl 'http://192.168.10.223'
192.168.10.221

(゚∀゚)キタコレ!!

これで、ロードバランサーが行えるようになりましたが、
ラウンドロビン先のサーバーの死活監視などを行って自動で切り替えるという部分までは行えないため、
別途ソフトウェアを利用する必要があります

以上です(`・ω・´)ゞビシッ!!

社内のPCでXenの仮想環境を作ってこれをまずやろうとしたら、
Xenが絡むと複雑になる?せいなのかネットワーク周りがチンプンカンプンで悩みました・・・orz

なので、一旦Xenをやめて、シンプルな構成でやったらうまく行ったので、 その後に、Xenを利用してやってみたらうまくいったという流れでした(゚д゚)(。_。)(゚д゚)(。_。) ウンウン
いまだにあまり分かっていませんが、とりあえず設定が出来たのでラッキーって感じです(ΦωΦ)フフフ…

ネットワーク周りの勉強をしないとダメだな~って思いました:(;゙゚'ω゚'):

参考URL

0 件のコメント:

コメントを投稿