LoginSignup
41

More than 5 years have passed since last update.

Elastic BeanstalkのAutoScale環境でWebSocketを使う(Sails.js 0.10.x)

Last updated at Posted at 2014-12-17

どのAdventCalendarにしようか迷った挙句AWSにしてみた。

非常にややこしいタイトルですが、Sails.js(node.js)をElastic BeanstalkのAutoScale環境に載せた上で、WebSocketを使用する方法です。

以前投稿した記事でSails.js(node.jsのフレームワーク)を使用したリアルタイムWebを実現しましたが、今回はそのサービスを公開できる環境を整えます。要件としては、

  • HTTPSで通信すること
  • AutoScaleの設定をしてELB(ロードバランサ)を設定すること

の2点です。基本的にそんなに難しくないのですが、これがWebSocketと相性が悪く、素直にやるとうまくいきません。

1.Beanstalk環境を作成

まずはElastic Beanstalkの環境を作成。特に難しいことはやってないので作ったことのある人はスルー推奨。画像のない箇所はとりあえずNextで。

アプリ名を設定

1.png

動作環境を設定

今回はnode.jsを使います。また、Load balancingの設定を忘れずに。

2.png

VPCを設定

3.png

インスタンスの設定

サンプルのために財布を痛めたくないのでt2.microを設定。EC2 key pairは自分で作ったやつです。
4.png

VPC設定

こんな感じで。
5.png

動作確認

Greenになったら上のURLからページが表示されるか確認。Greenになってもしばらく繋がらないこともあるので気長に待ちましょう。

6.png

2.HTTPSを適用する

鍵を作る

いわゆるオレオレ証明書を作ります。

$ openssl genrsa -aes128 2048 > server.org
$ openssl rsa -in server.org > server.key
$ openssl req -new -key server.key > server.csr
$ openssl x509 -req -days 365 -in server.csr -signkey server.key > server.crt

Listenerの追加

ロードバランサがListenするサービスを設定します、EC2メニューでロードバランサを選択、ListenersタブでEdit。

7.png

HTTPSを追加、証明書を設定するために[SSL Certificate]を選択。

9.png

Private Keyに.keyファイルの中身、Publick Key Certificateに.pemファイルの中身をコピペ。今回は自己証明なのでChainは不要。

10.png

ELBのSecurity Groupの設定

Listenerの設定だけではダメでELBに設定されたSecurityGroupでもポートを開かないといけない。次はSecurityタブからSecurityGroupを選択。

11.png

Security Groupの設定ページに飛ぶので、InboundをEdit。

13.png

HTTPSを追加。

15.png

動作確認

ここまで設定できていたら動作確認。HTTPでもHTTPSでも繋がるはず。

3.HTTPをHTTPSへリダイレクトする

実際のサービスでは同じページをHTTPとHTTPSの両方で表示するということは稀で、ユーザ情報を扱うサービスならHTTPSは必須。この時単純にHTTPを禁止してしまうと間違ってHTTPでアクセスしたユーザにはページが表示されずに機会損失につながるので、HTTPでアクセスしてきたユーザはHTTPSへリダイレクトしてあげます。

AutoScale環境下での問題点

問題点というほどでもないんですけど、AutoScale環境(ELBを通した環境)での注意点。先ほどELBのListenerを設定しましたが、HTTPSで来た通信をELBでHTTPに変換しています。こうすることでEC2インスタンス個々にSSL証明書を設定する必要がなくなるのですが逆にEC2側からはすべてHTTPに見えるためプロトコルを判定できない。

6026d97f-1c52-fc61-f94d-7e1085058eab.jpeg

じゃあどうするかというとELB上でHTTP/HTTPで来た通信のポート番号を変更してEC2に渡してあげる。EC2側ではポート番号からHTTPなのかHTTPSなのかを判定し、HTTPの場合はHTTPSになるようにリダイレクトしてあげます。

dcd242c7-2144-806f-1bce-4aa94e244b40.jpeg

ELBのListenerの設定

先ほどのListener設定にて、HTTPアクセスを適当なポート(ここでは12345)にフォワードするように変更。

16.png

ELBのSecurity Group設定(Outbound)

ELBから12345ポートでEC2に接続できるよう設定。本気でサービス稼働を考える場合はDestinationにEC2 Security Groupを設定しましょう。

17.png

EC2のSecurity Group設定

EC2に設定されているSecurity Groupを探し...

18.png

先ほどELCで設定したポートのInboundを許可する。

19.png

ここまででAWS Console上での設定は完了。

nginxの設定

残りはEC2インスタンス側での設定。nginxの設定でリダイレクトします。今回はSSHでインスタンスに直接つなぎに行って設定していますが、これだとAutoScaleでインスタンスが再生成されたときに対応できない。本当は.ebextensionsに書かないと行けないのだけれども時間が無いのでいつか追記します...

EC2の以下の設定ファイルを変更。

/etc/nginx/nginx.conf
/etc/nginx/nginx.conf
http {

# Elastic Beanstalk Modification(EB_INCLUDE)
include /etc/nginx/conf.d/*.conf;
# End Modification

    port_in_redirect off;
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;

    keepalive_timeout  65;

    ############################ ここから追加
    server {
      listen 12345;
      server_name chatsample-env.elasticbeanstalk.com;
      rewrite ^(.*) https://chatsample-env.elasticbeanstalk.com$1 permanent;
    }
    ############################ ここまで追加
}

nginxを再起動

$ sudo service nginx restart

これでリダイレクトできます!

4.WebSocketを通す

ここまで設定をして、このアプリケーションをデプロイしてみると分かるのですが、WebSocketが動きません。何故かwebsocketのリクエストが502で返ってきていて、仕方なくpolingしています。まあpolingはうまく出来ているようなのでSocket.IOとしては動いているのですがpolingの限界か、とてもリアルタイムWebと言える代物ではないです。

21.png

というわけでこれを解消する方法について。

原因

Bad Gatewayになる主な原因はELB(ロードバランサ)。ちょっと見づらいパケットキャプチャですが、websocketのリクエストのConnectionがになってます。WebSocketのリクエストの場合は本来になっていないといけない。

どうやらELBにHTTP/HTTPS通信が来た場合、無駄な気をきかせてに書き換えるそう。というわけでHTTPSでやったような感じで対策しましょう。

23.png

ELBのListener設定

HTTPSと同じようにListenerを設定。HTTP/HTTPSは使えないがWebSocketもセキュアな通信をしたいのでSSL(Secure TCP)を選択。[Load Balancer Port]と[Instance Port]は何でもいいですが、後で使うので覚えておきましょう。今回は3000と11111を使う。

26.png

ELBのSecurity Group設定

Inboundに3000を追加

27.png

Outboundには11111を追加

28.png

EC2のSecurity Group設定

Inboundに1111を追加

29.png

nginxの設定

nginxにWebSocket用の設定を追加。

/etc/nginx/nginx.conf
http {

# Elastic Beanstalk Modification(EB_INCLUDE)
include /etc/nginx/conf.d/*.conf;
# End Modification

    port_in_redirect off;
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;

    keepalive_timeout  65;

    server {
      listen 12345;
      server_name chatsample-env.elasticbeanstalk.com;
      rewrite ^(.*) https://chatsample-env.elasticbeanstalk.com$1 permanent;
    }

######################### ここから追加
   server {
     listen 11111;

     location / {
      proxy_pass http://nodejs;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
     }
   }
########################## ここまで追加
}          

アプリケーションの修正

何度も言いますが今回はこのアプリケーションで動作確認をします。今回修正が必要なのは2点。

まずはWebSocketを自動で接続する設定を解除(というかこれ前の記事でもやらないといけない設定だった...)

/assets/js/dependencies/sails.io.js
    // Set a `sails` object that may be used for configuration before the
    // first socket connects (i.e. to prevent auto-connect)
    io.sails = {

      // Whether to automatically connect a socket and save it as `io.socket`.
      autoConnect: false,

そして自作のソケット接続箇所でURLを指定。この時にWebSocket用にELBに設定したポート(3000)を指定。

/assets/js/main.js
(function() {

  // Socket.IOに接続
  var socket = window.io.connect('https://' + location.host + ':3000');

これをデプロイしたら完了です!

動作確認

ウィンドウを2つ開いて動作確認します。ネットワークコンソールを見てみるとWebSocketのリクエストが通ってる!

30.png

そしてしっかりとリアルタイム通信できました!

31.png

残タスク

とりあえずWebSocketの接続確立までは出来ました。ただし、このままではAutoScaleで複数インスタンス立ち上がった時に、別インスタンスにアクセスしてしまうと動きません。

サービスがスケールするまではこの方法で複数インスタンスを一つのmongoインスタンスに接続し、セッションを一括管理する感じでも大丈夫です。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41