部署 在 Staging 环境中部署 ActionCable 遇到的坑

a0nqm · March 14, 2017 · Last by a0nqm replied at November 27, 2017 · 4420 hits

现在公司项目里加入了 ActionCable 组件,在本地环境中用 async 的方法能够正常启动;后来我在 Staging 环境中测试总是失败,刚刚解决了这个问题,因此发篇帖子记录一下。自己对其中内容有不理解的地方,社区各位大神也可以帮忙解答一下。 接下来,等功能开发完毕,会在配有 TLS 的正式服务上部署,如果有新的坑我会来更新。

Rails 中的最基本配置

在 Rails 中配置 ActionCable 并不难,只需要配置三部分:cable.ymlstaging.rbroutes.rb

# config/cable.yml

development:
  adapter: async

test:
  adapter: async

production:
  adapter: redis
  url: redis://localhost:6379/3

staging:
  adapter: redis
  url: redis://localhost:6379/3

# config/environments/staging.rb
# ...
config.action_cable.url = 'ws://staging.xxx.cn/cable'
config.action_cable.allowed_request_origins = [ 'http://staging.xxx.cn' ]
# ...
# config/routes.rb
# ...
mount ActionCable.server => '/cable'
# ...

如果在 development 环境中,仅仅将adapter配置为async即可在本机上完成 ActionCable 的基本配置。

Staging 环境中的配置

仅凭以上配置,在配置好 Nginx 和 Puma 的 Staging 环境中部署,会遇到一个 Error:

ERROR -- : Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)

页面中 Console 里的表现是/cable报 404。

查过很多方法之后,最终在网站的 nginx 配置文件里加上以下内容,问题得到解决:

location /cable {
    proxy_pass http://xxx;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-Proto http;
    proxy_set_header Host $http_host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header X-Real-IP $remote_addr;
}

本机 development 环境能配置接入 redis 服务器么?

目前尝试的结果是:不能

本机开发环境不配置 Nginx,所有请求都走 Rack,因此理论上需要在config/development.rb中写出与以上 Nginx 配置作用相同的 Rack 配置,实践上我还没写出来,然而除非做研究学习否则也没有必要这样做。(如果有人感兴趣,可以尝试)

就先分享这么多。

本机 development 环境能配置接入 redis 服务器么?可以啊,和其他环境一样配置啊

这就是 docker 的好处,没有开发环境,生产环境的区别

Reply to mengqing

上面那段 nginx 的配置在 development 环境中如何体现?

Reply to nouse

docker 也有 docker 的成本,业务没那么复杂的时候,Docker 有点大材小用。

你 staging 环境用负载均衡了么?

Reply to a0nqm

我不明白如何定义“大材小用”?为了保证环境一致到底算大用还是小用?最新的 ruby:2.4-alpine 镜像只要 60MB,而 phusion 官方镜像也“只有”653MB。

Reply to nouse

是我没讲清楚。 我目前不用 Docker 是有原因的:

  1. 微服务的拆分与持续维护的成本,在我看来是不可估量的。
  2. 每次 Build image,会在 image 内部重新 bundle install,就有可能引起依赖软件的版本变化,就有可能引入未知的新的 bug,且排查成本较高。而这种 bug 在常规开发/部署的方式中几乎不存在,即使有也能很快定位到问题。
  3. 在企业中不能脱离人员架构讲技术。我可以自己用起来 Docker,但总有人不是那么熟练;不论是培训、技术转型还是后期维护都会有成本,这种成本现阶段是完全不必要的。
Reply to Kungs

没有,只是用 Puma 起了多线程。

Reply to a0nqm
  1. 微服务化不是必须的,集成应用也可以
  2. Dockerfile 写对了是有缓存的
  3. 赞成
  4. 容器部署的难点是调度工具,需要提升运维能力或者直接用基于容器部署的云(最好的在国内不可用)
Reply to Rei

嗯那就是我道行还不够深,先把 Dockerfile 写对了,再考虑用进去。

容器部署的调度工具我也是昨天才想到这个问题,脑子里转了一圈发现周围没什么好用的。至于提升运维能力,这得看公司发展,我自己心很大,但这不是我一厢情愿就能做成的事情。

研究清楚问题细节也是对的,不然用容器出问题也不知道问题在哪。

Reply to Rei

我先去参考一下 Homeland 的 Dockerfile。

Reply to a0nqm

刚看了一下,也没写对。最佳实践是把不变的东西写在前面,然后就会利用缓存:

WORKDIR /app
ADD Gemfile /app
ADD Gemfile.lock /app
RUN bundle install
ADD . /app

如果 Gemfile 不变,Bundle 是缓存的。

我看这里学的

Reply to Rei_mk2

感谢!

我之前写的确实不对,这次受教了。

阿里云负载均衡不支持 ws 和 wss 协议,也会出现 Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket) 这个问题。 如果是用阿里云负载均衡了,出现这个问题,去掉负载均衡就好了

Reply to qwlong

感谢提醒。不过我服……

You need to Sign in before reply, if you don't have an account, please Sign up first.