mittwald / kube-httpcache

Varnish Reverse Proxy on Kubernetes
MIT License
295 stars 72 forks source link

Purge from magento wrong behaviour #153

Open mutonby opened 1 year ago

mutonby commented 1 year ago

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.

martin-helmich commented 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?

mutonby commented 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?

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 version 5.0 is not supported so it should be 4.0 even though actually used Varnish version is 5

  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

and pod 2

`* << Request >> 496589

Thx so much