Rails 5の目玉機能の一つに、WebSocketsを容易に利用できるActionCableがあります。Rails 4でもgemで提供されてましたが、Rails 5で本体にマージされました。しかし本番環境の情報が少なかったので、記事にまとめました。ActionCableを使ったRails 5アプリは、以下の記事がわかりやすいです。
この例ではdevelopment環境およびHerokuでは動作しますが、この記事は本番環境のnginxを自前で設定してゆきます。
動作環境と構成
フロントのWebサーバにはnginxを、RackサーバにはPumaを使い、Capistrano3でデプロイします。UnicornをRackサーバに使うと、WebSocketsサーバも別に作成する必要があるので、今回はPumaを使います。Pumaとnginxの処理は puma.sock
経由で行います。デプロイおよびPumaの制御にはCapistranoを使います。サービスとして起動するには、jungleまたはsystemdの設定を自前で用意すれば良さそうですが、ここでは触れません。
ActionCableのデフォルトに従い/cable
をWebSocketsのパスに、つまりws://your-domain.example.com/cable
(またはwss://your-domain.example.com/cable
)からWebSocketsに接続します。
Railsアプリの設定
まずRailsアプリ側で、ActionCableが許可するホストを設定します。nginxとRailsアプリが同サーバ上でも、サーバのホストを許可する必要があります(Allowed Request Origins)。
config.action_cable.allowed_request_origins = [ 'https://your-domain.example.com' ]
Capistranoの設定とデプロイ
capistrano-pumaというgemを使うと、サーバ側のpumaを手元から制御できます。Gemfile
に capistrano3-puma
(capistrano-puma
は別のgemないので注意!)を追加して、Capfile
に設定します。
gem 'capistrano3-puma' , group: :development
require 'capistrano/puma'
以上でCapistrano経由で本番環境のpumaを制御できます。
(bundle exec) cap production puma:start
この時 puma:config
タスクが走り、pumaの設定ファイルがデプロイ先の "#{shared_path}/puma.rb"
に作成されます。pumaの設定ファイルは config/deploy.rb
を元に生成されます。設定項目はcapistrano-pumaのREADMEにあります。デフォルトでは、pumaのsockファイルが "#{shared_path}/tmp/sockets/puma.sock"
に配置されます。つまり puma:start
を走らせると"#{shared_path}/tmp/sockets/puma.sock"
越しにRailsアプリにアクセスできるので、nginxはこのファイルを見に行くよう設定します。
nginxの設定
通常のHTTPリクエストは、Unicorn同様upstreamにソケットを指定します。WebSocketsに対するリクエスト(/cable
)はHTTPバージョンとヘッダを書き換えて同じソケットに渡します。
http {
upstream puma {
server unix:/path-to-your-project/tmp/sockets/puma.sock;
}
server {
# General web access
try_files $uri $uri/index.html $uri.html @webapp;
location @webapp {
root /path-to-your-project/public/;
proxy_pass http://puma;
}
# WebSocket configure
location /cable {
proxy_pass http://puma;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
}
繋がらないときは
検証用にcURLでWebSockets通信もできますが、wscatが便利です。
wscat -c ws://your-domain.example.com/cable # or wss://... in SSL version
設定ができてなければエラーが帰ってきて、反応が無いときは正常に通信できてる場合が多いです。正しく通信できないときはログとにらめっこしましょう。