Running Faye server on AWS ElasticBeanstalk with Load Balancer
One of the amazing features you want to build in your application is the ability to deliver content to users in real-time without requiring them to refresh the browsers. You can achieve this by sending requests in fixed interval to the server to get changes or you can have a dedicated process on server to push updates down to the client browsers. I love the second solution because it saves your server resources and can improve your app performance (of course, it has drawback). And when talking about real-time notification in Rails application, one popular choice is Faye server . And there is a gem called private_pub which acts as a wrapper for setting up everything in your Rails app very easily.
Normally, the steps to set up private_pub is pretty easy and straightforward. Your Rails application and Faye server will run as two separate processes on the server at different ports. Browsers will listen to Faye server for changes (using websocket or long polling). And Rails app will post message to this Faye server to notify clients.
If your app runs on a VPS, you should be able to set up everything easily. After deployment, you just need to run necessary command to start up Faye server and then everything will be working as you expected. The problem arises when you want to deploy your Rails application on ElasticBeanstalk with Load Balancer and having Faye server running on it.
Why is it a problem?
Imagine that your Rails application is running on this domain: example.com
. And you expect Faye server to be running at this url: example.com:9292
. There are two issues you might see:
-
If you use EB with LoadBalancer, users will not connect directly to your EC2 instances, but instead, they will connect to the load balancer, and this balancer will then route the traffic to corresponding EC2 instances. By default, the load balancer will only accept connection to port 80 and 443, all connection to other ports will be dropped. It means that if you request for resources at
example.com:9292
, you would get an error -
Each time you deploy your application to Elastic Beanstalk, old code will be removed and replaced with new code. And you would want to automatically start faye process after each deployment without manually SSH into the server to do that
Solution
In order to solve the first issue, we need to have a bit knowledge about security group on AWS. Nearly every services on AWS is related to security group. It is a way for you to define what permission you can have on a specific service. For example, security group on an EC2 instance allows you to control which port is open and who can connect to that EC2 instance
First of all, you have to know what security group is being used in your EC2 instances. Click on a specific EC2 and you would see the security group in the bottom panel
Next, you will modify the permission on that security group. Click on the security group name from above, you will see the following screen
Notice that there are 4 tabs in bottom panel, Incoming tab is used to define which ports will be open to the outside world and it is our main focus here. As we want the Load Balancer to be able to connect to this instance via 9292 port, we will define a new entry like this
Type: Custom TCP Rule
Protocol: TCP
Port Range: 9292
Source: Anywhere (0.0.0.0/0)
After creating this, if you browse to the IP address of your EC2 instance with port 9292, you would see Faye server on browser (assuming that Faye server process has already been started on that instance). However, if you browse to this port through your custom domain, this still does not work, further work needs to be done.
In addition to the security group on EC2 instance, you will also need to change the listeners of the EB Load Balancer. You can accomplish this by creating a new listener on Load Balancer. If you click on a Load Balancer, you will see the following section at the bottom panel.
You can then add the new entry to forward requests coming to 9292 port of you load balancer to port 9292 of the EC2 instances. For example, I created the following entry:
Load Balancer Protocol: TCP
Load Balancer Port: 9292
Instance Protocol: TCP
Instance Port: 9292
Cipher: N/A
SSL Certificate: N/A
The above config tells your Load balancer that any requests coming to port 9292 will be forwarded to port 9292 of the EC2 instances.
And that's it, you would now be able to browse to example.com:9292
and see the message from Faye
Sure you're not looking for /faye ?
Regarding the second issue, this can be easily fixed by defining a config inside .ebextensions in your Rails app. You can read more at this post to understand how to config other services. The faye.config on my app will be like this
commands:
create_post_dir:
command: "mkdir /opt/elasticbeanstalk/hooks/appdeploy/post"
ignoreErrors: true
files:
"/opt/elasticbeanstalk/hooks/appdeploy/post/100_start_faye_server.sh":
mode: "000755"
owner: root
group: root
content: |
#!/usr/bin/env bash
. /opt/elasticbeanstalk/support/envvars
cd $EB_CONFIG_APP_CURRENT
su -c "RAILS_ENV=production bundle exec thin -p 9292 -e production -R private_pub.ru --timeout 60 start -d" $EB_CONFIG_APP_USER
And that's it. I understand that it is not easy and straightforward for us to configure custom service on ElasticBeanstalk. And I hope this post will help you feel happy with this service. If you have any further question, feel free to let me know.