0

I've added some features to a couple of our web apps that needs websocket-rails. Everything works fine in development, but I am not sure how to deploy all this in our production environment since it's a bit more complex.

The production setup:

  • 1 server used as a Load balancer (Nginx).
  • 2 servers used as web servers, where our rails apps run using Nginx and Passenger (both servers are identical).
  • Several other servers used by the app servers but I believe they are irrelevant for this question.
  • All sites are running on HTTPS.

Load balancer configs

Here's an example for one of the sites, the others have similar configs:

upstream example {
    ip_hash;
    server xx.xx.xx.xx:443;
    server xx.xx.xx.xx:443;
}

server {
  listen   80;
  listen 443 ssl;

  ssl on;
  ssl_certificate /etc/nginx/ssl/example.chained.crt;
  ssl_certificate_key /etc/nginx/ssl/example.key;

  server_name example.com;
  rewrite ^(.*) https://www.example.com$1 permanent;
}

server {
  listen   80;
  listen 443 ssl;

  ssl on;
  ssl_certificate /etc/nginx/ssl/example.chained.crt;
  ssl_certificate_key /etc/nginx/ssl/example.key;

  server_name www.example.com;
  if ($ssl_protocol = "") {
    rewrite     ^   https://$server_name$request_uri? permanent;
  }

  client_max_body_size 2000M;

  location /css { root /home/myuser/maintenance; }
  location /js { root /home/myuser/maintenance; }
  location /img { root /home/myuser/maintenance; }
  location /fonts { root /home/myuser/maintenance; }

  error_page 502 503 @maintenance;
  location @maintenance {
    root /home/myuser;
    if ($uri !~ ^/maintenance/) {
      rewrite ^(.*)$ /maintenance/example.html break;
    }
  }

  location / {
    proxy_pass https://example;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

Web server configs

Again, here's an example for one of the sites, the others have similar configs:

server {
  server_name example.com;
  rewrite ^(.*) https://www.example.com$1 permanent;
}

server {
  listen   80;
  listen 443 ssl;

  ssl on;
  ssl_certificate /etc/nginx/ssl/example.chained.crt;
  ssl_certificate_key /etc/nginx/ssl/example.key;

  root /var/www/example/public;
  server_name www.example.com;
  if ($ssl_protocol = "") {
    rewrite     ^   https://$server_name$request_uri? permanent;
  }
  client_max_body_size 2000M;
  passenger_enabled on;
  rails_env production;
  passenger_env_var SECRET_KEY_BASE "SOME_SECRET";
}

What I've gathered so far:

My Questions:

  • Do I have to enable the passenger sticky sessions also in the load balancer's configs? I am guessing this is only for the web servers.
  • How would the location section for the websocket server look like?
  • Do I have to create the websocket location section also on the load balancer?
  • Having the sticky sessions is enough to keep the various apps and servers in synch?
  • I have various apps running on each server and they should all receive the same notifications (socket messages) so they should all connect to the same websocket server (I'm guessing). Now that websocket-rails is part of their gemsets, won't each app try to spawn their own websocket server? If so, how do I prevent that and make them spawn only one in case none is running yet?

As you can see I am quite confused about how websocket-rails works with passenger and nginx in production so even if you don't have all the answers, any input is greatly appreciated!

UPDATE

I've tried the following on the load balancer:

upstream websocket {
  server xx.xx.xx.xx:443;
  server xx.xx.xx.xx:443;
}

location /websocket {
    proxy_pass https://websocket;
    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;

    #also tried with this:
    #proxy_set_header Upgrade $http_upgrade;
    #proxy_set_header Connection "upgrade";
}

and on the app servers:

location /websocket {
    proxy_pass https://www.example.com/websocket;
    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;

    #also tried with this:
    #proxy_set_header Upgrade $http_upgrade;
    #proxy_set_header Connection "upgrade";
}

On the client side I connect to the url WebSocketRails('www.example.com/websocket'); and i get the following error:

WebSocket connection to 'wss://www.example.com/websocket' failed: Error during WebSocket handshake: Unexpected response code: 404

Any ideas?

Julien
  • 2,217
  • 2
  • 28
  • 49

1 Answers1

0
  1. I don't think you'll need passenger sticky sessions on the load balancer

  2. This blog covers relevant WebSocket config for NGINX. You need the WebSocket config on the load balancer, and also on the web server if you want to pass the Upgrade and Connection headers to the rails app.

Faisal Memon
  • 1,047
  • 7
  • 7
  • Thanks for the input, please see my update to see what i've got so far. – Julien Jan 25 '17 at 06:32
  • All i could see related to it was in the access log: `"GET /websocket HTTP/1.1" 404 731 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36"` on the load balancer – Julien Jan 25 '17 at 07:51
  • oh i see the same also on the app server – Julien Jan 25 '17 at 07:53
  • It seems like your NGINX config is correct. My guess is something is mis configured on the passenger side. Are there any errors in the passenger logs? – Faisal Memon Jan 25 '17 at 19:35
  • well i can't find any passenger logs but looking at the `production.log` of the rails app on the app servers i don't see there any problems, not even a 404 – Julien Jan 25 '17 at 21:18
  • The 404 error showing up on the app server means the load balancer is proxying the connection properly. The app server and load balancer have identical config, so next logical place to look is passenger or the rails app. I know very little of passenger or rails so unfortunately I can't help you there. – Faisal Memon Jan 26 '17 at 01:59