ELBについてとことん理解する

2011/07/31 | Filed in ■トラフィック分散(ElasticLoadBalancing)

ELB(AmazonEC2のロードバランサー)について深く知りたくなってしまったので、改めて調べたり聞いたりした。
今回そもそも知りたかったポイントは下記の2点

  • ELBがどういう仕組みで膨大なトラフィックに耐えているのか
  • ELBで稀に障害が発生するみたいなので、その影響をなんとか回避できないか

これらを理解するためには、ELBの内部仕様まで知る必要があった。調べていくうちに、単純だと思ってたELBは、実はすごく複雑であることを思い知った。

 














ELBの概要

内部仕様に踏み込む前に、改めて概要と基本機能を確認。

ELBの役割

ELB(ElasticLoadBalancer)は、Webトラフィックを配下のEC2Instanceに適切に分散してバランスを取る仕組み、いわゆるロードバランサー。なぜ分散させる必要があるかというと、1台のサーバで処理可能なトラフィックには限りがあるから。また、AutoScalingや、Zone間分散(Multi-AZ)といった構成をとる為にも必要となる。
about elb

ELBの基本機能

ELBの基本機能は、高負荷システムにおいて、肝となる重要なものばかり

負荷分散
  • ELBは、大量のトラフィックにより、単一Instanceが過負荷になってダウンすることを解決する。ELBは、ユーザからの全てのアクセスを一旦受け、登録されているEC2 Instanceにトラフィックを分散して割り振る。単純にラウンドロビンで割り振るのではなく、負荷の具合をみて、適切な量を割り振る。結果的に、ELB配下のInstanceの負荷は均一になってゆく。また、障害を起こしたEC2Instanceを自動的に排除する機能をもつ
Zone間冗長(Multi-AZ)
  • ELBは、単一Zone(単一Region内にある、複数のデータセンターのうちのひとつ)が、(電源障害等で)丸ごとダウンして利用できなった際でも、サービスが継続することができる構成である、Mutl-AZ構成構築するのを簡単にする
AutoScaling
  • AutoScalingをシステムに組み込む為に、ELBが必要。EC2Instanceが自動的に起動してきたとき、ELBは自動でそのEC2Instanceを配下に登録し、トラフィックを割り振る

なお、ELBは下記のプロトコルをサポートする

  • HTTP
  • HTTPS
  • SSL
  • TCP プロトコルを使用するアプリケーション

ELBの付加機能

下記のような付加機能がある。必要に応じて自由に利用することができる。

セッション維持機能
SSL termination
IPv6のサポート
  • まだ米国東リージョンとヨーロッパリージョンだけ(2011/7/23時点)
Zone Apexのサポート
  • 前に詳細書いた。利用可能なドメインの制限を解決する。凄い大事。
EC2 Security Groupのサポート
  • EC2 Instanceへのアクセス元として、ELBを指定出来る機能。最近サポートされたけど、なぜ今までなかったのか不思議。AWSブログをみるには、ユーザからの要望の結果らしい。ELBのSG名は固定(amazon-elb-sg)らしく、下記のように指定する。

    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のコスト

例えば、一つの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 時間。
  • ELBの内部動作仕様

    前述のことさえ知っていれば、あとはWeb上で数クリックするだけでELBを作成できる。が、本格的に運用していくうちに、いくつかの問題がでてくる。その対処を迅速に行う為に、詳細を知っておく必要がある。

    ELBの内部動作仕様を知らなければならない理由

    最も大きな理由は、ELBに障害が発生し、その影響が甚大だから。そして、その時の対応は難しい。
    個人的に、ELB障害の時切り分けがやりにくいと感じる理由は、ざっくり下記4点

    ELBは簡単に交換できない
    ELBは、ElasticIPのような取り外し可能なIPを持つことが出来ない。ELBのアドレスは外部へDNSで公開しているので、伝搬遅延等も考えると、安易に再登録もできない。なので、EC2Instanceのように、不良になったら直ぐ入れ替え、という判断は難しい
    ELBはブラックボックス
    ELBは、公式に内部仕様が公開されていない。ステータスも取得するすべもない(あるかもしれないけど知らない)
    ELBの障害はあまりにも突然に起こる
    障害の兆候がなく(把握する術がない)、発生したら即サービス影響
    ELBの障害に関して、AWSから報告が無い(ことが多い)
    ELBの障害範囲が小さいからなのかわからんけど、大抵Service Health Dashboardに何も表示されない。(表示されることもあるし、フォーラムに話題が出ることもあるらしい。)障害の有無については、プレミアムサポートで問い合わせると教えてくれる

    実際、半年間、2つELBを運用して、ELBに起因していると思われる5〜10分程度の通信断(もしくは機能縮退)を、少なくとも4回程経験した(後からPremiumSupport等でAWSに問い合わせて初めてELBの問題だと判明した。 この発生頻度を、多いと取るか、少ないと取るかは人それぞれだだと思う。)

    (※下記は、実際にELBの影響により突発的に応答遅延が生じた時のCloudWatchのSnapshot)
    ELB障害発生時、応答時間が長くなったり、全く返せなくなったりする現象が、短時間だが発生する

    これら障害に直面した時に、的確で素早い判断を行うため、ELBの動作を深く知らなければならないと思った。

    ELBの内部図解(予想)

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

    ELB Internal

    上記のうち、わりかし障害が起きるのが、ELBInstance。ELBInstanceは、死んでしまうと、自動で正常なものと入れ替わる。でも、この入れ替えには10分くらいかかってしまうらしい。その10分間、クライアントからのトラフィックの一部はひたすら破棄されっぱなし。
    不良ELB InstanceがどちらのZoneに属しているかが分かれば、ZoneをELBの対象からRemoveすることで対応できそうだけど、60秒間のDNSキャッシュの問題とかあって、即時に切り離しとかは現実的に無理そう。


    ※ELBInstanceの存在を確認したければ、ELBのアドレスをdiggコマンドで引けばわかる。(トラフィックが多いときほど、多くのアドレスを返すはず)
    また、実際に検証されている方のブログもあるので、参考になる。

    ELBの内部処理フロー(予想)

    処理の流れとしては、下記のような感じになる

    1. クライアントは、ELBのDNS(XX.amazonaws.com)に向けてアクセスする
    2. DNSで受けたトラフィックは、ZoneAとZoneBに等しく分散(RoundRobin)される(MultiAZの構成にしておいた場合)
    3. ELBの中ではELBInstanceが待ち受けている。ELBInstanceの役目は、自身へ向けられたトラフィックをEC2Instanceへリバースプロキシ(受け流し)すること。ELBInstanceは、トラフィックの量に応じて増減する。
    4. ZoneAとZoneB、各々で立ち上がるELBInstanceの数は等しい。このELBInstanceに対して、トラフィックはRoundRobinされる
    5. ELBInstanceは、トラフィックを自身の属するZone配下の、登録されたEC2Instanceに、付加調整(Load Adjustment)しながらリバースプロキシする
    6. ELBは、常に配下のEC2Instanceの生死確認(ヘルスチェック)を行う。HTTPレスポンス200を応答しなけば、ステータス「Out of Service」とし、そのEC2Instanceをトラフィック送信先から除外する

    ELBを利用したシステム設計で気をつけること

    運用してみて、初めて気づくこともある

    ELBのヘルスチェック先は動的ファイルを指定


    静的ファイルを指定してしまうと、リクエスト応答の不具合を正常に検知できない場合がある。

    詳しい説明の前に、まず、ヘルスチェックの設定によって、ELBがどのような動作を行うのか把握する必要がある。ELB設定は、ManagementConsoleの下記画面で確認できる。

    • Ping Protocol:HTTP
    • Ping Port:3001
    • Ping Path:/index.php
    • Response Timeout: 3s
    • Health Check Interval: 0.1m
    • Unhealthy Threshold: 4
    • Healthy Threshold: 4

    上記で設定している場合のヘルスチェックの動きは、下記のようになる。

    • 0.1m(6秒)間隔で、登録しているEC2Instanceに対してヘルスチェックを行う。
    • 各EC2InstanceのPort3001のindex.phpファイルに対してHTTPアクセスを行い、3秒以内のResponseCode 200応答が4回以上(4回連続で)あったら、そのEC2Instanceを正常(in Service)と判断する。
    • in Serviceと判断されたEC2 Instanceへは、ユーザからのトラフィックを負荷調整しつつ割り振る。
    • ResponseCode 200の応答が4回以上(4回目に)無かったら、そのEC2Instance を異常(Out of Service)とする。
    • レスポンスに3秒以上かかった場合も、応答無しとしてカウントする
    • 連続で成功しないといけないので、もし3秒以上かかった場合には、カウントし直しとなる。
    • 未確認だが、応答が60秒以上無い場合も無条件でOutOfServiceとなるらしい。
    • レスポンスが正常に行えても、3秒以上かかった場合は、一回にカウントしない。

    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プロセスが死んでしまった場合を考える。

    PingPathが動的ファイルの場合
    Passengerプロセスが死んでしまうと、動的ファイルが生成できなくなる。WebサーバはResponce200を返すことができない為、ELBはそのEC2Instanceを異常(Out of Service)とみなしてユーザトラフィック割り当てる対象から除外する。サービス影響は発生しない。
    PingPathが静的ファイルの場合
    Passengerプロセスの有無に関係なく、Webサーバは静的ファイルに対して応答可能。故に、そのEC2InstanceはELBから正常(In Service)と判断され続けてしまい、ユーザトラフィックに対してエラーを返し続ける。

    また、Webサーバに比べてアプリケーションサーバは起動が遅い傾向にあるので、アプリケーションサーバが起動しきっていないのにユーザトラフィックを受け付けてしまい、AutoScaleでInstanceがふえる度にエラーが発生してしまうという事態になってしまう。

    Zone毎に等数配置しないといけない

    ELB配下のEC2Instanceは、ZoneAとZoneBで等数にしなければいけない(若しくは、片方のZoneのみの配置にする)。トラフィックのZone間分散アルゴリズムがラウンドロビンだからというのが理由( 前述のELB内部図解を参照)
    等数にしないと、EC2Instanceのトラフィックバランスは不均等になってしまう。Zoneを分けて配置するということは、ReservedInstanceの購入計画にも影響を与えてくるので、事前に考慮して計画する必要がある。
    なお、GUIでEC2Instanceを不均等に登録しようとすると、下記のように注意を促すアラート文が表示されるので、意図せず構成してしまうという事態にはなりにくい(逆に言うと、アラート文を無視すれば構成できてしまう)

    等数にするということは、ELBの配下のInstanceを増加させる際、偶数単位になるので、スケールアウトの粒度が大きくなってしまう点も注意。

    クライアントのDNSキャッシュに注意

    ELBとの通信元IPがある程度固定されるときは、正常にトラフィックが分散されない可能性があるので、念入りにチェックしたほうがいい。

    例えば、 一台のクライアントから負荷試験等でトラフィックを投げるとき、ELB Instanceが複数いると(Multi-AZ化すると、ELBInstanceは最低でも2台起動する)、処理は均等にならない。(全てのトラフィックが、ZoneAとZoneBの間でばたつくことになる)これは、ELBの内部構造がDNSラウンドロビンなので、DNSキャッシュに依存してしまうから。片方のZoneにInstanceを寄せると、(負荷が低いうちは)ELB Instanceは一台しか起動しないので、回避できる

    ELB Pre-Warninが必要なパターン

    ELB Instanceは、トラフィックの増加に伴い、増加する。このELBInstanceの増減を、クライアント及びEC2管理者が意識することは無い。意識することは無いが、ELBInstanceが増減する時、一定量の機能縮退が起こると言われている。具体的には、クライアントの要求に対して、応答時間が長くなったりする。世の中には、この仕様が受け入れられないケースがある。(例えば、ソーシャルアプリを運用するケース等)動的なELBInstanceの増減を許容できない場合は、事前に想定されるトラフィック量をプレミアムサポートの、Webケースから事前申請しておくことで、ELBInstanceを増加させたままにしてもらうことができる(らしい)。なお、一日のPVが100万以下の小規模サイトでは、ELBの増加現象自体が発生しないので、ELB-Pre-Waringを行う必要性はまずなさそうです。

    Tips

    PVを監視できる

    個人的にアツイと思っているネタ。

    EC2には、Cloudwatchという機能があって、EC2の各要素のメトリックを1分単位で取得できる。主な使い方として各EC2InstanceのCPU状況のモニタリングなんかに利用したりする(下記は、複数EC2InstanceのCPUの負荷を取得して表示させた例)。特にOSに何も設定する必要もなく、負荷もかからずに自動で有効になっていて、クリック操作だけでサクっとグラフも表示できるので、結構楽です。

    で、このCloudwatchはロードバランサ(ELB)でも働くんですが、その時のメトリック(取得可能な監視パラメータ)は、下記4つです。

    • HealthyHostCount(ELB配下の、生きてるホストの数)
    • Latency(遅延)
    • RequestCount(ELBへのリクエスト数)
    • UnHealthyHostCount(ELB配下の、死んでるホストの数)

    LatencyとRequestCountを表示させると、こんな感じ。此処で言うLatencyの定義とは、ELBにリクエストが到達して、レスポンスを返すまでにかかった時間です。

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

    RequestCountとは、PVと同じようなもの。つまり、直近一分間のPV数がわかってしまうということになる。
    しかも、GUIのクリック操作だけでアラートの設定もできるので、「1分あたりのRequestCount数が8000を超えたら自分のケータイにメール通知」なんてことも可能。
    これはELBを利用することによって受けられる大きなメリットだと思う。

    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ドクターと同じように、よい判断を今すぐしなければならない。決断の遅れが長時間の機能停止につながる。脳を鍛え、受け取った情報に常に意識を集中すること。情報の「収集・調査・提案」が、あらゆる問題の解決の糸口となるはずだ。

    0 comments Tags : ,
    No comments yet. Be the first to leave a comment !
    Leave a Comment

    Previous Post
    «
    Next Post
    »
    Cloude designed by WebdesignIn conjunction with Free MMORPG Games , MonitorBankRates.com , Fat Burning Furnace Trial.