簡體   English   中英

如何為包含 WordPress 的多個應用程序和以 nginx 作為代理的 Ghost CMS 配置 Varnish?

[英]How to config Varnish for multiple application containing WordPress and Ghost CMS with nginx as proxy?

我們正在為 WordPress 和 Ghost 博客平台設置清漆緩存系統,以 nginx 作為網絡服務器/代理。

Wordpress Vcl(默認)

vcl 4.0;

import std;

backend default {
.host = "127.0.0.1";
.port = "8080";
.first_byte_timeout = 600s;
}

acl purger {
"localhost";
"127.0.0.1";
"172.17.0.1";
}

sub vcl_recv {
if (req.restarts \> 0) {
set req.hash_always_miss = true;
}

    #return (pass);
    
    if (req.method == "PURGE") {
        if (client.ip !~ purger) {
            return (synth(405, "Method not allowed"));
        }
        if (req.http.X-Cache-Tags) {
          ban("obj.http.X-Cache-Tags ~ " + req.http.X-Cache-Tags);
        } else {
          ban("req.http.host == " +req.http.host+" && req.url ~ "+req.url);
          return (synth(200, "Purged"));
        }
        return (synth(200, "Purged"));
    }
    
    if (req.method != "GET" &&
        req.method != "HEAD" &&
        req.method != "PUT" &&
        req.method != "POST" &&
        req.method != "TRACE" &&
        req.method != "OPTIONS" &&
        req.method != "DELETE") {
          /* Non-RFC2616 or CONNECT which is weird. */
          return (pipe);
    }
    
    # We only deal with GET and HEAD by default
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }
    
    # Set initial grace period usage status
    set req.http.grace = "none";
    
    # normalize url in case of leading HTTP scheme and domain
    set req.url = regsub(req.url, "^http[s]?://", "");
    
    # collect all cookies
    std.collect(req.http.Cookie);
    
    if (req.url ~ "^/admin/" || req.url ~ "/paypal/") {
        return (pass);
    }
    
    if (req.http.cookie ~ "wordpress_logged_in_") {
        return (pass);
    }
    
    if (req.http.Accept-Encoding) {
        if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") {
            # No point in compressing these
            unset req.http.Accept-Encoding;
        } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } elsif (req.http.Accept-Encoding ~ "deflate" && req.http.user-agent !~ "MSIE") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            # unknown algorithm
            unset req.http.Accept-Encoding;
        }
    }
    
    if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=") {
        set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=[-_A-z0-9+()%.]+&?", "");
        set req.url = regsub(req.url, "[?|&]+$", "");
    }
    
    if (req.http.Authorization ~ "^Bearer") {
        return (pass);
    }
    
    return (hash);

}

sub vcl_hash {
if (req.http.host) {
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
}

sub vcl_backend_response {

    set beresp.grace = 3d;
    
    if (beresp.http.content-type ~ "text") {
        set beresp.do_esi = true;
    }
    
    if (beresp.http.content-type ~ "text") {
        set beresp.do_gzip = true;
    }
    
    # cache only successfully responses and 404s that are not marked as private
    if (beresp.status != 200 && beresp.status != 404 && beresp.http.Cache-Control ~ "private") {
        set beresp.uncacheable = true;
        set beresp.ttl = 86400s;
        return (deliver);
    }
    
    # validate if we need to cache it and prevent from setting cookie
    if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
        unset beresp.http.set-cookie;
    }

if (!beresp.http.cache-control) {
set beresp.ttl = 0s;
set beresp.uncacheable = true;
}

    return (deliver);

}

sub vcl_deliver {

    set resp.http.X-Cache-Age = resp.http.Age;
    unset resp.http.Age;
    
    # Avoid being cached by the browser.
    if (resp.http.Cache-Control !~ "private") {
      set resp.http.Pragma = "no-cache";
      set resp.http.Expires = "-1";
      set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
    }
    
    unset resp.http.X-Powered-By;
    unset resp.http.Server;
    unset resp.http.X-Varnish;
    unset resp.http.Via;
    unset resp.http.Link;
    unset resp.http.X-Frame-Options;
    unset resp.http.X-Content-Type-Options;
    unset resp.http.X-Xss-Protection;
    unset resp.http.Referer-Policy;
    unset resp.http.X-Permitted-cross-domain-policies;

}

sub vcl_hit {
if (obj.ttl \>= 0s) {
return (deliver);
}
set req.http.grace = "unlimited (unhealthy server)";
return (deliver);
}

include "all-vhosts.vcl";

Nginx 所有 WordPress 站點的 Vhost

server {
listen 80;
listen \[::\]:80;
listen 443 ssl http2;
listen \[::\]:443 ssl http2;
{{ssl_certificate_key}}
{{ssl_certificate}}
server_name www.test.in;
return 301 https://test.in$request_uri;
}

server {
listen 8080;
listen \[::\]:8080;
server_name yourdomain.in www1.yourdomain.in;
{{root}}

try_files $uri $uri/ /index.php?$args;
index index.php index.html;

location \~ .php$ {
include fastcgi_params;
fastcgi_intercept_errors on;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files $uri =404;
fastcgi_read_timeout 3600;
fastcgi_send_timeout 3600;
fastcgi_param HTTPS "on";
fastcgi_param SERVER_PORT 443;
fastcgi_pass 127.0.0.1:{{php_fpm_port}};
fastcgi_param PHP_VALUE "{{php_settings}}";
}

if (-f $request_filename) {
break;
}
}

server {
listen 80;
listen \[::\]:80;
listen 443 ssl http2;
listen \[::\]:443 ssl http2;
{{ssl_certificate_key}}
{{ssl_certificate}}
server_name yourdomain.in www1.yourdomain.in;
{{root}}

{{nginx_access_log}}
{{nginx_error_log}}

if ($scheme != "https") {
rewrite ^ https://$host$uri permanent;
}

location \~ /.well-known {
auth_basic off;
allow all;
}

{{settings}}

location \~/.git {
deny all;
}

location \~/(wp-admin/|wp-login.php) {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8080;
proxy_max_temp_file_size 0;
proxy_connect_timeout      7200;
proxy_send_timeout         7200;
proxy_read_timeout         7200;
proxy_buffer_size          128k;
proxy_buffers              4 256k;
proxy_busy_buffers_size    256k;
proxy_temp_file_write_size 256k;
}

location / {
{{varnish_proxy_pass}}
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_hide_header X-Varnish;
proxy_redirect off;
proxy_max_temp_file_size 0;
proxy_connect_timeout      720;
proxy_send_timeout         720;
proxy_read_timeout         720;
proxy_buffer_size          128k;
proxy_buffers              4 256k;
proxy_busy_buffers_size    256k;
proxy_temp_file_write_size 256k;
}

location \~\* ^.+.(css|js|jpg|jpeg|gif|png|ico|gz|svg|svgz|ttf|otf|woff|woff2|eot|mp4|ogg|ogv|webm|webp|zip|swf|map)$ {
add_header Access-Control-Allow-Origin "\*";
expires max;
access_log off;
}

if (-f $request_filename) {
break;
}
}

一旦我們為 Ghost 添加另一個 vcl 文件,這就完美了,我們得到 ghost cms 的 503 后端獲取錯誤。(wordpress 仍然可以正常工作)。Ghost 在 3021 端口上運行。 清漆端口 6081

幽靈VCL

vcl 4.0;
import std;

backend ghost {
.host = "127.0.0.1";
.port = "3021";
}

acl purge {
"127.0.0.1";
}

# first vcl_recv block handles the cache purging

sub vcl_recv {
set req.backend_hint = ghost;
return (hash);

if (req.url \~ "/rebuild/purge") {
if (client.ip !\~ purge) {
return (synth(405, "Method Not Allowed"));
}
ban("req.http.host == yourdomain.you");
return(synth(200, "Cache cleared"));
}
}

# second vcl_recv block handles the actual caching

sub vcl_recv {
if (req.url \~ "/assets" || req.url \~ "/content/images") {
return (hash);
} else {
return (pass);
}
}

sub vcl_backend_response {
if (bereq.url \~ "/assets" || bereq.url \~ "/content/images") {
set beresp.http.cache-control = "public, max-age=259200";
set beresp.ttl = 3d;
return (deliver);
}
}

sub vcl_deliver {

# nothing here

}

幽靈虛擬主機

proxy_cache_path /tmp/nginx_ghost levels=1:2 keys_zone=ghostcache:600m max_size=100m inactive=24h;
server {
listen 80;
listen \[::\]:80;
listen 443 ssl http2;
listen \[::\]:443 ssl http2;
{{ssl_certificate_key}}
{{ssl_certificate}}
server_name $yourdomain;
{{root}}

{{nginx_access_log}}
{{nginx_error_log}}

if ($scheme != "https") {
rewrite ^ https://$host$uri permanent;
}

location \~ /.well-known {
auth_basic off;
allow all;
}

{{settings}}

index index.html;

location / {
proxy_pass http://127.0.0.1:6081/;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $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;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
\# Remove cookies which are useless for anonymous visitor and prevent caching
proxy_ignore_headers Set-Cookie Cache-Control;
proxy_hide_header Set-Cookie;
\# Add header for cache status (miss or hit)
add_header X-Cache-Status $upstream_cache_status;
proxy_cache ghostcache;
\# Default TTL: 1 day
proxy_cache_valid 1d;
\# Cache 404 pages for 1h
proxy_cache_valid 404 1h;
\# use conditional GET requests to refresh the content from origin servers
proxy_cache_revalidate on;
proxy_buffering on;
\# Allows starting a background subrequest to update an expired cache item,
\# while a stale cached response is returned to the client.
proxy_cache_background_update on;
\# Bypass cache for errors
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
proxy_pass_request_headers on;
proxy_max_temp_file_size 0;
proxy_connect_timeout 900;
proxy_send_timeout 900;
proxy_read_timeout 900;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_temp_file_write_size 256k;
}

# No cache + keep cookies for admin and previews

location \~ ^/(ghost/|p/) {
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 https;
proxy_pass http://127.0.0.1:{{app_port}};
}

# Bypass ghost for static assets

location ^\~ /assets/ {
root /home/$yourpath/htdocs/$yourpath/content/themes/$yourtheme;
}

# Bypass ghost for original images but not resized ones

location ^\~ /content/images/(!size) {
root /home/$yourpath/htdocs/$yourpath;
}

}

當涉及多個應用程序時,我們需要有關如何處理多個 vcl 文件的幫助。 我們已經嘗試了一切,但似乎沒有任何效果。

清漆版本 (varnish-7.1.1)

有兩種方法可以讓事情井井有條:

  1. 使用 VCL 標簽
  2. 使用包括

VCL標簽

有關 VCL 標簽的教程,請參閱https://varnish-cache.org/docs/trunk/users-guide/vcl-separate.html

這個想法是將多個 VCL 文件加載到varnishd程序中,並為每個 VCL 文件分配一個 label。 然后是一個主 VCL 配置,它了解各種標准並將加載正確的子配置。

這是一個示例實現:

vcl 4.1;

import std;

# We have to have a backend, even if we do not use it
backend default { .host = "127.0.0.1"; }

sub vcl_recv {
    # Normalize host header
    set req.http.host = std.tolower(req.http.host);

    if (req.http.host ~ "^(www.)?domain.ext$") {
        return (vcl(wordpress));
    }
    if (req.http.host == "ghost.domain.ext") {
        return (vcl(ghost));
    }
    return (synth(302, "http://www.domain.ext"));
}

sub vcl_synth {
    if (resp.status == 301 || resp.status == 302) {
        set resp.http.location = resp.reason;
        set resp.reason = "Moved";
        return (deliver);
    }
}

使用以下命令,您可以為 WordPress 和 Ghost 加載 2 個單獨的 VCL 文件,同時標記它們:

varnishadm vcl.load vc_wordpress /path/to/wordpress.vcl
varnishadm vcl.load vc_ghost /path/to/ghost.vcl
varnishadm vcl.label wordpress vc_wordpress
varnishadm vcl.label ghost vc_ghost

只要有一個了解標簽的主 VCL 配置,您就可以在單個 Varnish 實例上托管多個網站的 VCL 配置

包括

另一種方法是通過 VCL 包含,它在啟動時將包含文件中的代碼直接粘貼到占位符中。

這是同一個示例,但使用 include 而不是標簽進行了重構:

vcl 4.1;

import std;

# We have to have a backend, even if we do not use it
backend default { .host = "127.0.0.1"; }

sub vcl_recv {
    # Normalize host header
    set req.http.host = std.tolower(req.http.host);

    if (req.http.host ~ "^(www.)?domain.ext$") {
        include "wordpress.vcl";
    }elseif (req.http.host == "ghost.domain.ext") {
        include "ghost.vcl";
    } else {
        return (synth(302, "http://www.domain.ext"));
    }
}

sub vcl_synth {
    if (resp.status == 301 || resp.status == 302) {
        set resp.http.location = resp.reason;
        set resp.reason = "Moved";
        return (deliver);
    }
}

選擇哪個?

我個人更喜歡標簽,因為在各種 VCL 配置之間有更好的隔離。

當需要更改時,您可以更改單個 VCL 配置,而不必為所有網站重新加載配置。

缺點是您必須確保子配置始終存在。 重新啟動varnishd會清除這些配置。

一種避免這種情況的方法是在varnishd中指定-I運行時參數。 -I指向包含將在啟動時執行的 CLI 命令的文件。 這樣您就可以填充標記的子配置,而不會在重啟后丟失它們。

使用 include 簡單很多,但隔離度較低。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM