在 Varnish/Nginx 服务器上部署我的 node.js 应用程序的最佳方式

Sai*_*han 5 nginx varnish node.js

我即将部署一个全新的 node.js 应用程序,我需要一些帮助来设置它。

我现在的设置方式如下。

我有清漆运行 external_ip:80

我有 Nginx 在运行 internal_ip:80

两者都在侦听端口 80,一个内部端口,一个外部端口。

注意:node.js 应用程序在WebSockets 上运行

现在我有了我的新 node.js 应用程序,它将侦听端口 8080。

我可以在 nginx 和 node.js 前面设置清漆吗?

Varnish 必须将 websocket 代理到端口 8080,但是诸如 css、js 等静态文件必须通过端口 80 到达 nignx。

Nginx 不支持开箱即用的 websockets,否则我会这样设置:

清漆 -> nignx -> node.js

cyb*_*x86 10

刚刚设置了一个与您描述的项目基本相同的项目,我将分享我的方法 - 不能保证它是“最好的”,但它确实有效。

我的服务器堆栈是

  • Varnish (v3.0.2) - 所有接口,端口 80
  • Nginx (v1.0.14) - 本地接口,端口 81
  • Node.js (v0.6.13) - 本地接口,端口 1337
  • 操作系统为 CentOS 6.2(或类似)

我的 Node.js 应用程序使用 Websockets (sockets.io - v0.9.0) 和 Express (v2.5.8) - 并使用永久启动。(同一台服务器上还有其他站点——主要是 PHP,它们使用相同的 Nginx 和 Varnish 实例)。

我的方法的基本意图如下:

  • websocket 和“常规”数据的单一公共端口/地址
  • 使用 Varnish 缓存一些资源
  • 直接从 nginx 提供(未缓存的)静态资产
  • 将“网页”请求传递给 nginx,并从它们的代理传递给 Node.js
  • 将 Web 套接字请求直接(从 Varnish)传递到 Node.js(绕过 nginx)。

清漆配置 - /etc/varnish/default.vcl:

#Nginx - on port 81
backend default {
  .host = "127.0.0.1";
  .port = "81";
  .connect_timeout = 5s;
  .first_byte_timeout = 30s;
  .between_bytes_timeout = 60s;
  .max_connections = 800;
}
#Node.js - on port 1337
backend nodejs{
  .host = "127.0.0.1";
  .port = "1337";
  .connect_timeout = 1s;
  .first_byte_timeout = 2s;
  .between_bytes_timeout = 60s;
  .max_connections = 800;
}

sub vcl_recv {
    set req.backend = default;

    #Keeping the IP addresses correct for my logs
    if (req.restarts == 0) {
        if (req.http.x-forwarded-for) {
            set req.http.X-Forwarded-For =
            req.http.X-Forwarded-For + ", " + client.ip;
        } else {
            set req.http.X-Forwarded-For = client.ip;
        }
    }

    #remove port, if included, to normalize host
    set req.http.Host = regsub(req.http.Host, ":[0-9]+", "");

    #Part of the standard Varnish config
    if (req.request != "GET" &&
      req.request != "HEAD" &&
      req.request != "PUT" &&
      req.request != "POST" &&
      req.request != "TRACE" &&
      req.request != "OPTIONS" &&
      req.request != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        return (pipe);
    }
    if (req.request != "GET" && req.request != "HEAD") {
        /* We only deal with GET and HEAD by default */
        return (pass);
    }

    #Taken from the Varnish help on dealing with Websockets - pipe directly to Node.js
    if (req.http.Upgrade ~ "(?i)websocket") {
        set req.backend = nodejs;
        return (pipe);
    }

    ###Removed some cookie manipulation and compression settings##


    if(req.http.Host ~"^(www\.)?example.com"){
            #Removed some redirects and host normalization
            #Requests made to this path, even if XHR polling still benefit from piping - pass does not seem to work
        if (req.url ~ "^/socket.io/") {
            set req.backend = nodejs;
            return (pipe);
        }

    #I have a bunch of other sites which get included here, each in its own block
    }elseif (req.http.Host ~ "^(www\.)?othersite.tld"){
        #...
    }

 #Part of the standard Varnish config
 if (req.http.Authorization || req.http.Cookie) {
        /* Not cacheable by default */
        return (pass);
    }

    #Everything else, lookup
    return (lookup);
}


sub vcl_pipe {
    #Need to copy the upgrade for websockets to work
    if (req.http.upgrade) {
        set bereq.http.upgrade = req.http.upgrade;
    }
    set bereq.http.Connection = "close";
    return (pipe);
 }
 #All other functions should be fine unmodified (for basic functionality - most of mine are altered to my purposes; I find that adding a grace period, in particular, helps.
Run Code Online (Sandbox Code Playgroud)

Nginx 配置 - /etc/nginx/*/example.com.conf:

server {
    listen *:81;
    server_name example.com www.example.com static.example.com;
    root /var/www/example.com/web;
    error_log /var/log/nginx/example.com/error.log info;
    access_log /var/log/nginx/example.com/access.log timed;

    #removed error page setup

    #home page
    location = / {
        proxy_pass http://node_js;
    }

    #everything else
    location / {
        try_files $uri $uri/ @proxy;
    }
    location @proxy{
        proxy_pass http://node_js;
    }

    #removed some standard settings I use
}

upstream node_js {
    server 127.0.0.1:1337;
    server 127.0.0.1:1337;
}
Run Code Online (Sandbox Code Playgroud)

我对 proxy_pass 语句的重复并不是特别疯狂,但不幸的是,我还没有找到更干净的替代方案。一种方法可能是让一个位置块明确指定静态文件扩展名,并将 proxy_pass 语句留在任何位置块之外。

/etc/nginx/nginx.conf 中的一些设置:

set_real_ip_from 127.0.0.1;
real_ip_header X-Forwarded-For;

log_format  timed  '$remote_addr - $remote_user [$time_local] "$request" '
                   '$status $body_bytes_sent "$http_referer" '
                   '"$http_user_agent" "$http_x_forwarded_for" '
                   '$request_time $upstream_response_time $pipe';

port_in_redirect off;
Run Code Online (Sandbox Code Playgroud)

在我的其他服务器块和设置中,我还在我的 nginx 配置中启用了 gzip 和 keepalive。(顺便说一句,我相信 Nginx 有一个 TCP 模块可以使用 websockets - 但是,我喜欢使用“vanilla”版本的软件(及其相关的存储库),所以这对我来说并不是一个真正的选择)。

此设置的先前版本导致清漆管道出现异常“阻塞”行为。本质上,一旦建立了管道套接字连接,下一个请求将被延迟,直到管道超时(最多 60 秒)。我还没有看到与此设置相同的重复发生 - 但很想知道您是否看到类似的行为。