Rails5のActionCableをCapistrano経由でデプロイ


Rails 5の目玉機能の一つに、WebSocketsを容易に利用できる[ActionCable](https://github.com/rails/rails/tree/master/actioncable)があります。Rails 4でもgemで提供されてましたが、Rails 5で本体にマージされました。しかし本番環境の情報が少なかったので、記事にまとめました。ActionCableを使ったRails 5アプリは、以下の記事がわかりやすいです。

* [Rails 5 + ActionCableで作る！シンプルなチャットアプリ（DHH氏のデモ動画より）](http://qiita.com/jnchito/items/aec75fab42804287d71b)

この例ではdevelopment環境およびHerokuでは動作しますが、この記事は本番環境のnginxを自前で設定してゆきます。

<i class="fa fa-server"></i> 動作環境と構成
--------------

フロントのWebサーバにはnginxを、Rackサーバには[Puma](http://puma.io/)を使い、Capistrano3でデプロイします。UnicornをRackサーバに使うと、WebSocketsサーバも別に作成する必要があるので、今回はPumaを使います。Pumaとnginxの処理は `puma.sock` 経由で行います。デプロイおよびPumaの制御にはCapistranoを使います。サービスとして起動するには、[jungle](https://github.com/puma/puma/tree/master/tools/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](https://github.com/rails/rails/tree/master/actioncable#allowed-request-origins)）。

```ruby:config/environments/production.rb
config.action_cable.allowed_request_origins = [ 'https://your-domain.example.com' ]
```

Capistranoの設定とデプロイ
--------------------------

[capistrano-puma](https://github.com/seuros/capistrano-puma)というgemを使うと、サーバ側のpumaを手元から制御できます。`Gemfile` に `capistrano3-puma` （`capistrano-puma` は別のgemないので注意！）を追加して、`Capfile` に設定します。

```ruby:Gemfile
gem 'capistrano3-puma' , group: :development
```
```ruby:Capfile
require 'capistrano/puma'
```

以上でCapistrano経由で本番環境のpumaを制御できます。

```
(bundle exec) cap production puma:start
```

この時 `puma:config` タスクが走り、pumaの設定ファイルがデプロイ先の `"#{shared_path}/puma.rb"` に作成されます。pumaの設定ファイルは `config/deploy.rb` を元に生成されます。設定項目は[capistrano-puma](https://github.com/seuros/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バージョンとヘッダを書き換えて同じソケットに渡します。

```nginx:nginx.conf
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";
    }
  }
}
```

<i class="fa fa-question-circle"></i> 繋がらないときは
--------
検証用に[cURLでWebSockets通信](http://hwmemo01.tumblr.com/post/24658856934/一発芸curlでwebsocket受信専用)もできますが、[wscat](https://www.npmjs.com/package/wscat)が便利です。

```sh
wscat -c ws://your-domain.example.com/cable    # or wss://... in SSL version
```

設定ができてなければエラーが帰ってきて、反応が無いときは正常に通信できてる場合が多いです。正しく通信できないときはログとにらめっこしましょう。

<i class="fa fa-book"></i> 参考
----
* [Rails 5 + ActionCableで作る！シンプルなチャットアプリ（DHH氏のデモ動画より）](http://qiita.com/jnchito/items/aec75fab42804287d71b)
* [nginx(1.3.13)でWebSocketのプロキシを試した](http://qiita.com/tomo_watanabe/items/484aa9bfb7bc4206ffed)
* [Vagrantでサーバー立ててActionCableを試してる時にRequest origin not allowedがでて辛い](http://qiita.com/togana/items/bc75aa9d0bd1f7de357b)
* [Rails4系でのActionCableの使い方 - 珈琲駆動開発](http://totutotu.hatenablog.com/entry/2015/12/14/141539)
* [Action Cable - GitHub](https://github.com/rails/rails/tree/master/actioncable)