更新日: 2023-11-07 19:20:41 +0900
公開日: 2011/05/25
発売日: 2007/4/24
この文書は2007/4/24に書かれたもので、ソフトウエアの名称、バージョン、設定項目、社名などの固有名詞などなどは当時のまま掲載しています。
ですので、インストール手順や設定内容は最新版のドキュメントを参照していただき、この文書からは理論や考え方、構成のヒントなどを読み取っていただければと思います。
まずはちょっとだけ自己紹介をさせてください。
わたしはKLab(株)(注1)という会社で、ネットワーク/サーバインフラの設計、構築、運用に携わっています。
KLabではこのインフラを『DSAS』と呼んでいるのですが、このDSASの特徴のうちで、読者の方に興味を持ってもらえそうな点をいくつかあげてみます。
サーバ台数は全部で300台ぐらいで、DSASチームのメンバーは日夜サーバたちとたわむれています。
さて、そんなインフラ大好きっ子のわたしが本誌で連載をお届けすることになりました。「WEB+DB PRESSなのになんでインフラの記事が?」と思うかもしれません。そこで、今回は第1回目ということもあり、まずはこの連載の目的やテーマについてちょっとお話ししたいと思います。そして後半で「あんなものもロードバランス」という本題のトピックをお届けしたいと思います。
この連載で取り上げるのは、アプリケーション層より下の、ネットワーク/サーバまわりのインフラのお話です。
ひとくちにインフラといっても、スケールが違うと勘所も違ってきます。数台〜十数台規模のノリで数百台規模のサーバファームを組むと、きっと運用管理で破綻してしまうでしょう。
実はDSASもスタートアップのときは箱モノのロードバランサ1台に、Webサーバが3台というとても小さいインフラでした。今でこそ、300台という規模のわりには運用管理がラクなインフラだと自負できるのですが、ここまでの道程は平坦なものではありませんでした。
そんなわけでこの連載では、わたしたちがここまでDSASを育て上げてきた過程で得たいろいろなノウハウを、次節であげるテーマに沿って、読者の方にお伝えできればと思っています。
連載を通したテーマはこの3つです。
拡張性とは、システムに対する様々な負荷の上昇に応じて、どれだけ簡単にシステムの能力を高めて対応できるか、という指標です。
例えば、現状より多いアクセスを処理できるようにすることを考えたときに、サーバをどんどん追加していけば対応できるデザインのシステムは「スケーラビリティがある」とか「スケーラブルである」といえます。一方、ネットワークやサーバの基本構成から考え直さないと能力増強ができないようなデザインのシステムは「スケーラビリティがある」とはいえません。
さて、拡張性を語るときに、「スケールアップ」や「スケールアウト」という言葉がよく出てくるので整理しておきます。
システムの構成要素に障害が発生した場合でも、サービスを継続できるかどうかという指標を可用性といいます。
例としてWebサービスを考えた場合、フロントのWebサーバが1台だけの場合は、そのサーバがダウンしてしまうとサービスは全停止してしまいます。しかし、ロードバランサの配下に複数のWebサーバがいる場合ならば、1台ぐらいダウンしてもサービスは継続できます。
もう1つ例をあげると、RAIDは「ディスク」というサービスの可用性を高める手法だということができます。
クリエイティブじゃないオペレーションはコンピュータにやってもらうべきだと思います。運用管理はクリエイティブな部分も多いですが、そうでない部分があるのも確かです。そんなツマラナイ作業はコンピュータにやらせて、もっとクリエイティブな作業に時間をあてたほうがハッピーになれるとわたしは考えます。
ちょっと想像してみてください。サーバが2、3台ならば、Apacheの設定を書き換えてまわったりソフトウエアのバージョンアップをしてまわったりするのはそれほど苦ではないかもしれません。しかし、規模が大きくなりサーバが数十台、数百台になってもこんな運用をしていると、ちょっとなにかを変更するだけでも日が暮れてしまいます。
また、「ラクな運用」は時間の短縮だけでなく、オペレーションミスの減少という効果も期待できます。同じことを何度も何度も繰り返すと、人間だもの間違うことがありますが、処理をスクリプト化しておいてコンピュータにやらせれば間違うことはありません。
というわけで、わたしは常に「ラクをするにはどうすればいいか?」を考えています。DSASでもいろいろと「ラク」をする工夫をしているのですが、その中からみなさんの環境でも活用できそうなものを紹介していきたいと思っています。
この連載では、オープンソースのプロダクトを活用して、いかに拡張性と可用性があり運用もラクなシステムを作るかという内容をお届けします。
その際、単体プロダクトの紹介だけに終始せず、ひとつの「システム」を作るまでを紹介したいと思っています。
これはどういうことかというと、なにかしらの要件を満たすシステムを作る時は、単体のプロダクトではカバーしきれず、複数のプロダクトを組み合わせたり、間を埋めるスクリプトを自作したりしながら、システムを作らなければならないことが経験的に多々あったからです。
このように「システム」を作るまでを紹介することにより、より実用的で、かつ、より動作イメージがしやすい記事になるのではないかと考えています。
インフラの話というとプログラマの人には関係ないように思えるかもしれませんが、スケーラビリティや可用性はインフラ系の仕組みだけで実現するのは困難であったり、アプリケーション側だけでどうにかできる問題でもなかったりします。
つまり、インフラ+アプリの協力が必要であり、また、協力することによって、より高い効果が得られるわけです。
そんなわけで、「インフラ系の話だから…」と敬遠せずに、プログラマの人にも是非読んでもらいたいと思っています。
さてさて、今回の本題に入ります。
前号の特集(注2)で、Linuxをロードバランサにする方法を紹介しました。特集ではWebサービス(HTTP)をロードバランスする例を紹介しましたが、ロードバランサはHTTPだけでなく他のサービスも負荷分散することができます。
というわけで、今回はHTTP以外のいくつかのサービスをロードバランスしてみたいと思います。
今回の全体的なネットワーク/サーバ構成を図1に示します。
前号の特集では、Linuxをロードバランサに仕立てあげて、Webサービスをロードバランスする方法を紹介しました。簡単にですが、その内容をおさらいしておきます。
まずはその登場人物(図1の四角い箱)から。
続いて全体の流れを見てみましょう。まず、クライアント(cl01)がHTTPアクセスをします。このリクエストパケットはまずはロードバランサ(lvx)が受け取ります。このときの宛先のIPアドレスのことをバーチャルIPアドレス(VIP)といいます。リクエストパケットを受け取ったロードバランサは、配下のWebサーバ(w10x)にパケットを転送します。このように、ロードバランサ配下にあり実際のリクエスト処理をするサーバのことをリアルサーバといいます。さて、リアルサーバはレスポンスをクライアントに返さなければならないわけですが、これにはリクエストと同じようにロードバランサを経由して返す方法(これをNAT方式といいます)と、ロードバランサを経由せずに直接クライアントに返す方法(DSR - Direct Server Return方式といいます)の2つがあります。
それでは次にLinuxロードバランサの中身をもうちょっと詳しく見てみましょう。
Linuxをロードバランサにするには、IPVSというkernelの機能を使います。IPVSにより配下のリアルサーバに負荷分散できるようになるのですが、IPVSにはリアルサーバの死活監視機能がありません。そこでkeepalived (注3) を使います。
keepalivedは、定期的にリアルサーバの状態を監視し、停止を検出したらIPVSを制御してそのリアルサーバを分散対象から外してくれます。
このkeepalivedにはもう一つの機能があります。それはVRRP(Virtual Router Redundancy Protocol)です。VRRPを活用すると、VRRPに対応した機器同士でアクティブ/バックアップ構成の冗長構成を組むことができます。図1を見ると、lv1とlv2という機器があるのですが、この2台でVRRPを使ってロードバランサの2重化をしています。
基本的なところは前号の特集と同じですが、いくつかサーバが増えている点が異なります。
それぞれのサーバがどういう役割をするかは、次節以降で説明していきます。
せっかく、金銭コストがかからないロードバランサがあるのですから、ロードバランサをフロント(外側)だけに置いておくのはもったいないです。内部にもロードバランサを配置してみましょう。
ここでは、MySQLでレプリケーション構成を組んでいて、マスタが1台にスレーブが数台あるとしましょう。マスタの負担軽減のために、参照系のクエリはスレーブに問い合わせるのはわりとよくあるパターンだと思うのですが、スレーブが複数台ある場合は、個々のスレーブへのコネクション数や負荷に応じて均等に振り分けたくなってきます。また、あるスレーブがダウンした場合は、そのスレーブには問い合わせにいかないようにしなければなりません。
そこで内部ロードバランサの出番です。
図2のように、ロードバランサをスレーブ群の前に配置した場合、アプリケーションからみると次のメリットがあります。
このようなロジックをアプリケーション側に実装することは可能ですが、その場合、スレーブの数が増減した場合はプロパティファイルを書き換える必要があるなど、運用上、アプリケーションもスレーブの構成や状態を意識する必要があります。
一方、ロードバランサを介すと、アプリケーションには1つの大きなスレーブとして見えるので、アプリケーションはスレーブの状態などを意識しないで済むわけです。
レプリケーション構成になっているMySQLのマスタ(db100)とスレーブが2台(db101、db102)あるとします。これらスレーブたちに内部ロードバランサを経由してアクセスできるようにしてみましょう。仮想スレーブの名前は「db100-s」とします。
内部ロードバランサ(ll1)の構成は、ほとんど外部ロードバランサ(lv1)と同じ(注4)です。異なるのは、ll1固有のIPアドレスとkeepalivedの設定です。
ll1固有のIPアドレスは192.168.31.231とします。Debianの場合は/etc/network/interfacesで設定します。(リスト1)
次にkeepalived.confを見てみましょう。(リスト2)
まず「basic」のセクションを見てみます。このセクションでは、ll1がロードバランサとして振る舞うための基本的な設定をしています。注意しなければならないのは、(1)のvirtual_router_idで指定するVRIDです。
VRRPでは、VRIDが同じノード(ルータ)のグループで仮想ルータを構成します。したがって、同一のネットワークセグメントでは、仮想ルータグループごとに異なるVRIDをつけなければなりません。今回の構成例だと、lv1とlv2とが既に仮想ルータグループを構成しているので、ll1のVRIDはこれとは違うものにしなければなりません。lv1からkeepalived.confをコピーしてvirtual_router_idを変えずに使ってしまうと、ll1はlv1と仮想ルータを構成しようとしてしまい、誤動作原因になりますので注意してください。参考までに、tcpdumpを使ってネットワークを流れているVRRPパケットを覗くには、図3のようにします。
basicセクションで見て欲しいところがもう1つあります。(2)のvirtual_ipaddressです。リスト2では、内部ロードバランサ自身の仮想ルータアドレス(192.168.31.230)に加えて、仮想スレーブ(db100-s)用のIPアドレス(192.168.31.119)も設定しています。ちなみに、この仮想スレーブ用のIPアドレスはロードバランスするためのものなので、MySQLの世界のレプリケーションには一切関係しません。
続いて「MySQL slave」のセクションを見てみますが、特段変わったことはしていません。virtual_server_groupで仮想スレーブ用のIPアドレスとポート番号を指定して、続くvirtual_serverでリアルサーバ(スレーブ)の指定をしています。この例では、リアルサーバの死活監視は、TCP_CHECKでTCPの3306番ポートが開いているかどうかで行っています。より正確に監視を行うならば、MISC_CHECKを使って実際にクエリを発行して意図した結果が得られたか確認するスクリプトを指定するのがいいでしょう。
auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 192.168.31.231 netmask 255.255.0.0 network 192.168.31.0 broadcast 192.168.31.255 gateway 192.168.31.10
### basic vrrp_instance VI { state BACKUP interface eth0 garp_master_delay 5 virtual_router_id 230 ──────── (1) priority 100 nopreempt advert_int 1 authentication { auth_type PASS auth_pass himitsu } virtual_ipaddress { ─────────┐ 192.168.31.230/24 dev eth0 │(2) 192.168.31.119/24 dev eth0 │ } ──────────────────┘ } ### MySQL slave virtual_server_group MYSQL100 { 192.168.31.119 3306 } virtual_server group MYSQL100 { delay_loop 3 lvs_sched rr lvs_method DR ─────────── (3) protocol TCP real_server 192.168.31.111 3306 { weight 1 inhibit_on_failure TCP_CHECK { connect_port 3306 connect_timeout 3 } } real_server 192.168.31.112 3306 { weight 1 inhibit_on_failure TCP_CHECK { connect_port 3306 connect_timeout 3 } } }
ll1# tcpdump -n proto \\vrrp 00:59:42.164341 IP 192.168.31.231 > 224.0.0.18: VRRPv2, Advertisement, vrid 230, prio 100, authtype simple, intvl 1s, length 24
リアルサーバであるMySQLのスレーブサーバでもちょっと設定が必要です。
MySQLのサービス的には特に設定することはないのですが、リスト2の(3)の通り、DSRで分散するように設定したので、仮想スレーブのIPアドレス宛てのパケットを受け入れるようにしなければなりません。具体的には、スレーブのそれぞれ(db101、db102)で、リスト3のコマンドを実行します。
iptables -t nat -A PREROUTING -d 192.168.31.119 -j REDIRECT
以上で設定は終りました。ではお楽しみのロードバランス体験の時間です!
ここでは確認用に、分散対象となるスレーブのdb101とdb102のserver_id(注5)は、ホスト名に合わせて101と102としているものとします。このserver_idを問い合わせるクエリを仮想スレーブ(db100-s)に対して発行して、ちゃんと分散されるか確認してみましょう。(図4)
同じサーバ(db100-s)にクエリを発行しているにも関わらず、結果のserver_idの値が異なることから、ちゃんとロードバランスされているのが確認できます。
w101$ check_lb_slave() { > echo 'SHOW VARIABLES LIKE "server_id"' | mysql -s -hdb100-s > } w101$ check_lb_slave server_id 101 w101$ check_lb_slave server_id 102 w101$ check_lb_slave server_id 101
MySQLのスレーブ参照に限らず、外部ロードバランスにはない内部ロードバランサ特有の注意点があります。
それは、「分散方法はNAT(lvs_method NAT)ではなくDSR(lvs_method DR)にする」です。
なぜならNATにした場合、クライアントから見ると、パケットを送ったのとは違う相手から応答パケットが返ってくるようにみえるので、戻りのパケットを受理できないからです。
もう少し詳しく説明すると、まず、クライアントがVIPを終点アドレスとするリクエストパケットを発信します。NATの場合、それを受け取ったロードバランサは、終点アドレスをリアルサーバのIPアドレスに書き換えてリアルサーバに転送します。リアルサーバはパケットを受け取り応答を返すのですが、戻りのパケットの始点アドレスは自分(リアルサーバ)のアドレスとなります。この戻りのパケットが、ロードバランサを経由すればそのときに始点アドレスがVIPに書き換えられるので問題は起こらないのですが、リアルサーバとクライアントが同じネットワークに存在するときは、ロードバランサを介す必要なく直接クライアントにパケットが届いてしまい、結果的に、VIP宛てに送ったパケットの応答が、VIPとは異なるところ(リアルサーバのIPアドレス)から返ってきているように見えてしまうわけです。
ピンと来たかもしれませんが、このパケットの流れはまさにDSRです。ですので、分散方法をDSRにすれば、なんの問題もなく応答パケットがちゃんと受理できます。NATでもひとひねりすればできないことはないのですが、DSRの方がロードバランサの負荷が軽減されることもあり、内部ロードバランサの場合は苦労してNAT構成にする必要はないでしょう。
外部からのメールを受け取るメールサーバをロードバランサの配下に置いてみましょう。
先のMySQLの例は複数のリアルサーバへのロードバランスだったので、今度はあえて2台でのアクティブ/バックアップ構成を作ってみます。
もし既に外部ロードバランサがあれば、新たにSMTP用のロードバランサを配置する必要はなく、既存のロードバランサに設定を追加するだけです。
ここではw101をアクティブ側のSMTPサーバ、w102をバックアップ側として構成してみます。
keepalived.confをリスト4に示します。
新たに外部ロードバランサを作る場合は、これがそのままkeepalived.confになります。既存のロードバランサに設定を追加する場合は、「basic」セクションの部分は既に似たような設定があるはずなので、「SMTP」セクションのvirtual_server_groupとvirtual_serverのブロックを追加してください。
さて、肝心のアクティブ/バックアップの設定ですが、今回は、(1)のようにアクティブ側をreal_serverで指定して、バックアップ側をsorry_serverで指定する方法を取りました。
リアルサーバの死活監視はSMTP_CHECKを使い、keepalivedがSMTPを喋ってリアルサーバが応答するかどうかを監視しています。そして、死活監視に失敗した場合、ほかのリアルサーバがいないので、sorry_serverへリクエストは転送されるようになります。このsorry_serverがバックアップ側の役目になるわけです。
### basic vrrp_sync_group VG { group { VE VI } } vrrp_instance VE { state BACKUP interface eth0 garp_master_delay 5 virtual_router_id 31 priority 100 nopreempt advert_int 1 authentication { auth_type PASS auth_pass himitsu } virtual_ipaddress { 10.10.31.10/16 dev eth0 } } vrrp_instance VI { state BACKUP interface eth1 garp_master_delay 5 virtual_router_id 32 priority 100 nopreempt advert_int 1 authentication { auth_type PASS auth_pass himitsu } virtual_ipaddress { 192.168.31.10/24 dev eth1 } } ### SMTP virtual_server_group SMTP { 10.0.0.100 25 } virtual_server group SMTP { delay_loop 3 lvs_sched rr lvs_method DR protocol TCP sorry_server 192.168.31.102 25 ───┐ real_server 192.168.31.101 25 { │ weight 1 │ inhibit_on_failure │ SMTP_CHECK { │ host { │ connect_ip 192.168.31.101 │(1) connect_port 25 │ } │ connect_timeout 3 │ helo_name lvs │ } │ } ─────────────────┘ }
では、うまくアクティブ/バックアップが動くか試してみましょう。
ここではどちらが応答しているか区別しやすいように、リアルサーバのw101とw102は、SMTPのグリーティング応答でホスト名を返すように設定してあるとします。
VIP(10.0.0.100)の25番ポートに繋いでみると、図5のように、通常時はw101が常に応答します。そしてw101のSMTPサービスを止めると、今度はw102が応答するようになります。さらにw102のSMTPサービスを止めると、応答は返らなくなります。
cl01$ echo QUIT | nc 10.0.0.100 25 220 w101.example.org ESMTP 221 w101.example.org cl01$ echo QUIT | nc 10.0.0.100 25 220 w101.example.org ESMTP 221 w101.example.org cl01$ echo QUIT | nc 10.0.0.100 25 220 w101.example.org ESMTP 221 w101.example.org <<ここでw101のSMTPサービスを止める>> cl01$ echo QUIT | nc 10.0.0.100 25 220 w102.example.org ESMTP 221 w102.example.org cl01$ echo QUIT | nc 10.0.0.100 25 220 w102.example.org ESMTP 221 w102.example.org <<ここでw102のSMTPサービスを止める>> cl01$ echo QUIT | nc 10.0.0.100 25 (UNKNOWN) [10.0.0.100] 25 (smtp) : Connection refused <<ここでw101のSMTPサービスを再開する>> cl01$ echo QUIT | nc 10.0.0.100 25 220 w101.example.org ESMTP 221 w101.example.org
SMTPサーバをロードバランスするときの注意点は、分散環境であるということを忘れない、という点です。具体的には、届いたメールをユーザのメールボックスに保存するときや、フィルタプログラムに流す際にトラブルが起こりやすいです。
分散環境の場合、メールを受け取るSMTPサーバは複数あるので、メールボックスはどのSMTPサーバもアクセスできるストレージに置く必要がありますし、フィルタプログラムもなにかしらのデータを読んだり書いたりする場合は、共有できるストレージを使う必要があります。
特に、アクティブ/バックアップ構成の場合は、通常はアクティブ側だけがメールを受け取るので一見、問題にはなりませんが、アクティブ側がダウンしてバックアップ側がメールを受け取るようになったとたんに問題が発現することがままあるので、気をつけましょう。
今回は、HTTP以外のものをロードバランスしてみようということで、
の2つを紹介しました。
DSASでは、内部ロードバランサでサーバファーム内のDNSサーバを冗長化したり、HTTPSの処理で、外部ロードバランサがSSLプロセッサにロードバランスし、SSLプロセッサは内部ロードバランサ経由でWebサーバにリクエストを転送するといった外部/内部ロードバランサの連携も行っていたりします。
せっかく、安価に導入できるLinuxロードバランサがあるのですから、外部だけに置いておくのはもったいないです。みなさんの環境でも、いろいろなところにロードバランサを配置して活用してみてはいかがでしょうか?