webscoket-railsをAWS OpsWorksで使いたい

こんばんは,@h3_potetoです.

先日インターン生がリリースしたサービスで,langmatch.comというやつがありました.
これ,内部にチャット機能を実装するという要望があって,実に楽しそうでした.
開発自体は@ij_spitzがすべてやってくれたのですが,本番公開に向けて,AWS OpsWorksで動作させなければならないという,とても深そうなところがあったので,僕が手を出してみました(仕事を奪っている).

サーバー側からpush通知を飛ばす


チャットにおいて,こちらがわからPOSTする際はAjaxでPOSTしてやれば問題なさそうな気がします.
でも,相手から返信があった場合は?

そう,push通知ですよね.


サーバー側からpush通知を飛ばす方法自体は山のように考えられます.
それこそAWSを使うこと前提ならSQSとSNSで実現することだって可能.

しかし今回は,Railsでこういうときに割りと用いられているWebsocket-railsを使って開発されていました.
その辺,割りと自由になんでも使っていいと言ってありましたので.


Websocket-railsの使い方は以下を参考にしてください.
https://github.com/websocket-rails/websocket-rails


このGem,今回はじめて触ってみたのですが,非常に簡単にpush通知を実現できるのでかなりオススメです.


コントローラからpush通知を飛ばす,それをjsで受け取る,というアクションを追加するくらいで,面倒なのはそこをつなぐ設定くらい.


AWSに乗せて動かす


さて,今回はこいつをAWSのOpsWorksにぶら下がるEC2インスタンスで動作させることを考えます.



まず,websocket-railsは起動時にredis-serverを要求してきました,
内側でKey-value storeを使っているのだろうけれど,このへんはAWSのElastiCacheでRedisを一つ作って対応する.

こういうところは分離しておいたほうが楽だよ…….


設定はだいたいこんな感じ.


config/initializers/websocket-rails.rb

WebsocketRails.setup do |config|

  config.log_level = :debug


  config.log_path = "#{Rails.root}/log/websocket_rails.log"

  config.standalone = true
  config.standalone_port = Settings.websocket.port

  config.synchronize = false

  if Rails.env.production?
    config.redis_options = {:host => Settings.redis.host, :port => Settings.redis.port}
  end

end


redis_optionsで設定しなければ,自動的にローカル側でredis-serverの起動を必要とされるので,developmentで動かすときなどは先にredisを起動する必要があります.
Settings.redis.hostとSettings.redis.portはAWSのElastiCacheで用意したRedisの情報を入れておけば問題なさそう.

ElastiCacheでCache Clusterを作成した後,Replication Groupsを作成し,作成したCache Clusterを関連付ける.
するとRead Endpointが設定される.
これがホスト名とポートになる.

AWSVPC内になるので,ここはあまりポート番号を気にせずアクセスできるかと思いますが,多分基本的に6379になっているんじゃないかな.




さぁーって,これで動くかなぁー!!と思ってwebsocket-railsのサーバーを起動してみたのだけれど……

$ bundle exec rake websocket_rails:start_server RAILS_ENV=production

まぁ動くはずがない.


だって,Settings.websocket.portで設定したポート開放されてないんだもの.


ポートに穴を開けよう


原因はわかっているのだけれど,OpsWorksで動くプロジェクトのポート関連の設定は割りと面倒臭かった.


まずセキュリティグループを新規で一つ追加しました.
そのセキュリティグループに,Settings.websocket.portのポート番号を通します.


そして,

・OpsWorksのLayerセキュリティグループに追加
・ELBのセキュリティグループに追加
・ELB自体のポートに穴あけ

としてやります.


これでポートの穴あけは完了しました.

ようやっとELBからアクセスしても動くよ!!!


websocket-railsのプロセスをなんとかする


これ,チャットを上手く動かしておくためにはwebscoket-railsのサーバーを起動しておかなければなりません.
ましてやstandaloneサーバーモードとか…….

このへんは@ij_spitzが書いてくれました.


まず,before_restart時にコマンドを実行してやる.

Chef::Log.info("Running deploy/before_restart.rb...")
current_release = release_path

node[:deploy].each do |application, deploy|
  if deploy[:rails_env] == 'production'

    # launch websocket-rails
    execute "rake websocket_rails:start_server" do
      cwd current_release
      command "bundle exec rake websocket_rails:start_server"
      environment "RAILS_ENV" => deploy[:rails_env]
    end
  end
end

ただ,これだけだとデプロイ時に前のタスクが残ってしまうので,デプロイ前にkillしなければならない.


というわけで,before_migrateにこんなのを追加

execute "rake websocket_rails:stop_server" do
  cwd "/srv/www/langmatchcom/current/"
  command <<-EOH
    if ps aux | grep websocket | grep -v grep; then
        bundle exec rake websocket_rails:stop_server
    fi
  EOH
  environment "RAILS_ENV" => deploy[:rails_env]
end

割りと強引にkillします.



通った!!!

長い戦いでした.


そんなわけで,無駄にこだわってwebsocket-railsAWSで動かしてみたのですが,そんなサービスは今も稼働していて安心しています.

おつかれ.