How To Configure Varnish As Load Balancer For Meteor

Varnish supports sticky session and websockets, so it can be configured as a load balancer for Meteor  apps.
Here’s the sample Varnish configuration to distribute both web page requests and REST API requests to two Meteor servers.
Meteor Server #1:
Web Server: http://192.168.1.1:3000/
REST API: http://192.168.1.1:3000/api/
 Meteor Server #2:
Web Server: http://192.168.1.2:3000/
REST API: http://192.168.1.2:3000/api/
You shall have noticed we have dual-purpose Meteor server here – the web server and REST API are actually pointed to the same Meteor instance.  Requests made to /api get routed at Meteor server side by using Meteor.Router or RestStop2.
Full content of Varnish configuration file /etc/varnish/default.vcl:
backend api_svr1 {
  .host = "192.168.1.1";
  .port = "3000";
  .probe = {
      /* You can change to use your own light weight request for probing */
      .url = "/api/version";
      .interval = 5s;
      .timeout = 5s;
      .window = 5;
      .threshold = 3;
   }
}

backend api_svr2 {
  .host = "192.168.1.2";
  .port = "3000";
  .probe = {
      /* You can change to use your own light weight request for probing */
      .url = "/api/version";
      .interval = 5s;
      .timeout = 5s;
      .window = 5;
      .threshold = 3;
   }
}

backend web_svr1 {
  .host = "192.168.1.1";
  .port = "3000";
  .probe = {
      .url = "/";
      .interval = 5s;
      .timeout = 5s;
      .window = 5;
      .threshold = 3;
   }
}

backend web_svr2 {
  .host = "192.168.1.2";
  .port = "3000";
  .probe = {
      .url = "/";
      .interval = 5s;
      .timeout = 5s;
      .window = 5;
      .threshold = 3;
   }
}

director api_be client {
        {
                .backend = api_svr1;
                .weight = 1;
        }
        {
                .backend = api_svr2;
                .weight = 1;
        }
}

director web_be client {
        {
                .backend = web_svr1;
                .weight = 1;
        }
        {
                .backend = web_svr2;
                .weight = 1;
        }       
}

#pipe for websocket
sub vcl_pipe {
     if (req.http.upgrade) {
         set bereq.http.upgrade = req.http.upgrade;
     }
}

sub vcl_recv {
   if (req.http.Upgrade ~ "(?i)websocket") {
      set req.backend = web_be;
      return (pipe);
   }

   /* Default load balance by client IP */
   set client.identity = client.ip;

   if (req.url ~ "^/api") {
        /* dispatch /api to api backend */
        /* you can even use other balance strategy other than client ip, */
        /* e.g by request url path */
        set client.identity = req.url;
        set req.backend = api_be;
    } else {
        /* dispatch web request to web backend. */
        /* must use sticky balance strategy, e.g. by default client ip */
        set req.backend = web_be;
        return (pipe);
    }
}

Comments