LVS(IPVS 1.2.1)とipvsadmでopenblocks600をロードバランサ化

投稿者: | 2011年5月22日
Pocket

対象サーバについて

製品名 OpenBlockS 600
OS(kernel ver) Debian lenny(2.6.29)
CPU 600MHz(AMCC PowerPC 405EX)
メモリ 1GB(DDR2 SDRAM)
ストレージ 8GB(Compact Flash)

はじめに

こんなに簡単! Linuxでロードバランサを参考にさせて頂きながら、IPVSを使用してOpenblocks600をロードバランサにするための設定を記載していきます。

まずはIPVSとipvsadmについて取り上げますが、上記サイトにも詳しく説明がある通り、IPVSだけでは特定のリアルサーバが落ちた際にロードバランスの割り当てからはずすしたり、ロードバランサ自体を冗長化させたりはできませんので、別途keepalivedを導入する必要があります。

一度に書いてしまうと長くなってしまいますので、上記サイトと同じく、1.IPVS(本記事)、2.keepalivedでリアルサーバの障害検出3.keepalivedでロードバランサの冗長化、の流れで試していきたいと思います。

IPVS及びipvsadmの導入

IPVSのロード

IPVSはカーネルモジュールとして提供されていますので、IPVSをサポートしたカーネルをビルドする必要がありますが、Openblocks600のカーネルはすでにIPVSをサポートしていました。早速カーネルモジュールをロードしてIPVSのバージョンを調べます。

obs-act:/usr/src# modprobe ip_vs_rr
obs-act:/usr/src#
obs-act:/usr/src# lsmod | grep ip_vs_rr
ip_vs_rr                1656  0
ip_vs                  98596  2 ip_vs_rr
obs-act:/lib/modules/2.6.29/kernel/net/ipv6# vi /etc/modules

# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
# Parameters can be specified after the module name.
bonding
8021q
ip_vs_rr
~
obs-act:/usr/src# cat /proc/net/ip_vs
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port Forward Weight ActiveConn InActConn

カーネルモジュールのIPVS versionは1.2.1でした。

ipvsadmのインストール

ipvsadmはIPVSを使用するためのコマンドラインツールで、仮想サーバやリアルサーバの設定を行ったり、設定・接続状況などが確認できます。

obs-act:/usr/src# aptitude install ipvsadm
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
拡張状態情報を読み込んでいます
パッケージの状態を初期化しています... 完了
タスクの記述を読み込んでいます... 完了
以下の新規パッケージがインストールされます:
  ipvsadm
更新: 0 個、新規インストール: 1 個、削除: 0 個、保留: 0 個。
39.8kB のアーカイブを取得する必要があります。展開後に 180kB のディスク領域が新たに消費されます。
拡張状態情報を書き込んでいます... 完了
取得:1 http://ftp.jp.debian.org lenny/main ipvsadm 1:1.24-2.1 [39.8kB]
39.8kB を 1s 秒でダウンロードしました (38.6kB/s)
パッケージを事前設定しています ...
未選択パッケージ ipvsadm を選択しています。
(データベースを読み込んでいます ... 現在 17029 個のファイルとディレクトリがインストールされています。)
(.../ipvsadm_1%3a1.24-2.1_powerpc.deb から) ipvsadm を展開しています...
man-db のトリガを処理しています ...
ipvsadm (1:1.24-2.1) を設定しています ...
ipvsadm is not configured to run. Please run dpkg-reconfigure ipvsadm.
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
拡張状態情報を読み込んでいます
パッケージの状態を初期化しています... 完了
拡張状態情報を書き込んでいます... 完了
タスクの記述を読み込んでいます... 完了

obs-act:/usr/src# ipvsadm --version
ipvsadm v1.24 2005/12/10 (compiled with popt and IPVS v1.2.0)

ipvsadmはカーネルモジュールのIPVSとバージョンをそろえる必要がありますが、Openblocks600のdebian付属のipvsadmは1.2.0でカーネルモジュールのバージョンと差分が生じてしまいました。

ipvsadmパッケージのアンインストール

obs-act:/lib/modules/2.6.29/kernel/net/ipv6# aptitude remove ipvsadm
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
拡張状態情報を読み込んでいます
パッケージの状態を初期化しています... 完了
タスクの記述を読み込んでいます... 完了
以下のパッケージが削除されます:
  ipvsadm
更新: 0 個、新規インストール: 0 個、削除: 1 個、保留: 0 個。
0B のアーカイブを取得する必要があります。展開後に 180kB のディスク領域が解放されます。
拡張状態情報を書き込んでいます... 完了
(データベースを読み込んでいます ... 現在 17044 個のファイルとディレクトリがインストールされています。)
ipvsadm を削除しています ...
ipvsadm is not configured to run. Please run dpkg-reconfigure ipvsadm.
man-db のトリガを処理しています ...
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
拡張状態情報を読み込んでいます
パッケージの状態を初期化しています... 完了
拡張状態情報を書き込んでいます... 完了
タスクの記述を読み込んでいます... 完了

ipvsadm 1.2.1をソースからビルド

ipvsadm1.2.1をダウンロードしてソースからビルドします。

obs-act:/usr/src# wget http://www.linux-vs.org/software/kernel-2.6/ipvsadm-1.26.tar.gz
--2011-05-10 23:04:35--  http://www.linux-vs.org/software/kernel-2.6/ipvsadm-1.26.tar.gz
www.linux-vs.org をDNSに問いあわせています... 69.56.251.119, 2001:470:1f0f:297::2
www.linux-vs.org|69.56.251.119|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 41700 (41K) [application/x-gzip]
`ipvsadm-1.26.tar.gz' に保存中

100%[===============================================================================>] 41,700      90.8K/s 時間 0.4s

2011-05-10 23:04:36 (90.8 KB/s) - `ipvsadm-1.26.tar.gz' へ保存完了 [41700/41700]

obs-act:/usr/src# tar zxf ipvsadm-1.26.tar.gz
obs-act:/usr/src# cd ipvsadm-1.26
obs-act:/usr/src/ipvsadm-1.26# ls
Makefile             SCHEDULERS       config_stream.h  dynamic_array.c  ipvsadm-restore.8  ipvsadm.8   ipvsadm.spec
PERSISTENCE_ENGINES  VERSION          contrib          dynamic_array.h  ipvsadm-save       ipvsadm.c   ipvsadm.spec.in
README               config_stream.c  debian           ipvsadm-restore  ipvsadm-save.8     ipvsadm.sh  libipvs
obs-act:/usr/src/ipvsadm-1.26# make
make -C libipvs
make[1]: ディレクトリ `/usr/src/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
In file included from libipvs.h:13,
                 from libipvs.c:23:
ip_vs.h:15:29: error: netlink/netlink.h: そのようなファイルやディレクトリはありません
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: array type has incomplete element type
ip_vs.h:521: error: array type has incomplete element type
ip_vs.h:522: error: array type has incomplete element type
ip_vs.h:523: error: array type has incomplete element type
ip_vs.h:524: error: array type has incomplete element type
ip_vs.h:525: error: array type has incomplete element type
libipvs.c: In function ‘ipvs_nl_message’:
libipvs.c:57: warning: implicit declaration of function ‘nlmsg_alloc’
libipvs.c:57: warning: assignment makes pointer from integer without a cast
libipvs.c:61: warning: 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: warning: control reaches end of non-void function
libipvs.c: At top level:
libipvs.c:72: error: expected declaration specifiers or ‘...’ before ‘nl_recvmsg_msg_cb_t’
libipvs.c: In function ‘ipvs_nl_send_message’:
libipvs.c:76: warning: implicit declaration of function ‘nl_handle_alloc’
libipvs.c:76: warning: assignment makes pointer from integer without a cast
libipvs.c:78: warning: implicit declaration of function ‘nlmsg_free’
libipvs.c:82: warning: implicit declaration of function ‘genl_connect’
libipvs.c:85: warning: implicit declaration of function ‘genl_ctrl_resolve’
libipvs.c:91: warning: implicit declaration of function ‘nl_handle_destroy’
libipvs.c:96: warning: implicit declaration of function ‘nl_socket_modify_cb’
libipvs.c:96: error: ‘NL_CB_VALID’ undeclared (first use in this function)
libipvs.c:96: error: ‘NL_CB_CUSTOM’ undeclared (first use in this function)
libipvs.c:96: error: ‘func’ undeclared (first use in this function)
libipvs.c:99: warning: implicit declaration of function ‘nl_send_auto_complete’
libipvs.c:102: warning: implicit declaration of function ‘nl_recvmsgs_default’
libipvs.c: In function ‘ipvs_init’:
libipvs.c:127: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_getinfo_parse_cb’:
libipvs.c:149: warning: implicit declaration of function ‘nlmsg_hdr’
libipvs.c:149: warning: initialization makes pointer from integer without a cast
libipvs.c:152: warning: implicit declaration of function ‘genlmsg_parse’
libipvs.c:159: warning: implicit declaration of function ‘nla_get_u32’
libipvs.c:162: error: ‘NL_OK’ undeclared (first use in this function)
libipvs.c:163: warning: control reaches end of non-void function
libipvs.c: In function ‘ipvs_getinfo’:
libipvs.c:176: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_flush’:
libipvs.c:199: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_nl_fill_service_attr’:
libipvs.c:215: warning: implicit declaration of function ‘nla_nest_start’
libipvs.c:215: warning: assignment makes pointer from integer without a cast
libipvs.c:219: warning: implicit declaration of function ‘NLA_PUT_U16’
libipvs.c:222: warning: implicit declaration of function ‘NLA_PUT_U32’
libipvs.c:225: warning: implicit declaration of function ‘NLA_PUT’
libipvs.c:229: warning: implicit declaration of function ‘NLA_PUT_STRING’
libipvs.c:236: warning: implicit declaration of function ‘nla_nest_end’
libipvs.c:239: warning: label ‘nla_put_failure’ defined but not used
libipvs.c: In function ‘ipvs_add_service’:
libipvs.c:255: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_update_service’:
libipvs.c:276: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_del_service’:
libipvs.c:296: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_zero_service’:
libipvs.c:321: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_nl_fill_dest_attr’:
libipvs.c:334: warning: assignment makes pointer from integer without a cast
libipvs.c:348: warning: label ‘nla_put_failure’ defined but not used
libipvs.c: In function ‘ipvs_add_dest’:
libipvs.c:366: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_update_dest’:
libipvs.c:396: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_del_dest’:
libipvs.c:425: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_set_timeout’:
libipvs.c:452: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c:454: warning: label ‘nla_put_failure’ defined but not used
libipvs.c: In function ‘ipvs_start_daemon’:
libipvs.c:473: warning: assignment makes pointer from integer without a cast
libipvs.c:483: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_stop_daemon’:
libipvs.c:504: warning: assignment makes pointer from integer without a cast
libipvs.c:514: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: At top level:
libipvs.c:526: warning: ‘struct nlattr’ declared inside parameter list
libipvs.c:526: warning: its scope is only this definition or declaration, which is probably not what you want
libipvs.c: In function ‘ipvs_parse_stats’:
libipvs.c:530: warning: implicit declaration of function ‘nla_parse_nested’
libipvs.c:548: warning: implicit declaration of function ‘nla_get_u64’
libipvs.c: In function ‘ipvs_services_parse_cb’:
libipvs.c:562: warning: initialization makes pointer from integer without a cast
libipvs.c:592: warning: implicit declaration of function ‘nla_get_u16’
libipvs.c:598: warning: implicit declaration of function ‘nla_data’
libipvs.c:599: warning: passing argument 2 of ‘memcpy’ makes pointer from integer without a cast
libipvs.c:604: warning: implicit declaration of function ‘nla_get_string’
libipvs.c:605: warning: passing argument 2 of ‘strncpy’ makes pointer from integer without a cast
libipvs.c:610: warning: passing argument 2 of ‘strncpy’ makes pointer from integer without a cast
libipvs.c:614: warning: implicit declaration of function ‘nla_memcpy’
libipvs.c:618: warning: passing argument 2 of ‘ipvs_parse_stats’ from incompatible pointer type
libipvs.c: In function ‘ipvs_get_services’:
libipvs.c:649: error: ‘NLM_F_DUMP’ undeclared (first use in this function)
libipvs.c:650: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_dests_parse_cb’:
libipvs.c:728: warning: initialization makes pointer from integer without a cast
libipvs.c:759: warning: passing argument 2 of ‘memcpy’ makes pointer from integer without a cast
libipvs.c:771: warning: passing argument 2 of ‘ipvs_parse_stats’ from incompatible pointer type
libipvs.c: In function ‘ipvs_get_dests’:
libipvs.c:809: error: ‘NLM_F_DUMP’ undeclared (first use in this function)
libipvs.c:813: warning: assignment makes pointer from integer without a cast
libipvs.c:829: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_get_service’:
libipvs.c:939: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_timeout_parse_cb’:
libipvs.c:972: warning: initialization makes pointer from integer without a cast
libipvs.c:986: error: ‘NL_OK’ undeclared (first use in this function)
libipvs.c:987: warning: control reaches end of non-void function
libipvs.c: In function ‘ipvs_get_timeout’:
libipvs.c:1005: error: too many arguments to function ‘ipvs_nl_send_message’
libipvs.c: In function ‘ipvs_daemon_parse_cb’:
libipvs.c:1023: warning: initialization makes pointer from integer without a cast
libipvs.c:1048: warning: 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: warning: control reaches end of non-void function
libipvs.c: In function ‘ipvs_get_daemon’:
libipvs.c:1071: error: ‘NLM_F_DUMP’ undeclared (first use in this function)
libipvs.c:1072: error: too many arguments to function ‘ipvs_nl_send_message’
make[1]: *** [libipvs.o] エラー 1
make[1]: ディレクトリ `/usr/src/ipvsadm-1.26/libipvs' から出ます
make: *** [libs] エラー 2

エラーが出て失敗しました。

類似事象を探してみたところ、海外のサイトに同じエラーが紹介されていました。

コンパイラがカーネルソースを見つけられないためにエラーが発生しているようです。

Openblocks600内にカーネルソースが見当たらなかったためkernelを落としてきてシンボリックリンクをはります。

再コンパイル

obs-act:/usr/src# aptitude install linux-source-2.6.26
obs-act:/usr/src# tar xvf linux-source-2.6.26.tar.bz2
obs-act:/usr/src# ln -s /usr/src/linux-source-2.6.26 /usr/src/linux
obs-act:/usr/src# ls -al
合計 48300
drwxrwsr-x  3 root src      4096 2011-05-11 01:09 .
drwxr-xr-x 13 root root     4096 2011-04-03 22:27 ..
lrwxrwxrwx  1 root src        28 2011-05-11 01:09 linux -> /usr/src/linux-source-2.6.26
drwxr-xr-x 21 root root     4096 2011-01-25 14:55 linux-source-2.6.26
-rw-r--r--  1 root root 49391360 2011-01-25 15:35 linux-source-2.6.26.tar.bz2
obs-act:/usr/src# wget http://www.linuxvirtualserver.org/software/kernel-2.6/ipvsadm-1.24.tar.gz
obs-act:/usr/src# tar xvf ipvsadm-1.24.tar.gz
obs-act:/usr/src# cd ipvsadm-1.24
obs-act:/usr/src/ipvsadm-1.24# make

今度はエラーが出なかったので、インストール作業を続けます。

obs-act:/usr/src/ipvsadm-1.24# make install
obs-act:/usr/src/ipvsadm-1.24# ipvsadm -v
ipvsadm v1.24 2005/12/10 (compiled with getopt_long and IPVS v1.2.1)

ipvsadmのバージョンがめでたく1.2.1になりました。

IPフォーワードの有効化

ロードバランサの前に、特定のポートから別のポートへの転送機能をONにする必要があるため、IPフォーワード機能の有効化を行います。

obs-act:~# cat /proc/sys/net/ipv4/ip_forward
0

デフォルトではIPフォーワード機能は無効になっています。

obs-act:~# vi /etc/sysctl.conf

...

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1

...

"/etc/sysctl.conf" 67 lines, 2274 characters written

net.ipv4.ip_forwardを1にします。

obs-act:~# sysctl -p
net.ipv4.ip_forward = 1

/etc/sysctl.confファイルを読み込みました。

obs-act:~# cat /proc/sys/net/ipv4/ip_forward
1

IPフォーワードが有効化されました。

IPVSの動作試験

ネットワーク構成について

下図のようなネットワークを構築して実験します。

左が物理的な接続を表し、右が論理的な接続を表しています。

openblocks600でロードバランサ

ラウンドロビン動作確認用のクライアントとしてはWindowsにインストールしたcURLを利用します。

Openblocks600はBondingでL2SWと接続されているため、VIPを作成するセグメント(192.168.0.0/24)とリアルサーバを収容するセグメント(172.16.0.0/24)をvlanで分離します。

VIP設定

ユーザがWebアクセスする際に利用するVIPを手動で振ります。

OBS-ACT:~# /etc/init.d/networking stop

OBS-ACT:~# vi /etc/network/interfaces

...

auto bond0.2:100
iface bond0.2:100 inet static
 address 192.168.0.151
 netmask 255.255.255.0
 network 192.168.0.0
 broadcast 192.168.0.255
 gateway 192.168.0.1
 vlan-raw-device bond0


OBS-ACT:~# /etc/init.d/networking start

OBS-ACT:~# ip addr show

...

5: bond0.2@bond0:  mtu 1500 qdisc noqueue state UP
    link/ether 00:0a:85:04:02:ff brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.150/24 brd 192.168.0.255 scope global bond0.2
    inet 192.168.0.151/24 brd 192.168.0.255 scope global secondary bond0.2:100
    inet6 fe80::20a:85ff:fe04:2ff/64 scope link
       valid_lft forever preferred_lft forever

...

IPアドレスが二つ振られていることを確認します。

ipvsadmによるIPVSの設定

設定をリセットします。

OBS-ACT:~# ipvsadm -C

状態確認します。

OBS-ACT:~# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn

何も登録されていないことを確認します。

OBS-ACT:~# ipvsadm -A -t 192.168.0.151:80 -s rr

VIPを追加します。ポート80、ラウンドロビン(rr)でリアルサーバにバランスさせます。

OBS-ACT:~# ipvsadm -a -t 192.168.0.151:80 -r 172.16.0.2 -m
OBS-ACT:~# ipvsadm -a -t 192.168.0.151:80 -r 172.16.0.3 -m

リアルサーバのIPアドレスを追加します。

パケット転送方式にはNAT(-m)を選択します。

OBS-ACT:~# 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.0.151:80 rr
  -> 172.16.0.3:80                Masq    1      0          0
  -> 172.16.0.2:80                Masq    1      0          0

登録されていることを確認します。

※keepalivedを起動させる際には一度設定をクリアする必要があります。

リアルサーバ側の設定

両リアルサーバのDocumentRootにホスト名だけ記載したhtmlファイルを用意して、リアルサーバ1にパス指定無しでhttpアクセスするとDellと返し、リアルサーバ2にアクセスするとHitachiと返却するように設定します。

クライアントからcurlにてアクセス

クライアントはサーバ台数もありWindowsにcURLを入れました。

YUU BLOGさんのサイトを参考にさせて頂きました。

C:\Users\abc>curl 192.168.0.151
Hitachi

C:\Users\abc>curl 192.168.0.151
Dell

C:\Users\abc>curl 192.168.0.151
Hitachi

C:\Users\abc>curl 192.168.0.151
Dell

ラウンドロビンしました。

NAT動作確認

Openblocksのbond0.2、DellとHitachiのeth0.10でtcpdumpをまわしながら、curlでVIPにアクセスします。

クライアント

C:\Users\abc>curl 192.168.0.151
Hitachi

C:\Users\abc>curl 192.168.0.151
Dell

Openblocks600

OBS-ACT:~# tcpdump -i bond0.2 port www
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on bond0.2, link-type EN10MB (Ethernet), capture size 96 bytes
18:59:11.878754 IP 192.168.0.7.53925 > 192.168.0.151.www: S 3573158802:3573158802(0) win 8192 
18:59:11.878957 IP 192.168.0.151.www > 192.168.0.7.53925: S 1108100247:1108100247(0) ack 3573158803 win 5840 
18:59:11.879229 IP 192.168.0.7.53925 > 192.168.0.151.www: . ack 1 win 64240
18:59:11.879369 IP 192.168.0.7.53925 > 192.168.0.151.www: P 1:137(136) ack 1 win 64240
18:59:11.879761 IP 192.168.0.151.www > 192.168.0.7.53925: . ack 137 win 6432
18:59:11.880843 IP 192.168.0.151.www > 192.168.0.7.53925: P 1:262(261) ack 137 win 6432
18:59:11.886597 IP 192.168.0.7.53925 > 192.168.0.151.www: F 137:137(0) ack 262 win 63979
18:59:11.887060 IP 192.168.0.151.www > 192.168.0.7.53925: F 262:262(0) ack 138 win 6432
18:59:11.887294 IP 192.168.0.7.53925 > 192.168.0.151.www: . ack 263 win 63979

18:59:17.488190 IP 192.168.0.7.53950 > 192.168.0.151.www: S 2921444013:2921444013(0) win 8192 
18:59:17.488521 IP 192.168.0.151.www > 192.168.0.7.53950: S 3572830156:3572830156(0) ack 2921444014 win 5840 
18:59:17.489837 IP 192.168.0.7.53950 > 192.168.0.151.www: . ack 1 win 64240
18:59:17.490022 IP 192.168.0.7.53950 > 192.168.0.151.www: P 1:137(136) ack 1 win 64240
18:59:17.490341 IP 192.168.0.151.www > 192.168.0.7.53950: . ack 137 win 6432
18:59:17.490651 IP 192.168.0.151.www > 192.168.0.7.53950: P 1:245(244) ack 137 win 6432
18:59:17.496756 IP 192.168.0.7.53950 > 192.168.0.151.www: F 137:137(0) ack 245 win 63996
18:59:17.497085 IP 192.168.0.151.www > 192.168.0.7.53950: F 245:245(0) ack 138 win 6432
18:59:17.498953 IP 192.168.0.7.53950 > 192.168.0.151.www: . ack 246 win 63996
^C
18 packets captured
18 packets received by filter
0 packets dropped by kernel

192.168.0.7(クライアント)と192.168.0.151(VIP)の間で通信が行われています。

リアルサーバ1(Dell)

dell:~# tcpdump -i eth0.10 port www
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0.10, link-type EN10MB (Ethernet), capture size 96 bytes
18:59:17.993340 IP 192.168.0.7.53950 > 172.16.0.2.www: S 2921444013:2921444013(0) win 8192 
18:59:18.006360 IP 172.16.0.2.www > 192.168.0.7.53950: S 3572830156:3572830156(0) ack 2921444014 win 5840 
18:59:17.994918 IP 192.168.0.7.53950 > 172.16.0.2.www: . ack 1 win 64240
18:59:17.995101 IP 192.168.0.7.53950 > 172.16.0.2.www: P 1:137(136) ack 1 win 64240
18:59:17.995121 IP 172.16.0.2.www > 192.168.0.7.53950: . ack 137 win 6432
18:59:17.995402 IP 172.16.0.2.www > 192.168.0.7.53950: P 1:245(244) ack 137 win 6432
18:59:18.001833 IP 192.168.0.7.53950 > 172.16.0.2.www: F 137:137(0) ack 245 win 63996
18:59:18.001866 IP 172.16.0.2.www > 192.168.0.7.53950: F 245:245(0) ack 138 win 6432
18:59:18.004070 IP 192.168.0.7.53950 > 172.16.0.2.www: . ack 246 win 63996
^C
9 packets captured
9 packets received by filter
0 packets dropped by kernel

192.168.0.7(クライアント)と172.16.0.2(リアルサーバ1)の間で通信が行われています。

リアルサーバ2(Hitachi)

hitachi:~# tcpdump -i eth0.10 port www
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0.10, link-type EN10MB (Ethernet), capture size 96 bytes
18:52:07.575499 IP 192.168.0.7.53925 > 172.16.0.3.www: S 3573158802:3573158802(0) win 8192 
18:52:07.598416 IP 172.16.0.3.www > 192.168.0.7.53925: S 1108100247:1108100247(0) ack 3573158803 win 5840 
18:52:07.576003 IP 192.168.0.7.53925 > 172.16.0.3.www: . ack 1 win 64240
18:52:07.576141 IP 192.168.0.7.53925 > 172.16.0.3.www: P 1:137(136) ack 1 win 64240
18:52:07.576244 IP 172.16.0.3.www > 192.168.0.7.53925: . ack 137 win 6432
18:52:07.577248 IP 172.16.0.3.www > 192.168.0.7.53925: P 1:262(261) ack 137 win 6432
18:52:07.583370 IP 192.168.0.7.53925 > 172.16.0.3.www: F 137:137(0) ack 262 win 63979
18:52:07.583518 IP 172.16.0.3.www > 192.168.0.7.53925: F 262:262(0) ack 138 win 6432
18:52:07.584017 IP 192.168.0.7.53925 > 172.16.0.3.www: . ack 263 win 63979
^C
9 packets captured
9 packets received by filter
0 packets dropped by kernel

192.168.0.7(クライアント)と172.16.0.3(リアルサーバ2)の間で通信が行われています。

上記の結果から、クライアントからVIP宛の通信は、宛先をリアルサーバのIPに書き換えてIPフォーワードしていることが分かります。

また、リアルサーバからクライアントへの戻り通信は、送信元をリアルサーバからVIPに書き換えていることが分かります。

リアルサーバ障害時の動作

リアルサーバ2(Hitachi)側のApacheを落として動作を確認します。

Hitachi

hitachi:~# /etc/init.d/apache2 stop

リアルサーバ2を落とした後にクライアントからアクセスします。

クライアント

C:\Users\abc>curl 192.168.0.151
curl: (7) couldn't connect to host

C:\Users\abc>curl 192.168.0.151
Dell

C:\Users\abc>curl 192.168.0.151
curl: (7) couldn't connect to host

C:\Users\abc>curl 192.168.0.151
Dell

2回に1回、接続NGになってしまいます。

リアルサーバの障害時は自動的に迂回するようにするためにはkeepalivedが必要ですが、weightをいじることで、手動で計画的にリアルサーバをラウンドロビンの対象からはずすことは可能です。

一度リアルサーバ2のApacheプロセスを上げてから動作を確認してみます。

リアルサーバ2

hitachi:~# /etc/init.d/apache2 start

Openblocks600

OBS-ACT:~# ipvsadm -e -t 192.168.0.151:80 -r 172.16.0.3 -m -w 0
OBS-ACT:~# 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.0.151:80 rr
  -> 172.16.0.3:80                Masq    0      0          4
  -> 172.16.0.2:80                Masq    1      0          8

172.16.0.3のWeightが0になっていることを確認して、クライアントからcurlを実行します。

クライアント

C:\Users\abc>curl 192.168.0.151
Dell

C:\Users\abc>curl 192.168.0.151
Dell

C:\Users\abc>curl 192.168.0.151
Dell

C:\Users\abc>curl 192.168.0.151
Dell

アクセスが無事、リアルサーバ1に偏りました。

元に戻して再度確認します。

Openblocks600

OBS-ACT:~# ipvsadm -e -t 192.168.0.151:80 -r 172.16.0.3 -m -w 1
OBS-ACT:~# 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.0.151:80 rr
  -> 172.16.0.3:80                Masq    1      0          0
  -> 172.16.0.2:80                Masq    1      0          0

クライアント

C:\Users\abc>curl 192.168.0.151
Hitachi

C:\Users\abc>curl 192.168.0.151
Dell

C:\Users\abc>curl 192.168.0.151
Hitachi

C:\Users\abc>curl 192.168.0.151
Dell

ラウンドロビン動作に戻りました。

IPVSを使用してOpenblocks600をロードバランサ化しましたが、このままでは障害中のリアルサーバにもアクセスを割り当ててしまいます。そこで次回の記事(keepalived(1.2.1)の導入)では、keepalivedを導入することでリアルサーバのサービス監視を行い、リアルサーバに障害が起きた際には自動的にロードバランス先からはずせるようにします。

参考文献

[24時間365日] サーバ/インフラを支える技術 ‾スケーラビリティ、ハイパフォーマンス、省力運用 (WEB+DB PRESS plusシリーズ)

海外のサイト

YUU BLOGさんのサイト

DSAS開発者の部屋

フルオープンソースで実現するロードバランサ

Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です