tail my trail

作るのも使うのも、結局は、人なのだ

WebSocket対応した噂のALB (Application Load Balancer) を試してみた

TL;DR

  • 2016年8月にAWSのLoad Balancerが WebSocketに対応した
  • スムーズすぎて心配になるくらい簡単に導入できる
  • 「うまい、はやい、やすい」ので、導入しない理由はないと思う

ALB Release!

巷では長らく噂になっていた、新しいAWSのLoad Balancer。 先月2016年8月11日についにリリースされた。待望のL7対応だ。

個人的に魅力の一つだったのはWebSocket対応。

従来のELBではWebSocket通信を終端できなかったため、 WebSocket通信の負荷分散/バランシングを行ったりSSL/TLS化する場合、 自前でバランシングする仕組みを実装したり、Nginxを前段に配置するなどちょっとした工夫が必要だった。 AWSのLoad BalancerがWebSocketサポートしてくれるのであれば、この設計構築および運用をアウトソース出来ることになる。ハッピーな未来しか見えない。

ちょうど時間ができたので、遅ればせながら試してみた。

新しいLoad Balancer, ALBとは

至るところで紹介されているので、ここでは簡潔に。

Application Load Balancer、通称ALBは、Elastic Load Balancerのラインナップの一つという位置付けで発表された。 以前のELBは "Classic Load Balancer" という扱いになるらしい。 Management ConsoleのLoad Balancerを開くと、以下のように2つから選択できるようになっている。

f:id:uorat:20160924224519p:plain

主だったUpdateは以下。 魅力盛り沢山!

  • Content-Based Routing (Path Based Routing):
    • URLパスに基いて、リクエストを異なるバックエンドサーバに振り分けすることが出来る
  • Support for Container-Based Applications:
    • ECS (EC2 Container Service) - ELB 連携対応、ポートマッピングやヘルスチェック定義の運用性が大幅改善
  • Better Metrics:
    • ポートやURLパス (ターゲットグループ) 毎のメトリクス、トラフィック(GB)、アクティブコネクション数、コネクションレートなどメトリクスを拡充
  • Support for Additional Protocols & Workloads:
    • WebSocketとHTTP/2 をサポート

引用: Amazon Web Services ブログ (日本語)

引用: AWS Blog (English)

WebSocketとLoadBalancer

これまでのWebSocketアプリケーションの構成

おさらい。

詳しくは以前の記事に記載しているが、従来のELBではWebSocket通信をサポートしていなかったため、 WebSocket通信の負荷分散/バランシングを行うためには、自前でバランシングする仕組みを用意する必要があった。

SSL/TLS化する場合も同様。ELBで終端することが出来ないためアプリケーション側で実装したり、Nginxを前段に配置するなどちょっとした工夫が必要だった。

これからのWebSocketアプリケーションの構成

通常のWeb Applicationと同じ構成が取れる。つまりこういう事だ。

f:id:uorat:20160924224649p:plain

Try it!

百聞は一見にしかず。試してみる。

なお、ALBの登場に伴い、AWSのLoad Balancerの構成要素が変わったので注意。一瞬面食らうが、思想がわかれば大した差ではない。

Make Target Group

今までのバックエンドサーバの設定が Load Balancer から独立し Target Group という要素に分離された。 L7対応ということでContent-based Routingが可能になったので、Load Balancerそのものと、後段のバックエンドサーバの定義の関係性が疎になったイメージ。

ということで、まずバックエンドサーバとTarget Groupを用意する。 バックエンドサーバは、以前使ったNode.js / Socket.IOなサンプルアプリケーションを再利用し、EC2上で稼働させる。

server

package.json

How to run

$ vim app.js
$ vim package.json
$ vim /usr/share/nginx/html/sample.html
$ npm install
$ node app.js

それからTarget Groupを作成する。

基本的に今までのELBのバックエンドサーバの設定箇所と同じ。 ここで設定/作成するTarget Groupを、後ほどALBに振り分けルールとセットで登録することとなる。

  • バックエンドのNode.jsサーバは :3000 で listen しているので、ProtocolはHTTP、ポートは3000を選択する
  • Health Check Pathは 今回は /socket.io/socket.io.js を指定する

f:id:uorat:20160924231855p:plain

まず、これでTarget Groupを作成。

その後、Target GroupにInstanceを登録する。今回はテスト用に2台のEC2 instanceを用意したので、これをTarget Groupとして設定する。

ここまで特にWebSocket特有の設定項目はない。

一点注意点として、Socket.IOベースのアプリケーションを使用している場合は、Target Groupの Attribute を編集してStickness を有効化すること。 つまり、Sticky Sessionを効かせる。

f:id:uorat:20160924225023p:plain

理由は後述する。

Launch ALB

次に、Load Balancerを作成する。

まず、Application Load Balancer を選択する。

f:id:uorat:20160924224519p:plain

ALBの設定画面はいつも通り

  • 名前とScheme (internet-facing か internal ) を決め、ALBのListenerとAZの設定を行う
  • Listner Protocol は Target Groupと同じくHTTP / HTTPS のいずれかを選択すれば良い

f:id:uorat:20160924231919p:plain

HTTPSを選んだ場合は証明書を選択し、続いてSecurityGroupを選択/作成する。

ここまでは従来のELBと同じ。

次にRouting先のTarget Groupを設定する。

先程作成したTarget Groupをルーティング先として指定する。 これがデフォルトのルーティング先となる。

f:id:uorat:20160924225429p:plain

前述したとおり、URLパスに基づいて複数のTarget Groupを登録することも出来る。 複数のサブシステムやマイクロサービスな作りをしているものを一つのLB、ドメインでまとめたり、アプリケーションコードを変えずにALBだけでパスルーティングを返ることが出来たり、 運用が幸せになる可能性を感じる。

設定内容が問題ないことを確認後、ALBを立ち上げる。

これだけ。 WebSocket特有の設定項目は必要ない。

Run!

このLoad Balancer経由でサンプルアプリケーションを稼働させる。

以下の sample.html を手元のPCで作成し、ブラウザで開くとサンプルアプリケーションが立ち上がる。

ブラウザのDeveloper ConsoleなどでHTTP Upgrade (101) やWebSocketの通信状況を確認して、無事接続確立していればOK。

f:id:uorat:20160924225800p:plain

心配になるくらい、驚くほどあっさり動いてしまう。

Socket,IO 利用時の注意点

前述したが、Socket.IOを利用しているWebSocketアプリケーションの場合、Sticknessを有効化しておく必要がある。 これはSocket.IOのハンドシェイク処理の仕様で、接続確立までに行われる幾つかのHTTPリクエストが同一サーバに固定される必要がある。 公式ドキュメントにも言及されている。

Socket.IO — Using multiple nodes

Sticknessが無効のままだと以下のように400エラーや502エラーが発生し、接続確立に失敗する。

f:id:uorat:20160924225829p:plain

この時のResponse Dataを見てみると、前リクエストでSession ID が見つからない旨出力される。

{"code":1,"message":"Session ID unknown"}

シンプルにWebSocketを話すアプリケーションは特にSticknessの有効化は不要だと思うが、 環境やブラウザによってはWebSocketを話せないケースもあるため、両方に対応可能なSocket.IOを利用するケースは多いと思うので、参考までに。

所感

何度も言うが、心配になるくらい、驚くほどあっさり動いてしまう。 簡単なチャットアプリケーションであれば、ALB前段にかませばそれで終了だ。

WebSocket接続用のEndpointの管理が不要となり、DNS Recordのメンテは初回以降不要。 SSL/TLS通信の終端先としても使えるのでReverseProxyを置く必要もなくなる。 Target GroupにAutoScaling Groupの登録もできるようなので、普通のWebアプリケーションサーバと同じ感覚でScalingできるようになる。

WebSocketアプリケーションの作りや規模によってはシャーディングっぽい作りが必要になることもあると思うが、 その際はTarget GroupとPath Rule追加して、同じALBにぶら下げれば良い。

ALBを活用すればランニングコスト、構築や運用にかかる労力ともに減るので、メリットしか感じられない。

がしがし活用していこう。