ELB(AmazonEC2のロードバランサー)について深く知りたくなってしまったので、改めて調べたり聞いたりした。
今回そもそも知りたかったポイントは下記の2点
これらを理解するためには、ELBの内部仕様まで知る必要があった。調べていくうちに、単純だと思ってたELBは、実はすごく複雑であることを思い知った。
内部仕様に踏み込む前に、改めて概要と基本機能を確認。
ELB(ElasticLoadBalancer)は、Webトラフィックを配下のEC2Instanceに適切に分散してバランスを取る仕組み、いわゆるロードバランサー。なぜ分散させる必要があるかというと、1台のサーバで処理可能なトラフィックには限りがあるから。また、AutoScalingや、Zone間分散(Multi-AZ)といった構成をとる為にも必要となる。

ELBの基本機能は、高負荷システムにおいて、肝となる重要なものばかり
なお、ELBは下記のプロトコルをサポートする
下記のような付加機能がある。必要に応じて自由に利用することができる。
ec2-authorize HOGE-SG --region ap-northeast-1 --source-or-dest-group amazon-elb-sg --source-or-dest-group-user amazon-elb # 上記を実行すると、下記のように出力される。(ELBからの通信に対して、TCP,UDP,ICMPの全ポートを許可、の意味) GROUP HOGE-SG PERMISSION HOGE-SG ALLOWS tcp 0 65535 FROM USER amazon-elb NAME amazon-elb-sg ingress PERMISSION HOGE-SG ALLOWS udp 0 65535 FROM USER amazon-elb NAME amazon-elb-sg ingress PERMISSION HOGE-SG ALLOWS icmp -1 -1 FROM USER amazon-elb NAME amazon-elb-sg ingress
例えば、一つのELBを30日間利用し、100GBのデータ送信を行なった場合の総額は、$18.8($18 +データ通信料 $0.80) となる。1時間未満は1時間分として請求される。
いくつかのクラウド系サービスにおいて、ロードバランサーには高めの初期費用がかかる(ここは以外と見落としがち)。ロードバランサをハードウェアで購入しようとすると数十万~数百万近くするので、これは妥当なように感じる。EC2では、初期費用ゼロで従量制課金で始められる。個人やスタートアップ企業において、この点は大きい。価格は $0.028/hour + データ転送量。(2011/7 Tokyo Region)よほど大規模なサービスでもない限り、月あたり数千円以内には収まる価格設定の為、気軽に利用可能となっている。
ELBには、1年間に限り、アカウント毎に、毎月下記の無料利用枠を利用することができる。これらは、自動で適用される15 GB データ処理を加えた Elastic Load Balancing 750 時間。
前述のことさえ知っていれば、あとはWeb上で数クリックするだけでELBを作成できる。が、本格的に運用していくうちに、いくつかの問題がでてくる。その対処を迅速に行う為に、詳細を知っておく必要がある。
最も大きな理由は、ELBに障害が発生し、その影響が甚大だから。そして、その時の対応は難しい。
個人的に、ELB障害の時切り分けがやりにくいと感じる理由は、ざっくり下記4点
実際、半年間、2つELBを運用して、ELBに起因していると思われる5〜10分程度の通信断(もしくは機能縮退)を、少なくとも4回程経験した(後からPremiumSupport等でAWSに問い合わせて初めてELBの問題だと判明した。 この発生頻度を、多いと取るか、少ないと取るかは人それぞれだだと思う。)
(※下記は、実際にELBの影響により突発的に応答遅延が生じた時のCloudWatchのSnapshot)

これら障害に直面した時に、的確で素早い判断を行うため、ELBの動作を深く知らなければならないと思った。
ELBの動作仕様は積極的に公開されていない。多くの箇所が、ブラックボックスな状態となっている。ELBが100%の可用性を保証するのなら、ブラックボックスな状態でも問題ないけど、そんなことは無いので推測していく。おおよそこんな感じらしい。(間違ってたらごめんなさい)

上記のうち、わりかし障害が起きるのが、ELBInstance。ELBInstanceは、死んでしまうと、自動で正常なものと入れ替わる。でも、この入れ替えには10分くらいかかってしまうらしい。その10分間、クライアントからのトラフィックの一部はひたすら破棄されっぱなし。
不良ELB InstanceがどちらのZoneに属しているかが分かれば、ZoneをELBの対象からRemoveすることで対応できそうだけど、60秒間のDNSキャッシュの問題とかあって、即時に切り離しとかは現実的に無理そう。
※ELBInstanceの存在を確認したければ、ELBのアドレスをdiggコマンドで引けばわかる。(トラフィックが多いときほど、多くのアドレスを返すはず)
また、実際に検証されている方のブログもあるので、参考になる。
処理の流れとしては、下記のような感じになる
運用してみて、初めて気づくこともある
詳しい説明の前に、まず、ヘルスチェックの設定によって、ELBがどのような動作を行うのか把握する必要がある。ELB設定は、ManagementConsoleの下記画面で確認できる。

上記で設定している場合のヘルスチェックの動きは、下記のようになる。
EC2Instanceには下記のようなアクセスログが残る(一番左側に記載されているIP(ELBのIP)が複数あることに注目。どうやら、各ELBInstance各々からヘルスチェックされるらしい)
10.148.145.201 - - [30/Jul/2011:23:45:50 +0900] "GET /index.php HTTP/1.1" 200 234 "-" "ELB-HealthChecker/1.0" "-" 10.148.74.236 - - [30/Jul/2011:23:45:50 +0900] "GET /index.php HTTP/1.1" 200 234 "-" "ELB-HealthChecker/1.0" "-" 10.148.145.201 - - [30/Jul/2011:23:45:56 +0900] "GET /index.php HTTP/1.1" 200 234 "-" "ELB-HealthChecker/1.0" "-" 10.148.74.236 - - [30/Jul/2011:23:45:56 +0900] "GET /index.php HTTP/1.1" 200 234 "-" "ELB-HealthChecker/1.0" "-" 10.148.145.201 - - [30/Jul/2011:23:46:02 +0900] "GET /index.php HTTP/1.1" 200 234 "-" "ELB-HealthChecker/1.0" "-" 10.148.74.236 - - [30/Jul/2011:23:46:02 +0900] "GET /index.php HTTP/1.1" 200 234 "-" "ELB-HealthChecker/1.0" "-" 10.148.145.201 - - [30/Jul/2011:23:46:08 +0900] "GET /index.php HTTP/1.1" 200 234 "-" "ELB-HealthChecker/1.0" "-" 10.148.74.236 - - [30/Jul/2011:23:46:08 +0900] "GET /index.php HTTP/1.1" 200 234 "-" "ELB-HealthChecker/1.0" "-" 10.148.145.201 - - [30/Jul/2011:23:46:14 +0900] "GET /index.php HTTP/1.1" 200 234 "-" "ELB-HealthChecker/1.0" "-" 10.148.74.236 - - [30/Jul/2011:23:46:14 +0900] "GET /index.php HTTP/1.1" 200 234 "-" "ELB-HealthChecker/1.0" "-" 10.148.145.201 - - [30/Jul/2011:23:46:20 +0900] "GET /index.php HTTP/1.1" 200 234 "-" "ELB-HealthChecker/1.0" "-" 10.148.74.236 - - [30/Jul/2011:23:46:20 +0900] "GET /index.php HTTP/1.1" 200 234 "-" "ELB-HealthChecker/1.0" "-"
話を本題に戻す。ヘルスチェック先は、「Ping Path」項で指定する。チェック先ということで、応答負荷の低そうな静的ファイルを指定したくなるが、動的ファイルにしたほうがよいと思われる。
何故かというと、静的ファイルを指定してしまうと、背後のアプリケーションサーバの生死を把握できないから。
例えば、NginxとPassengerが起動しているサーバを複数台LoadBalancer配下に登録しているとする。そして、突然Passengerプロセスが死んでしまった場合を考える。
また、Webサーバに比べてアプリケーションサーバは起動が遅い傾向にあるので、アプリケーションサーバが起動しきっていないのにユーザトラフィックを受け付けてしまい、AutoScaleでInstanceがふえる度にエラーが発生してしまうという事態になってしまう。
ELB配下のEC2Instanceは、ZoneAとZoneBで等数にしなければいけない(若しくは、片方のZoneのみの配置にする)。トラフィックのZone間分散アルゴリズムがラウンドロビンだからというのが理由( 前述のELB内部図解を参照)
等数にしないと、EC2Instanceのトラフィックバランスは不均等になってしまう。Zoneを分けて配置するということは、ReservedInstanceの購入計画にも影響を与えてくるので、事前に考慮して計画する必要がある。
なお、GUIでEC2Instanceを不均等に登録しようとすると、下記のように注意を促すアラート文が表示されるので、意図せず構成してしまうという事態にはなりにくい(逆に言うと、アラート文を無視すれば構成できてしまう)

等数にするということは、ELBの配下のInstanceを増加させる際、偶数単位になるので、スケールアウトの粒度が大きくなってしまう点も注意。
ELBとの通信元IPがある程度固定されるときは、正常にトラフィックが分散されない可能性があるので、念入りにチェックしたほうがいい。
例えば、 一台のクライアントから負荷試験等でトラフィックを投げるとき、ELB Instanceが複数いると(Multi-AZ化すると、ELBInstanceは最低でも2台起動する)、処理は均等にならない。(全てのトラフィックが、ZoneAとZoneBの間でばたつくことになる)これは、ELBの内部構造がDNSラウンドロビンなので、DNSキャッシュに依存してしまうから。片方のZoneにInstanceを寄せると、(負荷が低いうちは)ELB Instanceは一台しか起動しないので、回避できる
ELB Instanceは、トラフィックの増加に伴い、増加する。このELBInstanceの増減を、クライアント及びEC2管理者が意識することは無い。意識することは無いが、ELBInstanceが増減する時、一定量の機能縮退が起こると言われている。具体的には、クライアントの要求に対して、応答時間が長くなったりする。世の中には、この仕様が受け入れられないケースがある。(例えば、ソーシャルアプリを運用するケース等)動的なELBInstanceの増減を許容できない場合は、事前に想定されるトラフィック量をプレミアムサポートの、Webケースから事前申請しておくことで、ELBInstanceを増加させたままにしてもらうことができる(らしい)。なお、一日のPVが100万以下の小規模サイトでは、ELBの増加現象自体が発生しないので、ELB-Pre-Waringを行う必要性はまずなさそうです。
個人的にアツイと思っているネタ。
EC2には、Cloudwatchという機能があって、EC2の各要素のメトリックを1分単位で取得できる。主な使い方として各EC2InstanceのCPU状況のモニタリングなんかに利用したりする(下記は、複数EC2InstanceのCPUの負荷を取得して表示させた例)。特にOSに何も設定する必要もなく、負荷もかからずに自動で有効になっていて、クリック操作だけでサクっとグラフも表示できるので、結構楽です。

で、このCloudwatchはロードバランサ(ELB)でも働くんですが、その時のメトリック(取得可能な監視パラメータ)は、下記4つです。
LatencyとRequestCountを表示させると、こんな感じ。此処で言うLatencyの定義とは、ELBにリクエストが到達して、レスポンスを返すまでにかかった時間です。

RequestCountを表示させると、こんな感じです。

RequestCountとは、PVと同じようなもの。つまり、直近一分間のPV数がわかってしまうということになる。
しかも、GUIのクリック操作だけでアラートの設定もできるので、「1分あたりのRequestCount数が8000を超えたら自分のケータイにメール通知」なんてことも可能。
これはELBを利用することによって受けられる大きなメリットだと思う。
ELBの障害が疑われる場合、ELBまでの到達経路を調べることで解決に近づくことがあります。(ELBが悪いのか、ELBに到達するまでの経路が悪いのか、の切り分け等)
しかしながら、ELBはICMPを受け付けないので、Pingは通らない。tracerouteもダメ。(下記のようになる)
$ ping TestELB-1476501791.ap-northeast-1.elb.amazonaws.com PING TestELB-1476501791.ap-northeast-1.elb.amazonaws.com (175.41.252.138) 56(84) bytes of data. --- TestELB-1476501791.ap-northeast-1.elb.amazonaws.com ping statistics --- 5 packets transmitted, 0 received, 100% packet loss, time 4771ms $ traceroute TestELB-1476501791.ap-northeast-1.elb.amazonaws.com traceroute to TestELB-1476501791.ap-northeast-1.elb.amazonaws.com (175.41.252.138), 30 hops max, 60 byte packets 1 ip-10-148-8-2.ap-northeast-1.compute.internal (10.148.8.2) 0.719 ms 0.693 ms 0.672 ms 2 ec2-175-41-192-176.ap-northeast-1.compute.amazonaws.com (175.41.192.176) 0.349 ms ec2-175-41-192-178.ap-northeast-1.compute.amazonaws.com (175.41.192.178) 0.836 ms 0.820 ms 3 ec2-175-41-192-27.ap-northeast-1.compute.amazonaws.com (175.41.192.27) 0.252 ms ec2-175-41-192-21.ap-northeast-1.compute.amazonaws.com (175.41.192.21) 0.357 ms 0.339 ms 4 * * * 5 * * * 6 * * * 7 * * * 8 * * * 9 * * * 10 * * * 11 * * * 12 * * * 13 * * * 14 * * * 15 * * * 16 * * * 17 * * * 18 * * * 19 * * * 20 * * * 21 * * * 22 * * * 23 * * * 24 * * * 25 * * * 26 * * * 27 * * * 28 * * * 29 * * * 30 * * *
なので、調べるときはtracerouteをTCPベースで実行する必要がある。
具体的には、tracerouteコマンドに、-TオプションをつければOK(AWSのサポートに教えてもらった)
$ sudo traceroute -T ttCloudELB-1219434367.ap-northeast-1.elb.amazonaws.com traceroute to ttCloudELB-1219434367.ap-northeast-1.elb.amazonaws.com (46.51.254.241), 30 hops max, 60 byte packets 1 ip-10-148-8-2.ap-northeast-1.compute.internal (10.148.8.2) 0.548 ms 0.525 ms 0.732 ms 2 ec2-175-41-192-178.ap-northeast-1.compute.amazonaws.com (175.41.192.178) 30.403 ms 30.404 ms 30.396 ms 3 ec2-175-41-192-27.ap-northeast-1.compute.amazonaws.com (175.41.192.27) 0.368 ms 0.388 ms ec2-175-41-192-21.ap-northeast-1.compute.amazonaws.com (175.41.192.21) 0.363 ms 4 * * * 5 * * * 6 ec2-46-51-254-241.ap-northeast-1.compute.amazonaws.com (46.51.254.241) 0.486 ms 0.413 ms 0.497 ms
上記の結果は、ELB Instanceのアドレスである46.51.254.241までの到達経路を示します。
また、pingの代わりは、httpingが使えます。
$ httping -b -G -s -g ttCloudELB-1219434367.ap-northeast-1.elb.amazonaws.com PING ttCloudELB-1219434367.ap-northeast-1.elb.amazonaws.com:80 (ttCloudELB-1219434367.ap-northeast-1.elb.amazonaws.com): connected to 46.51.254.241:80 (278 bytes), seq=0 time=1188.68 ms 200 OK 70KB/s connected to 46.51.254.241:80 (278 bytes), seq=1 time=955.93 ms 200 OK 82KB/s connected to 46.51.254.241:80 (278 bytes), seq=2 time=1019.32 ms 200 OK 78KB/s connected to 46.51.254.241:80 (278 bytes), seq=3 time=1073.84 ms 200 OK 77KB/s connected to 46.51.254.241:80 (278 bytes), seq=4 time=1018.09 ms 200 OK 75KB/s connected to 46.51.254.241:80 (278 bytes), seq=5 time=954.95 ms 200 OK 82KB/s connected to 46.51.254.241:80 (278 bytes), seq=6 time=936.91 ms 200 OK 83KB/s connected to 46.51.254.241:80 (278 bytes), seq=7 time=983.32 ms 200 OK 81KB/s connected to 46.51.254.241:80 (278 bytes), seq=8 time=1097.39 ms 200 OK 72KB/s
AWSに問い合わせるときは、このような情報も添えると解決が早くなる可能性があるので、覚えておいて損はないです。
ELBの障害を契機に、改めてELBについて調べてみたところ、分かっているようでよくわかっていなかった部分が多かったと思い知った。本格的にAWSを運用している人は、突然サービスが止まってしまった時、ELBの障害という可能性も疑って頂きたい。そして、障害切り分けの手順対象にELBを追加し、事前に確立しておく。障害発生時は慌てず関係者に説明、オペレーションを経てAmazonに問合せ。 できるならば、 ELB障害時フェイルオーバーするようなシステム設計もできればそれがBESTです(自分も模索中)。
AWSの利用者、利用シーンは確実に増えているのに、実運用から発生する現場志向的ノウハウがネット上に不足しているように感じる。そろそろ、現場志向の情報共有を行う場が必要だなと日々思う。
で、ここまで書いといてなんですが、間違いや思い違いを見つけたら(多分ある)、ご指摘のコメント頂けると幸いです。
最後に、今読んでいるOreillyの「ウェブオペレーション」から、ささった言葉を引用
優柔不断はあらゆる分野で不利になるが、ウェブオペレーションでは絶対に許されない。救命救急士やERドクターと同じように、よい判断を今すぐしなければならない。決断の遅れが長時間の機能停止につながる。脳を鍛え、受け取った情報に常に意識を集中すること。情報の「収集・調査・提案」が、あらゆる問題の解決の糸口となるはずだ。