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が設定される.
これがホスト名とポートになる.
AWSのVPC内になるので,ここはあまりポート番号を気にせずアクセスできるかと思いますが,多分基本的に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-railsをAWSで動かしてみたのですが,そんなサービスは今も稼働していて安心しています.
おつかれ.