Open mutonby opened 1 year ago
This is odd; I have to admit that I'm not particularly familiar with Magento, and their recommended Varnish setup. Since you've asked about a shared volume: No, there shouldn't be any special setup necessary.
From your issue, I infer that you're already using the Signaller component to broadcast PURGE requests to your cluster members, and that these signals are in fact received by the members. Could you share how you run your purge request specifically, and also the part of your VCL configuration (reminder: don't know a thing about Magento) that handles the purge requests?
This is odd; I have to admit that I'm not particularly familiar with Magento, and their recommended Varnish setup. Since you've asked about a shared volume: No, there shouldn't be any special setup necessary.
From your issue, I infer that you're already using the Signaller component to broadcast PURGE requests to your cluster members, and that these signals are in fact received by the members. Could you share how you run your purge request specifically, and also the part of your VCL configuration (reminder: don't know a thing about Magento) that handles the purge requests?
Yes sure, I make the purge from the admin of magento, and from the cli of magento with a command, and when I change a product or something in the admin panel magento do a purge. I can see the purge well in logs in the 2 pods, but the change is cached about 5 mins when I have 2 pods.
The vcl config is:
`vclTemplate: |
vcl 4.0;
import std;
import directors;
# The minimal Varnish version is 5.0
# For SSL offloading, pass the following header in your proxy server or load balancer: 'X-Forwarded-Proto: https'
{{ range .Frontends }}
backend {{ .Name }} {
.host = "{{ .Host }}";
.port = "{{ .Port }}";
}
{{- end }}
backend default {
.host = "xxx.svc.cluster.local";
.port = "80";
.first_byte_timeout = 600s;
.connect_timeout = 100s; # Wait a maximum of 1s for backend connection (Apache, Nginx, etc...)
.between_bytes_timeout = 200s; # Wait a maximum of 2s between each bytes sent
.probe = {
.url = "/health.html";
.timeout = 2s;
.interval = 5s;
.window = 10;
.threshold = 5;
}
}
backend usa {
.host = "yyy.svc.cluster.local";
.port = "80";
.first_byte_timeout = 600s;
.connect_timeout = 100s; # Wait a maximum of 1s for backend connection (Apache, Nginx, etc...)
.between_bytes_timeout = 200s; # Wait a maximum of 2s between each bytes sent
.probe = {
.url = "/health.html";
.timeout = 2s;
.interval = 5s;
.window = 10;
.threshold = 5;
}
}
acl purge {
"127.0.0.1"; #localhost
"localhost"; #localhost
"62.97.127.74"; #vpn cloud
"10.12.0.0/16"; #cluster
}
sub vcl_init {
new cluster = directors.hash();
{{ range .Frontends -}}
cluster.add_backend({{ .Name }}, 1);
{{ end }}
}
sub vcl_recv {
set req.backend_hint = cluster.backend(req.url);
set req.http.X-shard = req.backend_hint;
#Descomentar para mantenimiento
#return (synth(758, "Moved Temporarily"));
if (req.http.X-shard == server.identity) {
set req.backend_hint = default;
} else {
return(pass);
}
if (req.url == "/varnish-status") {
return(synth(752, "OK"));
}
# https
if ( (req.http.host ~ "^(?i)xxx.com" || req.http.host ~ "^(?i)xxx.com" || req.http.host ~ "^(?i)uk.xxx.com"|| req.http.host ~ "^(?i)it.xxx.com"|| req.http.host ~ "^(?i)es.xxx.com"|| req.http.host ~ "^(?i)us.xxx.com"|| req.http.host ~ "^(?i).xxx.com"|| req.http.host ~ "^(?i)fr.xxx.com") && req.http.X-Forwarded-Proto !~ "(?i)https" && req.method != "PURGE") {
return (synth(750, ""));
}
#Logica ALB
if (req.url ~ "he_il/(.*)" && req.http.host == "xxx.com") {
set req.url = "/he_il";
}
if ( (req.url ~ "/en_us(.*)" || req.url ~ "/static_x(.*)" || req.url ~ "/en_ca" || req.url ~ "/en_ca/" || req.url ~ "/fr_ca(.*)") && req.http.host == "xxx.com") {
set req.backend_hint = usa;
}
if (req.url ~ "/en_catalog(.*)") {
set req.backend_hint = default;
}
if ( (req.url ~ "/admin_x/(.*)" || req.url ~ "/media_x(.*)" || req.url ~ "/cat_es(.*)") && req.http.host == "xxx.com") {
set req.backend_hint = usa;
}
if (req.http.host ~ "^www\.") {
return (synth(753, ""));
}
# xxx.cat
if(req.http.host == "xxx.cat" ){
return (synth(751, ""));
}
if(req.http.host == "uk.xxx.com" ){
return (synth(754, ""));
}
if(req.http.host == "fr.xxx.com" ){
return (synth(756, ""));
}
if(req.http.host == "us.xxx.com" ){
return (synth(755, ""));
}
if(req.http.host == "es.xxx.com" || req.http.host == "ext.xxx.com" || req.http.host == "test.xxx.com"){
return (synth(757, ""));
}
# Bypass adyen
if (req.url ~ "/adyen") {
return (pass);
}
if (req.url ~ "/admin_y/(.*)" || req.url ~ "/admin_x/(.*)") {
return (pass);
}
# Bypass REST/SOAP
if (req.url ~ "/([A-Za-z]{2}_[A-Za-z]{2}/)?rest/(.*)" || req.url ~ "/([A-Za-z]{2}_[A-Za-z]{2}/)?soap/(.*)") {
return (pass);
}
# Add X-Forwarded-For client ip
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;
}
}
if (req.method == "PURGE") {
if (client.ip !~ purge) {
return (synth(405, "Method not allowed"));
}
# To use the X-Pool header for purging varnish during automated deployments, make sure the X-Pool header
# has been added to the response in your backend server config. This is used, for example, by the
# capistrano-magento2 gem for purging old content from varnish during it's deploy routine.
if (!req.http.X-Magento-Tags-Pattern && !req.http.X-Pool) {
return (synth(400, "X-Magento-Tags-Pattern or X-Pool header required"));
}
if (req.http.X-Magento-Tags-Pattern) {
ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern);
}
if (req.http.X-Pool) {
ban("obj.http.X-Pool ~ " + req.http.X-Pool);
}
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);
}
# Bypass shopping cart, checkout and search requests
if (req.url ~ "/checkout" || req.url ~ "/catalogsearch" || req.url ~ "/customer" || req.url ~ "/sales") {
return (pass);
}
# Bypass health check requests
if (req.url ~ "/health_check.php") {
return (pass);
}
if (req.url ~ "/health.html") {
return (pass);
}
if (req.url ~ "/cookie-policy" || req.url ~ "/privacy-policy") {
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);
# Compression filter. See https://www.varnish-cache.org/trac/wiki/FAQ/Compression
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 {
# unkown algorithm
unset req.http.Accept-Encoding;
}
}
# Remove Google gclid parameters to minimize the cache objects
# set req.url = regsuball(req.url,"\?gclid=[^&]+$",""); # strips when QS = "?gclid=AAA"
# set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar"
# set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz"
# NEW REMOVAL OF MARKETING TAGS
# Remove all marketing get parameters to minimize the cache objects
if (req.url ~ "(\?|&)(adgroupid|targetid|keyword|gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=") {
set req.url = regsuball(req.url, "(adgroupid|targetid|keyword|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.url ~ "^/(pub/)?(media|static)/.*\.(ico|css|js|gif|tiff|bmp|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)$") {
# Static files should not be cached by default
return (pass);
# But if you use a few locales and don't use CDN you can enable caching static files by commenting previous line (#return (pass);) and uncommenting next 3 lines
# unset req.http.Https;
# unset req.http.X-Forwarded-Proto;
# unset req.http.Cookie;
}
# ---
return (hash);
}
sub vcl_hash {
if (req.http.cookie ~ "X-Magento-Vary=") {
hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1"));
}
# For multi site configurations to not cache each other's content
if (req.http.host) {
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
# To make sure http users don't see ssl warning
if (req.http.X-Forwarded-Proto) {
hash_data(req.http.X-Forwarded-Proto);
}
}
sub vcl_backend_response {
set beresp.grace = 3d;
if (beresp.http.content-type ~ "text") {
set beresp.do_esi = true;
}
if (bereq.url ~ "\.js$" || beresp.http.content-type ~ "text") {
set beresp.do_gzip = true;
}
if (beresp.http.X-Magento-Debug) {
set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;
}
# cache only successfully responses and 404s
if (beresp.status != 200 && beresp.status != 404) {
set beresp.ttl = 0s;
set beresp.uncacheable = true;
return (deliver);
} elsif (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
# images, css and js are cacheable by default so we have to remove cookie also
if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
unset beresp.http.set-cookie;
}
# If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass
if (beresp.ttl <= 0s ||
beresp.http.Surrogate-control ~ "no-store" ||
(!beresp.http.Surrogate-Control &&
beresp.http.Cache-Control ~ "no-cache|no-store") ||
beresp.http.Vary == "*") {
# Mark as Hit-For-Pass for the next 2 minutes
set beresp.ttl = 120s;
set beresp.uncacheable = true;
}
return (deliver);
}
sub vcl_deliver {
if (resp.http.X-Magento-Debug) {
if (resp.http.x-varnish ~ " ") {
set resp.http.X-Magento-Cache-Debug = "HIT";
set resp.http.Grace = req.http.grace;
} else {
set resp.http.X-Magento-Cache-Debug = "MISS";
}
} else {
unset resp.http.Age;
}
# Not letting browser to cache non-static files.
if (resp.http.Cache-Control !~ "private" && (req.url ~ "/checkout" || req.url ~ "/catalogsearch" || req.url ~ "/customer" || req.url ~ "/sales")) {
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";
}
if (resp.http.Cache-Control !~ "private" && req.url !~ "^/(pub/)?(media|static)/") {
set resp.http.cache-control = regsub(resp.http.cache-control,
"(,\s*max-age=[0-9]+\s*$)|(\s*max-age=[0-9]+\s*,)","max-age=388,");
}
unset resp.http.X-Magento-Debug;
unset resp.http.X-Magento-Tags;
unset resp.http.X-Powered-By;
unset resp.http.Server;
unset resp.http.X-Varnish;
unset resp.http.Via;
unset resp.http.Link;
}
sub vcl_hit {
if (obj.ttl >= 0s) {
# Hit within TTL period
return (deliver);
}
if (std.healthy(req.backend_hint)) {
if (obj.ttl + 300s > 0s) {
# Hit after TTL expiration, but within grace period
set req.http.grace = "normal (healthy server)";
return (deliver);
} else {
# Hit after TTL and grace expiration
return (miss);
}
} else {
# server is not healthy, retrieve from cache
set req.http.grace = "unlimited (unhealthy server)";
return (deliver);
}
}
sub vcl_synth {
if (resp.status == 758) {
set resp.status = 302;
set resp.http.Location = "https://blog.xxx.com/maintenance/";
return(deliver);
}
if (resp.status == 752) {
set resp.status = 200;
synthetic("hello world");
return(deliver);
}
if (resp.status == 750) {
set resp.status = 301;
set resp.http.Location = "https://xxx.com" + req.url;
return(deliver);
}
if (resp.status == 753) {
set resp.status = 302;
set resp.http.Location = "https://xxx.com" + req.url;
return(deliver);
}
if (resp.status == 754) {
set resp.status = 302;
set resp.http.Location = "https://xxx.com/en_uk/";
return(deliver);
}
if (resp.status == 755) {
set resp.status = 302;
set resp.http.Location = "https://xxx.com/en_us/";
return(deliver);
}
if (resp.status == 756) {
set resp.status = 302;
set resp.http.Location = "https://xxx.com/fr_fr/";
return(deliver);
}
if (resp.status == 757) {
set resp.status = 302;
set resp.http.Location = "https://xxx.com/es_es/";
return(deliver);
}
if (resp.status == 751) {
set resp.status = 302;
set resp.http.Location = "https://xxx.com/cat_es/";
return(deliver);
}
} `
The logs of purge received in same time to 2 pods of varnish, pod1 :
`* << Request >> 1183468
Begin req 1902755 rxreq
Timestamp Start: 1681815550.142062 0.000000 0.000000
Timestamp Req: 1681815550.142062 0.000000 0.000000
ReqStart 10.12.26.194 54522 a0
ReqMethod PURGE
ReqURL /
ReqProtocol HTTP/1.1
ReqHeader Host: kubehttpcache.svc.cluster.local
ReqHeader User-Agent: Go-http-client/1.1
ReqHeader X-Magento-Tags-Pattern: ((^|,)cms_b_2(,|$))|((^|,)cms_b_footer_links_two(,|$))
ReqHeader Accept-Encoding: gzip
ReqHeader X-Forwarded-For: 10.12.27.150:44870, 10.12.27.223
ReqHeader X-shard: kube-httpcache-758d7bc875-88bpp
ReqHeader X-Varnish: 496590
ReqUnset X-Forwarded-For: 10.12.27.150:44870, 10.12.27.223
ReqHeader X-Forwarded-For: 10.12.27.150:44870, 10.12.27.223, 10.12.26.194
VCL_call RECV
ReqUnset X-shard: kube-httpcache-758d7bc875-88bpp
ReqHeader X-shard: kube-httpcache-758d7bc875-88bpp
ReqUnset X-Forwarded-For: 10.12.27.150:44870, 10.12.27.223, 10.12.26.194
ReqHeader x-forwarded-for: 10.12.27.150:44870, 10.12.27.223, 10.12.26.194, 10.12.26.194
VCL_acl MATCH purge "10.12.0.0/16"
VCL_return synth
VCL_call HASH
VCL_return lookup
Timestamp Process: 1681815550.142149 0.000087 0.000087
RespProtocol HTTP/1.1
RespStatus 200
RespReason OK
RespReason Purged
RespHeader Date: Tue, 18 Apr 2023 10:59:10 GMT
RespHeader Server: Varnish
RespHeader X-Varnish: 1183468
VCL_call SYNTH
RespHeader Content-Type: text/html; charset=utf-8
RespHeader Retry-After: 5
VCL_return deliver
RespHeader Content-Length: 242
Storage malloc Transient
RespHeader Accept-Ranges: bytes
RespHeader Connection: keep-alive
Timestamp Resp: 1681815550.142200 0.000138 0.000050
ReqAcct 317 0 317 220 242 462
End
<< Request >> 2228795
Begin req 2228794 rxreq
Timestamp Start: 1681815550.143587 0.000000 0.000000
Timestamp Req: 1681815550.143587 0.000000 0.000000
ReqStart 10.12.27.223 52384 a0
ReqMethod PURGE
ReqURL /
ReqProtocol HTTP/1.1
ReqHeader Host: kubehttpcache.svc.cluster.local
ReqHeader User-Agent: Go-http-client/1.1
ReqHeader X-Forwarded-For: 10.12.27.150:44870
ReqHeader X-Magento-Tags-Pattern: ((^|,)cms_b_2(,|$))|((^|,)cms_b_footer_links_two(,|$))
ReqHeader Accept-Encoding: gzip
ReqUnset X-Forwarded-For: 10.12.27.150:44870
ReqHeader X-Forwarded-For: 10.12.27.150:44870, 10.12.27.223
VCL_call RECV
ReqHeader X-shard: kube-httpcache-758d7bc875-88bpp
ReqUnset X-Forwarded-For: 10.12.27.150:44870, 10.12.27.223
ReqHeader x-forwarded-for: 10.12.27.150:44870, 10.12.27.223, 10.12.27.223
VCL_acl MATCH purge "10.12.0.0/16"
VCL_return synth
VCL_call HASH
VCL_return lookup
Timestamp Process: 1681815550.143650 0.000063 0.000063
RespProtocol HTTP/1.1
RespStatus 200
RespReason OK
RespReason Purged
RespHeader Date: Tue, 18 Apr 2023 10:59:10 GMT
RespHeader Server: Varnish
RespHeader X-Varnish: 2228795
VCL_call SYNTH
RespHeader Content-Type: text/html; charset=utf-8
RespHeader Retry-After: 5
VCL_return deliver
RespHeader Content-Length: 242
Storage malloc Transient
RespHeader Accept-Ranges: bytes
RespHeader Connection: keep-alive
Timestamp Resp: 1681815550.143696 0.000109 0.000046
ReqAcct 242 0 242 220 242 462
End `
and pod 2
`* << Request >> 496589
Thx so much
I have 2 pods of httpcache and 2 pods of magento and when purge I can see the signal in the 2 pods of httpcache, but I can't see the changes until after a few minutes, when they are automatically updated. When only have 1 pod of httpcache I can see the changes instantly. do you know why?
I have the vcl config recommended by magento. Need share a volume or a special vcl config for use 2 pods of httpcache?
Regards.