Skip to content

Instantly share code, notes, and snippets.

@arobson
Created June 27, 2017 19:50
Show Gist options
  • Save arobson/e47726ac48a0665ae1afcff7b9ae2a28 to your computer and use it in GitHub Desktop.
Save arobson/e47726ac48a0665ae1afcff7b9ae2a28 to your computer and use it in GitHub Desktop.
npme nginx + varnish

npme NGiNX & Varnish

This write up explains how to use NGiNX and Varnish in conjunction with multi-node npm Enterprise configurations. The goal is to use Varnish to provide load balancing and caching and use NGiNX as a SSL terminating reverse proxy.

Topology

The recommended topology is to provide a dedicated host for NGiNX and Varnish to run on. In environments where a secondary instance is warranted to support load, running NGiNX and Varnish on the primary will only introduce competition for resources and could destabilize your environment.

IMPORTANT The configuration files provided assume that NGiNX and Varnish are each running on the same dedicated host, separate from the hosts for the primary and secondary npmE instances.

Routing Host

Install Varnish and NGiNX using your distro's package manager. Varnish runs on port 6081 by default. The configuration provided will cause NGiNX to listen on ports 80, 443, and 4443.

  • sudo nano /etc/nginx/nginx.conf
    • edit the nginx.conf file provided according to the steps listed at the top
    • edit /etc/sysctl.conf and change or add the line fs.file-max = 4096
    • copy your SSL pem file to /etc/nginx/wildcard.pem
    • reload the NGiNX configuration with sudo nginx -s reload
  • sudo nano /etc/varnish/default.vcl
    • edit the varnish.vcl file provided according to the steps listed at the top
    • restart varnish:
      • on Debian sytems - sudo service varnish restart
      • on CentOS/RHEL systems - sudo systemctl restart varnish
# -----------------------------------------------------------------------------
# 1. replace `your-domain.com` with your public-facing domain
# 2. replace `1.1.1.1` with your primary server's IP
# 3. replace worker_processes with 1 if host only has 1 core
# 4. add `fs.file-max = 4096` to `/etc/sysctl.conf`
# 5. copy your SSL pem file to `/etc/nginx/wildcard.pem`
# -----------------------------------------------------------------------------
user root;
worker_processes 2;
pid /var/run/nginx.pid;
worker_rlimit_nofile 30000;
events {
worker_connections 4096;
}
http {
upstream website {
# primary server website.
server 1.1.1.1:8081;
}
upstream registry {
server 127.0.0.1:6081;
}
client_max_body_size 200M;
keepalive_timeout 65;
sendfile on;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log main;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# HTTPS - npmE website
server {
listen 443;
server_name your-domain.com;
ssl on;
ssl_certificate /etc/nginx/wildcard.pem;
ssl_certificate_key /etc/nginx/wildcard.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://website;
proxy_pass_request_headers on;
}
}
# HTTPS - npmE Registry
server {
listen 443;
server_name registry.your-domain.com;
ssl on;
ssl_certificate /etc/nginx/wildcard.pem;
ssl_certificate_key /etc/nginx/wildcard.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://registry;
proxy_pass_request_headers on;
}
}
# npm Enterprise on HTTPS.
server {
listen 4443;
server_name npm.your-domain.com;
ssl on;
ssl_certificate /etc/nginx/wildcard.pem;
ssl_certificate_key /etc/nginx/wildcard.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://registry;
proxy_pass_request_headers on;
}
}
# HTTP -
server {
listen 80;
server_name npm.your-domain.com;
rewrite ^ https://$server_name$request_uri? permanent;
}
}
# -----------------------------------------------------------------------------
# 1. replace `1.1.1.1` with your primary server's IP
# 2. replace `2.2.2.2` with your secondary server's IP
# 3. duplicate `replica1` block for each secondary server
# 4. add additional replicas in `vcl_init` via `pkgread.add_backend()`
# -----------------------------------------------------------------------------
vcl 4.0;
import directors;
backend primary {
.host = "1.1.1.1";
.port = "8080";
.probe = {
.url = "/";
.timeout = 2s;
.interval = 30s;
.window = 5;
.threshold = 3;
}
}
backend replica1 {
.host = "2.2.2.2";
.port = "8080";
.probe = {
.url = "/";
.timeout = 2s;
.interval = 30s;
.window = 5;
.threshold = 3;
}
}
sub vcl_init {
new pkgread = directors.round_robin();
pkgread.add_backend(primary);
pkgread.add_backend(replica1);
}
sub vcl_backend_response {
unset beresp.http.Etag;
if (bereq.url ~ "@" || bereq.url ~ "/-/whoami" || beresp.status >= 300 || bereq.method != "GET") {
# DON'T CACHE SCOPED MODULES.
set beresp.ttl = 0s;
} else if (bereq.url ~ "\.tgz$") {
# CACHE TARBALLS FOR A LONG TIME.
set beresp.ttl = 21600s;
} else if (bereq.url ~ "^/[^/]+$") {
# DON'T CACHE JSON FOR LONG.
set beresp.ttl = 300s;
} else {
# DON'T CACHE SPECIAL ROUTES
set beresp.ttl = 0s;
}
}
sub vcl_hash {
hash_data(req.url);
return (lookup);
}
sub vcl_recv {
set req.http.X-Authorization = req.http.Authorization;
unset req.http.Authorization;
unset req.http.If-Modified-Since;
# we can correct for specific package collisions here.
if (req.method == "GET") {
set req.backend_hint = pkgread.backend();
} else {
set req.backend_hint = primary;
}
}
sub vcl_backend_response {
if (beresp.status >= 300 && bereq.retries == 0 && bereq.method == "GET") {
return(retry);
}
}
sub vcl_backend_fetch {
set bereq.http.Authorization = bereq.http.X-Authorization;
if (bereq.retries > 0) {
set bereq.backend = primary;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment