Skip to main content

19 How to Deploy ActionCable and Rails 5 To Production

Episode 103 · February 4, 2016

Learn how to deploy ActionCable and Rails 5 with Passenger

ActionCable Rails 5.0 Deployment Production


Resources

# If you don't have redis server already installed, 
# make sure to install that if you choose to use that as the backend
sudo apt-get install redis-server
# config/cable.yml
production:
  url: redis://localhost:6379

local: &local
  url: redis://localhost:6379

development: *local
test: *local
# config/routes.rb
mount ActionCable.server => "/cable"
# app/assets/javascripts/channels/index.coffee
App.cable = ActionCable.createConsumer("/cable")
# config/environments/production
config.action_cable.allowed_request_origins = ["https://gorails.com"]
# /etc/nginx/sites-enabled/default
server {
  listen 80;
  server_name gorails.com;

  passenger_enabled on;
  rails_env production;
  root /home/deploy/gorails/current/public;

  location /cable {
    passenger_app_group_name gorails_websocket;
    passenger_force_max_concurrent_requests_per_process 0;
  }

  error_page 500 502 503 504 /50x.html;
  location = /50x.html {
    root html;
  }
}

Transcripts

This episode we're going to talk about deploying ActionCable to production, and we're going to be using Passenger, and if you don't have a server already, you can follow the GoRails guide for deploying Ruby on Rails on Ubuntu. I actually have a virtual box server set up here, and I just follow this guide, and we're going to skip the Capistrano set up, but we're going to just clone a Git repository into this server, and we're going to test it locally as if it were a real production server on DigitalOcean or AWS or something. Passenger 5.0.24 is the minimum version you're going to want to use with ActionCable. I tried using Passenger 5 a couple weeks back, and we ran into some issues with WebSockets, and ActionCable, and that's all getting improved and fixed. Infusion is working very hard to make sure that this is all up to date and working well with the latest rails versions, and speaking of the latest rails versions, rails 5 beta 2 came out, which removes the redis and event machine dependencies, which is really great, so this is going to be a lot lighter weight of an installation by default, and you won't even need redis by default because if you're using Postgres, you can take advantage of the built in pub sub functionality, but if you are using redis for something like Sidekiq or rescue or whatever, you'll be able to continue to use redis for that, but you don't have to, so that's pretty nifty and will allow you to do easier set ups and everything with ActionCable. Passenger 5, whatever I tested originally had some bugs that I have gotten fixed in 5.0.24, so this is going to be what we're using going forward. If you've already deployed a server using this tutorial, or you're already running Passenger, you're basically wanting to go through and just update that package. If you've installed this on Ubuntu with the aptitude repository, you can just simply say: apt-get update && sudo apt-get upgrade This will go download a list of all the latest software, and it will look to upgrade any of the software you have on your Ubuntu server. Really that's just going to say: Ok, there's a new version of passenger, let's update that. There may be a new version of like NginX or MySQL or PostgreSQL, so we'll go ahead and update those things. Mainly you're just looking to make sure that you get the latest version of Passenger, and I've already done that, but this server is running following that tutorial, so everything is up to date as if you were to do that completely fresh. Now, there's the github.com/rails/actioncable examples repository that we're going to use here, and basically yesterday DHH merged a pull request and that pulled in rails 5 beta 2, so this is no longer using rails from master, and we'll have the ActionCable dependency in and everything, so this is going to be basically what you want to use to run in production. I removed the ActionCable process log in which got changed and basically updated this to the basics that you're going to need. Let's clone this into our server, and then deploy this rails app as a production rails app running on passenger. We'll do that in our virtual machine. On our server we're going to need to have that rails application. Now, normally you would use Capistrano to deploy that to your server, we're going to cheat and just git clone directly that repository. Normally if you're using Capistrano, you would have like the directory here, but if you go into the directory you would see like a current directory which would be where the real application sits. We're skipping that because of not using Capistrano, but you'll just change the folder names when you set up your NginX stuff, and that will be the only difference to do this in production. We have our application here, and there's a couple things that we want to make sure that we set. For one, if we open up, app/assets/javascrips/channels/index.coffee, this app/cable is going to create a JavaScript web socket consumer, so it's going to be the thing that connects to the web socket server side. Now, we actually want to mount this, and in the /cable route.

The reason why we're going to do this is because it's going to simplify it, and you'll be using web sockets through your rails app basically without having to use a separate port on your machine, so we'll go over to port 80 and go to this url and then it will be handed off to ActionCable without going into your full rails application, so in order to set that up correctly, you'll save that file, and then you'll make sure that you have that change and then you'll also go into your config/routes.rb file, and here at the top you'll say mount ActionCable.server => "cable" this is really just mounting the ActionCable server to that and setting that connection up, and you can read more about this if you take a look at the rails ActionCable, because it is in a rails directory where the documentation in the REDME is. If you come to the README here, you'll be able to see a lot of examples about it, but you can either point to like a sub domain, where all of your ActionCable servers would be running. You can also point it to a port so you could handle that on a separate port, or you could use the /cable, and that would allow you to use the route, and thats probably going to be the easiest way. Now, one of this things that you're going to notice is that if you're using a domain like this or a port, you're going to probably have different ports or domains in development, so there's an ActionCable meta tag that you can just print out into the head of your application layout. You can print this out here, if you like, and then you can set those ActionCable urls in your config environments development and production.rb files, and then this will automatically just print out that, and then you would use createConsumer without any arguments, so this would be empty and that would automatically check for this meta tag and then set that up, so you would have this in your index.coffee in the channels JavaScript file. That's pretty straight forward, the way we're going to do it is going to be using this url, which will be relative to the current server, so it won't matter. You won't have to do any of this setup because it will always access that, which will be the simplest way. The other thing that you're going to want to do is set your ActionCable.server.config.allowed_request_origins, otherwise, when you open up in the browser, you're going to get a failure that the web socket couldn't connect, and in your rails logs that will say it wasn't an allowed request origin. In development, of course, this is localhost:3000, but because we're using a separate server here, we're actually going to need to set the allowed request origin, and I'll show you an example of the error that you'll get, how to see that in your logs and then how to go fix that. I won't put that in just yet, but we'll go ahead and take a look at that briefly. If you search down here to the running the cable server section, you can either do it standalone, where it runs with Puma on it's own port, or you can mount the ActionCable server in your rails urls, so that basically runs alongside your rails application, and then gets separated out, which is an easier way of handling that. We now have both of those things set up, and if you run your rails server, that's all you're going to need to do, but we've got passenger installed, so we're not using this in development, so we won't be running rails server to test this out.

We'll actually be running and updating our config for NginX. Let's edit with Vim /etc/nginx/sites-enables/default, and basically, that says: Let's just create our passenger application, we're going to use the environment that we would like, so normally this will be a production. Let's just change that back. Let's change that back to development, so that we don't have to compile our assets. If you're using Capistrano, you can have it already compile your assets, and you won't have to worry about that, and then you set your root to the public directory just like you would do with the normal Passenger set up. The one piece that you're going to need to change for ActionCable is this location, so you know how we just said there was that /cable section. We're going to be creating that here in our NginX config, and that's just going to tell passenger that this is a web socket location, so we'll say location /cable, and then we'll also type in the same passenger app group name and passenger force max concurrent requests per process to zero, which basically means unlimited, so that can have an unlimited number of concurrent requests, allowing you to support a whole lot more web sockets. Let's type that in, we'll move this down and we'll say passenger_app_group_name actioncable_example_websocket:, passenger_force_max_concurrent_request_per_process 0; That's the only difference that you're going to need to do in order to use passenger with ActionCable. There's about those three changes, and once you have done that, you're going to need to sudo service nginx restart, and that will restart NginX, and you'll see an OK over there in the right when that has finished, now that NginX and passenger are up and running and configured, if we type in the IP address of our server, or the domain if you're doing this on a production server, then you should get the rails application, and we do, so clicking on this should be able to go into our channel, and we'll be able to open up the JavaScript console to see if your web sockets are working, and here you can see that it's checking for web socket on that IP address / cable, so that's a /cable that we set up on the JavaScript side, but you're noticing that, there's warnings here, and it's saying that if failed, and it closed before the connection is established. Now, what is that? if you see this, chances are, what you're going to get, if you open up your log and take a look at that, you're going to see that the request origin was not allowed there at the bottom, so I can't show you this with my mouse to highlight that line, the second from the last line says "request origin not allowed", and that is because with web sockets, you actually need to say that: Ok, this is an allowed domain to talk on those web sockets. That is what I mentioned earlier in the request_origins, and really you're just going to want to put this inside your rails app. By default this defaults to localhost:3000, of course.

Then, for development, if you used an IP address that's not localhost:3000, or like your own little domain in development, you'll have to change that, and we're just going to take this, and we're going to go edit our application, so let's open up config/environments/development.rb. In your example, you should do this in production, but because we're using development here, we'll use that. I've actually run into a bug, I believe that when you use the exact line that they give you, it doesn't actually set this, at least in development, so when I did this, it actually didn't take and all it would do was set it to localhost:3000. What you can do instead, and that likely will get fixed, and then what you can do instead is set config.action_cable.allowed_request_origins and set your urls there. Now, this is probably going to be the one that I would recommend the most, for that, and then this, because we're using the urls and we set that in the config, we can change this to keep it consistent to /cable, but this isn't being used, because we're not using that actioncable meta tag, so that we can change here, but we're going to importantly set this allowed request origins to the http protocol, or https, and then the IP address or the domain that you're going to be accessing this through. Once you have that set, if you need to test this out and make sure that that's working correctly, just open up your rails console and say actioncable.server.config, and at the very bottom you'll see the allowed request origins as the last one, and that's set to the array that I set before, and if this doesn't take, you'll notice that that allowed request origins defaults to localhost:3000. You'll probably want to check that until that is correctly set up, and then you can just touch tmp/restart.txt to restart the rails application with passenger, and then refreshing this page should take a little bit longer this first time as it reloads everything, and then if you let this sit for a little bit, you should see that there's no more errors, but there is this time, so let's do some debugging. Register connection, actually that seems to have worked. This first request did not, but it looks like it has retried, so it does look like it has worked. What we can do is we can open up the incognito version, and then log in as a different user and going to the same channel, and we should see in this user that there are no web socket errors, and we'll leave that open so you can see that just in case."Send another test", and so, you can see that this automatically got posted and automatically showed up on the other window.

Now, if you want to test this out even more, you can open up like Safari and FireFox and both of their private browsinng modes, and then you can see that you will be able to access your server with all of those. All of them will have web socket connections, and then everything will get real time updating, which is super duper cool. There is likely lots of little bugs and things that will change, like we ran into that one with the request origin. All of those things will probably get a lot more polished as the release candidates come out and any more betas, and then the final version which should happen in the next few weeks. That's really all you have to do. The important piece here being that your /etc/nginx/sites-enabled/default really just needs this location chunk here for the /cable part, and that about wraps it up for this episode, deploying in ActionCable is actually super duper easy if you're using passenger, I expect there to be a lot more improvements and fixes for passenger. Once rails 5 gets out of beta, and starts to get into release candidates in the final versions, you'll probably see more updates, so keep an eye on those, rails 5 updates as well, and if you deploy this to production and get something going, definitely include a link into your app in the comments below, I'd love to see those, and until then, happy hacking.

Discussion