LVS ( Linux Virtual Server ) で DSR ( Direct Server Return ) のロードバランサ環境を、オーケストレーションツールの Serf を使って自動管理しよう、という内容です。当記事の検証は、CentOS 6.5 (x86_64)上で、Serf v0.4.5 を使用しています。
設定の方向性は、バランシング先のノードの増減を Serf を使って管理します。Serf のメンバに join する事で、自動的にバランシング先に組み込みます。また、障害発生時 ( fail ) やノードから離れる ( leave ) 時にも、自動的にバランシング情報の削除を試みました。
◆ OSC 1014 Tokyo/Fall で登壇させて頂きました
オープンソースカンファレンス 3月1日(土) 、オープンクラウドキャンパス枠で登壇させていただきました。内容は、オーケストレーションツールの Serf について。当日発表のスライドはこちら。冒頭、LVS について、その場でデモが出来れば・・・と思っていたのですが、準備が間に合わなかったので、こちらのブログ上で展開させていただきます。
LVS の良いところは、シンプルで分かりやすいところだと思います。ipvsadm コマンドを使うことで、ラウンドロビンや重み付けの設定や、バランシング先の制御が可能です。LVS は、同じネットワークセグメント上でのバランシングが、DSR 方式で比較的簡単に設定できます。NAT できない環境でバランシングの設定を行いたい時に重宝。
その一方で、ノードのヘルスチェックを監視したり、ノードの死活に関する制御は行わないません。そのため、keepalived と組みあわせたり、何か仕組みを導入したり一手間かかります。
この「何か」に Serf を使う事によって、LVS の管理を自動的に行う事が目的です。
※そもそも、HAProxy を使って同様の処理をさせるという方法もあります。が、HAProxy+Serf の組み合わせは、既に @glidenote さんが試みられていますので、ここは LVS でチャレンジします。
◆ 構成
192.168.39.0/24 のネットワークを用意します。
・LVS サーバ 192.168.39.3 ( LVS の VIP 用に 192.168.39.1 の IP エイリアス)
・バランシング先 192.168.39.11 ~ 192.168.39.12 の HTTP ポート(tcp 80)
・全台に Serf エージェントをセットアップします。
もし、ノードがダウンしたり撤去した時は、自動的に LVS から切り離します。また、新たにノードを追加したときは、Serf agent を起動して既存の Serf ネットワークに join することで、バランシングを開始します。
ただし、バランシングに対象に含まれるのは、serf のタグ名「role」が「webapp」の場合に限ります。この指定が無いと、Serf のメンバになった対象が、全てバランシング対象に含まれてしまうため、制御できる余地を残します。
では、以下は具体的な作業です。
◆ Serf のセットアップ手順 (全台)
バイナリが含まれているアーカイブをダウンロード、展開し、ファイルをコピーするだけで作業が完了します。
$ wget -O 0.4.5_linux_amd64.zip https://dl.bintray.com/mitchellh/serf/0.4.5_linux_amd64.zip $ unzip ./0.4.5_linux_amd64.zip # cp ./serf /usr/bin/serf
動作確認のために、バージョン情報の表示を確認します。
$ serf -v Serf v0.4.5 Agent Protocol: 3 (Understands back to: 1)
◆ LVS サーバ側のセットアップ (192.168.39.3 のみ)
LVS 管理用のツール ipvsadm をセットアップします。
# yum -y install ipvsadm
サーバ起動時に、自動で設定が有効になるようにします。
# /sbin/chkconfig ipvsadm on $ /sbin/chkconfig --list ipvsadm ipvsadm 0:off 1:off 2:on 3:on 4:on 5:on 6:off
このように、ランレベル 3 で自動起動する事を確認します。
ポートフォワーディングを有効化し、rp_filter (逆方向パスのフィルタリング=IPアドレス詐称対策) を無効化します。
# sysctl -w net.ipv4.ip_forward=1 # sysctl -w net.ipv4.conf.default.rp_filter=0
コマンドを実行するだけでなく、/etc/sysctl.conf にも同様の記述がありますので、パラメータを書き換えておきます。(※ここでは動作検証のため全無効化しましたが、本来はインターフェースを net.ipv4.conf.eth1.rp_filet=0 などのように、環境にあわせて設定した方が望ましいと思います)
次に、IP エイリアスの設定を追加します。
既存の環境は、eth1 に 192.168.39.3 が割り当てられています。
$ cat /etc/sysconfig/network-scripts/ifcfg-eth1 DEVICE=eth1 TYPE=Ethernet ONBOOT=yes IPADDR=192.168.39.3 NETMASK=255.255.255.0
この設定をコピーして、ifcfg-eth1:0 のファイルを作成します。これは VLS 用に使うためのアドレスです。IP エイリアスにしたのは、後々の冗長化なり拡張を考えてです。
# cat /etc/sysconfig/network-scripts/ifcfg-eth1:0 DEVICE=eth1:0 TYPE=Ethernet ONBOOT=yes IPADDR=192.168.39.1 NETMASK=255.255.255.0
設定を有効にするために、次のコマンドを実行します。
# service network restart
再起動後、ifconfig コマンドを実行すると、以下のようにインターフェース情報が確認出来ます。
$ ifconfig eth1 Link encap:Ethernet HWaddr 08:00:27:0B:A6:80 inet addr:192.168.39.3 Bcast:192.168.39.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:fe0b:a680/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:24951 errors:0 dropped:0 overruns:0 frame:0 TX packets:26693 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:2374222 (2.2 MiB) TX bytes:2275008 (2.1 MiB) eth1:0 Link encap:Ethernet HWaddr 08:00:27:0B:A6:80 inet addr:192.168.39.1 Bcast:192.168.39.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
これで、このサーバには 192.168.39.1 と 192.168.39.3 の2つのアドレスを持ちます。
◆ LVS ノード側の事前準備
LVS ノード側では、バランシングするための準備を行います。それぞれのノードは、DSR のバランシングに応答するため、192.168.39.1 ( LVS 用の IP アドレス ) へのパケットを処理させる必要があります。
# iptables -t nat -A PREROUTING -d 192.168.39.1 -j REDIRECT # service iptables save
動作確認は、iptables の nat テーブルの情報を確認します。
# iptables -t nat -nL Chain PREROUTING (policy ACCEPT) target prot opt source destination REDIRECT all -- 0.0.0.0/0 192.168.39.1
それから、ブラウザからアクセスした時に、どのホストが表示されるか分かるように、ファイルを設置します。以下は、何も無いドキュメントルートにファイルを置く例です。
例: # echo 'IPアドレス' > /var/www/html/index.html
◆ ipvsadm で、手動によるバランシング設定テスト
まず、LVS サーバ、192.168.39.3 にログインします。ノード 192.168.39.11 と 12 に対して、HTTP のバランシング(ラウンドロビン)の設定を行います。
# ipvsadm -A -t 192.168.39.1:80 -s rr # ipvsadm -a -t 192.168.39.1:80 -r 192.168.39.11:80 -g # ipvsadm -a -t 192.168.39.1:80 -r 192.168.39.12:80 -g
設定情報を確認するコマンドで、設定が正しく入っている事を確認します。
# 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.39.1:80 rr -> 192.168.39.11:80 Route 1 0 0 -> 192.168.39.12:80 Route 1 0 0
次に、バランシング配下ではないクライアントからアクセスします。crul で確認すると、確実です。
$ curl http://192.168.39.1 HOST: 192.168.39.11 $ curl http://192.168.39.1 HOST: 192.168.39.12
このように、curl を実行する度に、IP アドレスが切り替わる(ラウンドロビン)ことが分かります。
LVS が正常稼働することが分かれば、一旦設定をクリアします。
# ipvsadm -C
◆ Serf でノードを自動認識する設定
さて、ここからが Serf の出番です。Serf のノードが増えた段階で、ipvsadm コマンドを発効し、自動的にバランシングを設定します。作業は 192.168.39.3 で行います。
Serf のイベントハンドラ member-join と、member-leave または left 時にコマンドを実行します。ただし、Serf の role が「webapp」のみコマンドを実行します。
# vi /opt/serf-lvs/lvs.sh
ファイルの内容は、以下のようにします。
#!/bin/sh while read line do echo ${line} HOSTNAME=`echo ${line} | cut -d ' ' -f 1` ADDRESS=`echo ${line} | cut -d ' ' -f 2` ROLE=`echo ${line} | cut -d ' ' -f 3` case ${SERF_EVENT} in "member-join") if [ "${ROLE}" = "webapp" ] ; then ipvsadm -a -t 192.168.39.1:80 -r ${ADDRESS}:80 -g fi;; "member-leave" | "member-failed") if [ "${ROLE}" = "webapp" ] ; then ipvsadm -d -t 192.168.39.1:80 -r ${ADDRESS}:80 fi;; \?) echo "other";; esac break done exit 0
ファイルには、実行属性をつけておきます。
# chmod 700 /opt/serf-lvs/lvs.sh
それでは、Serf agent を起動してみます。
# serf agent -iface=eth1 -event-handler=/opt/serf-lvs/lvs.sh -log-level=debug
「-iface=eth1 」は、複数 NIC のある環境で eth1 を明示しているだけですが、環境によっては eth0 等に置き換えてください。
次に、LVS の設定を手動でリセットします(ここはイマイチな手順ですね。もっとスマートになりそうです)。
# ipvsadm -C # ipvsadm -A -t 192.168.39.1:80 -s rr
設定が正しいかどうかは -Ln オプションで確認します。
# 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.39.1:80 rr
次は、ようやくバランシング先であるノード側の作業。192.168.39.11 に入り、serf を起動します。
$ serf agent -join 192.168.39.3 -tag role=webapp -iface=eth1
‘-iface’オプションは、先ほど同様に必要に応じて指定します。ここで重要なのは「-tag role=webapp」というタグの指定です。この role という名称のタグが「webapp」の場合に、バランシング設定が入ります。
コマンドを実行したあと、192.168.39.3 で確認すると、バランシング設定の追加が確認出来ます。
# 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.39.1:80 rr -> 192.168.39.11:80 Route 1 0 0
次に、192.168.39.12 でも、serf agent を起動します。このとき、試しに -tag の指定を外してみると、Serf のメンバに加わったとしてもバランシング対象には含まれない事が分かります。
正しく実行した後、再び 192.168.39.3 で確認すると、バランシング対象に含まれています。
# 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.39.1:80 rr -> 192.168.39.11:80 Route 1 0 0 -> 192.168.39.12:80 Route 1 0 0
では、ノード1(192.168.39.11)の serf agent を停止させましょう。コンソール上で Ctrl + C を押します。これで対象のメンバから外れました。「serf members」コマンドを実行すると、メンバから離れたことが分かりますね。
# serf members
manager.pocketstudio.net 192.168.39.3:7946 alive
node1.pocketstudio.net 192.168.39.11:7946 left role=webapp
node2.pocketstudio.net 192.168.39.12:7946 alive role=webapp
あわせて LVS の設定を確認してみましょう。
# 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.39.1:80 rr -> 192.168.39.12:80 Route 1 0 0
自動的に 192.168.39.11 のバランシング設定が削除されていることが確認できます。
あとは、同様の手順で、ノードの数が増えても減っても、自動的に LVS を運用することが出来ます。実運用にあたっては、もうすこし工夫のしがいがあると思います。色々お試しください。
◆ まとめと課題
LVS の自動管理も、Serf を使えば簡単にできることが分かりました。「オーケストレーションツール」というと大仰な感じがしますが、中身はこんな感じでシンプルなものです。大事なのは、 Serf が行っているのは、単純にノードの「追加」「削除」というイベントに対して、コマンドを実行しているだけ。たったこれだけですが、コマンドを変えれば他にも使い勝手が増えてくると思います。
ここ数年は「構成管理」というキーワードで、サーバの設定を自動化・省力化する様々なツールが登場し、普及しはじめています。僕としては、これは「開発者視点」での自動化だなと。
そして、「運用者視点」での自動化も求められている訳で、それが「オーケストレーション」なのかなと。日常の運用の中で、細かな手間がかかるところを、Serf を使って楽に出来るのではないかと、考えています。
この記事をお読みになって、Serf が気になったら、是非お試し下さい。