A melhor maneira de implantar meu aplicativo node.js em um servidor Varnish / Nginx

5

Estou prestes a implantar um novo aplicativo node.js e preciso de ajuda para configurá-lo.

A maneira como minha configuração é agora é a seguinte.

Eu tenho o Varnish sendo executado em external_ip:80

Eu tenho o Nginx atrasado na execução de internal_ip:80

Ambos estão ouvindo na porta 80, uma porta interna e outra externa.

NOTA: o aplicativo node.js é executado em WebSockets

Agora eu tenho o meu novo aplicativo node.js que vai escutar na porta 8080.

Posso configurar o verniz para que ele fique na frente do nginx e do node.js.

O Varnish tem que fazer proxy do websocket para a porta 8080, mas depois os arquivos estáticos como css, js, etc precisam passar pela porta 80 para o nignx.

O Nginx não suporta websockets prontos para o uso, senão eu faria uma configuração como:

verniz - > nignx - > node.js

    
por Saif Bechan 18.03.2012 / 16:07

1 resposta

10

Tendo apenas configurado um projeto que é essencialmente idêntico ao que você descreve, compartilharei minha abordagem - não há garantias de que seja "o melhor", mas funciona.

Minha pilha de servidores é

  • Varnish (v3.0.2) - todas as interfaces, porta 80
  • Nginx (v1.0.14) - interface local, porta 81
  • Node.js (v0.6.13) - interface local, porta 1337
  • O sistema operacional é o CentOS 6.2 (ou similar)

Meu aplicativo Node.js usa Websockets (sockets.io - v0.9.0) e Express (v2.5.8) - e é iniciado usando para sempre. (O mesmo servidor também tem outros sites nele - principalmente PHP, que usam as mesmas instâncias de Nginx e Varnish).

A intenção básica da minha abordagem é a seguinte:

  • Porta / endereço público único para dados do websocket e 'regulares'
  • Cache alguns recursos usando o verniz
  • Servir ativos estáticos (sem cache) diretamente do nginx
  • Passar solicitações de 'páginas da web' para o nginx e de seu proxy para o Node.js
  • Passar solicitações de soquete da web diretamente (do verniz) para o Node.js (ignorar o nginx).

Configuração do verniz - /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.

Configuração do 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;
}

Eu não sou particularmente louco pela repetição da declaração proxy_pass, mas ainda não encontrei uma alternativa mais limpa, infelizmente. Uma abordagem pode ser ter um bloco de localização especificando as extensões de arquivo estático explicitamente e deixar a instrução proxy_pass fora de qualquer bloco de localização.

Algumas configurações do /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;

Entre meus outros servidores e configurações, eu também tenho o gzip e o keepalive ativados na configuração do meu nginx. (Como um aparte, eu acredito que há um módulo TCP para Nginx que permitiria o uso de websockets - no entanto, eu gosto de usar versões 'vanilla' de software (e seus repositórios associados), então isso não era realmente uma opção para mim ).

Uma versão anterior dessa configuração resultou em um comportamento incomum de "bloqueio" com a tubulação em verniz. Essencialmente, uma vez estabelecida uma conexão de soquete canalizado, a próxima solicitação seria adiada até o tempo limite do cano (até 60s). Eu ainda não vi a mesma recorrência com esta configuração - mas estaria interessado em saber se você vê um comportamento semelhante.

    
por 19.03.2012 / 05:20