AWSでロードバランサー使用時のwss://xxxyyy.com/ handshake error 404対応

#やりたいこと
Rails5(APIモード)のアプリケーションで、Actioncableを使って、websocket通信によるリアルタイムチャットを実装。そんで、それをAWSのBeanstalkで作った環境にデプロイしたい。（ロードバランサー使用）


#結論
（１）websocketを使う場合はClassic Load BalancerではなくApplication Load Balancerを使うこと

（２）nginxの設定ファイルにwebsocketの設定を追記すること(そんな難しくない)

（３）本番もredisじゃなくてasyncでいけた。


※以下、
Classic Load Balancer = CLB
Application Load Balancer = ALB
とする

※Railsで実装した場合の例なので悪しからず。


##(1)CLBではなくALBを使う

CLBはaction cableで使うwebsocketに対応していないため、
普通にやると404になってしまう。

※それ用の対応をすればええみたいやけど、
そもそもwebsocketに公式で対応してるALBを使った方がよさそう。

ALBについての説明記事(ALBがwebsocketに対応したって書いてる)
https://dev.classmethod.jp/cloud/aws/alb-application-load-balancer/

CLBで設定する場合の記事
https://qiita.com/tarom/items/0ae12fa3876f5b5d18e9


現状既にCLBで構築してしまっている場合は簡単に移行できるウィザードっぽいのができたみたいなのでそれに従って移行すればOK

CLB→ALB以降のやり方に関する記事
https://dev.classmethod.jp/cloud/aws/migrate-from-clb-to-alb-nlb-wizard/


既にCLBで作っちゃってたので上のやり方でALBに移行した。

※ALBを新しく作ったらDNSをCLBの方からALBに切り替えるのを忘れないこと

##(2)nginxの設定ファイルへ追記

nginxの設定ファイルに下記を追記する。
※action cableで使用している```/cable```パスに対してのみ、ヘッダの書き換え等が必要

***！！！設定ファイル書き換え後にnginxを再起動する必要があるので忘れないように！！！***


```sh:/etc/nginx/conf.d/XXXXXX.conf
location /cable {
        proxy_pass http://[ロードバランサーのURL※後述]/cable;
        proxy_http_version 1.1;
        proxy_set_header Upgrade websocket;
        proxy_set_header Connection Upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
```

nginxの設定ファイルに関してはここ↓を参考にした。
https://qiita.com/maggam/items/5259543569093689d6e4


###設定ファイルに関する補足（個人メモ）

・[ロードバランサーのURL※後述]の箇所は、確かにロードバランサーのURLやけど、upstreamでAPサーバに流す設定になっている場合は（というか大体そうなってるはずやけど）、upstreamで設定した値を入れること

以下の場合でいうと、

```sh:/etc/nginx/conf.d/どこかの.conf
upstream yyy {
  server unix:///var/path/to/yoursock/aaaaa.sock;
}
```

以下のような記述となる。

```sh:/etc/nginx/conf.d/XXXXXX.conf
proxy_pass http://yyy;
```


・ロードバランサーを使用している状態でwebsocket通信を行うためには、ヘッダに特定の情報を追加しないとダメなのでproxy_set_headerで必要なヘッダを追加している

・nginxは本来は ```/etc/nginx/nginx.conf ```の設定ファイルを見ているが、nginx.conf内で下記の記述があった場合は```/etc/nginx/conf.d ```の中にある.confファイルもモジュールとして参照しているので注意。

```sh
include /etc/nginx/default.d/*.conf;
```
なので、nginxの設定ファイルに追記というのは```/etc/nginx/conf.d ```の中にモジュールとしてファイルを新たに作って入れてもいいし、既にある.confファイルに追記しても良い。

ただ、Beanstalkを使ってると環境再構築とかした時にnginxの設定ファイルも(多分）初期化されてしまうので、```.ebextensions```などのように設定をドキュメント化・コード化してデプロイ時に自動設定する方が便利やしうっかりミスもなくなる。

欲を言えば、CloudFormation（AWS）やTerraform(OSS)などを使えば
もっと広くインフラをコード化できて便利なので、その辺についても今度調べようと思う。


↓そもそもnginxの設定ファイルとかがよく分からんという場合はここが参考になる
https://qiita.com/syou007/items/3e2d410bbe65a364b603


##(3)本番もredisじゃなくてasyncでいけた
Actioncableで使用するサブスクリプションアダプタはデフォルトでは開発/テストがasyncで、本番はredisとなっているが、本番もasyncで動きそう。

※下記のRailsガイドにもあるように本番でもasyncを使用するのは非推奨なので注意
https://railsguides.jp/action_cable_overview.html

その際のサブスクリプションアダプタの設定方法は下記の通り

```rb:/yourapp_root/config/cable.yml
development:
  adapter: async
 
test:
  adapter: async
 
production:
  #adapter: redis
  #url: redis://10.10.3.153:6381
  #channel_prefix: appname_production
  adapter: async
```

アクションケーブルでのチャットの実装方法や、cable.ymlの内容を参考にしたのはこの記事
https://qiita.com/Hijiri-K/items/c3774c72a2cb68e1a720


##safariだけpost時にクロスオリジンエラーになる問題

ロードバランサーをCLBからALBに変える際、公式の移行ウィジェットを使うんじゃなくて、自分で最初からALBを使う環境でBeanstalkを使って別に環境を作ったら、websocketはいけてるけど、safariでpostが通らなくなる問題が起きた。（クロスオリジンエラー）

chromeは普通に動くのになんでなんやろ。

調べて見たけどよく分からんかった〜。

しかも、postと言っても特定のpostだけがあかんくて、
自分の場合は、devise_token_authで使ってるpostがこけてた。
（しかもログインはできるけど登録はできないって感じ）

もちろんcors対策はしてるし現にchromeでは問題なく動いてたのになんでなんやろか。

corsの問題じゃないんかな？
また時間ある時に調べてみよう。


##終わりに

そもそも、ロードバランサーが原因とは全く思っておらず、
rails actioncable websocket 404 handshake 等でググりまくってたら
stackoverflowでCLBやと動かんみたいなことが書いてあって

それで、ロードバランサーの問題なのか！と気づいた。

最初からロードバランサーで調べてたら日本語情報もあったけど
エラー内容だけで探してたら英語情報しか見当たらなかった。（気がする）

英語の記事全スルーとかにしてる人やったら詰んでたんちゃうかな。

別に英語が得意な訳じゃないけど
マジで情報源として英語は大切だなと再認した。






※記事の内容で理解が間違っているところや、もっと効率的にできるところがあればコメントで教えてもらえると幸いです。