lando / varnish

The Official Varnish Lando Plugin
https://docs.lando.dev/varnish/
GNU General Public License v3.0
2 stars 2 forks source link

Backend host "appserver": resolves to too many addresses when running multiple sites with Lando #5

Open kaszarobert opened 2 years ago

kaszarobert commented 2 years ago

I'm experiencing an issue I don't have any idea how to solve it when working with the Drupal 9 recipe.

After lando poweroff if I start a site with Varnish, the Varnish can connect to the "appserver" as expected. But if I start another website that doesn't have Varnish, and only after that I try to start a site together with Varnish, visiting the site via Varnish, the page only shows this text: Bad Gateway

Running lando logs -s varnish shows this error:

varnish_1      | Error:
varnish_1      | Message from VCC-compiler:
varnish_1      | Backend host "appserver": resolves to too many addresses.
varnish_1      | Only one IPv4 and one IPv6 are allowed.
varnish_1      | Please specify which exact address you want to use, we found all of these:
varnish_1      |        172.28.0.5:80
varnish_1      |        172.28.0.4:80
varnish_1      | ('/etc/varnish/includes/backend.vcl' Line 4 Pos 13)
varnish_1      |     .host = "appserver";
varnish_1      | ------------###########-
varnish_1      |
varnish_1      |
varnish_1      | In backend specification starting at:
varnish_1      | ('/etc/varnish/includes/backend.vcl' Line 3 Pos 1)
varnish_1      | backend backend1 {
varnish_1      | #######-----------
varnish_1      |
varnish_1      | Running VCC-compiler failed, exited with 2
varnish_1      | VCL compilation failed

Why does Lando want to connect to a whole different project that happens to have an "appserver" service, too? I thought they are completely separated, no matter how many sites I'm starting with lando start.

This is how I include the varnish in the Landofile currently:

  varnish:
    type: varnish:6
    backends:
      - appserver
    ssl: true
    config:
      vcl: .lando/default.vcl
    overrides:
      environment:
        VARNISH_BACKEND_HOST: appserver
        VARNISH_BACKEND_PORT: 80
        VARNISH_BACKEND_FIRST_BYTE_TIMEOUT: 300s
        VARNISH_ALLOW_UNRESTRICTED_PURGE: 1
        VARNISHD_PARAM_HTTP_RESP_HDR_LEN: 65536
        VARNISHD_PARAM_HTTP_RESP_SIZE: 98304
        VARNISHD_PARAM_WORKSPACE_BACKEND: 131072

And this is the "default.vcl" I'm trying to use:

vcl 4.0;
import std;
import directors;

# Default backend definition. Set this to point to your content server.
backend default {
    .host = "{{ getenv "VARNISH_BACKEND_HOST" }}";
    .port = "{{ getenv "VARNISH_BACKEND_PORT" "80" }}";
    .first_byte_timeout = 300s;
}

# Respond to incoming requests.
sub vcl_recv {
    # Pass through files, as they are static, and can be returned quickly.
    if (req.url ~ "^/sites/default/files/" ||
        req.url ~ "^/sites/[^/]+/files/" ||
        req.url ~ "^/system/files/") {
            if (!req.url ~ "^/sites/default/files/styles/") {
                return (pass);
            }
    }

    # Add an X-Forwarded-For header with the client IP address.
    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;
        }
    }

    # Only allow PURGE requests from IP addresses in the 'purge' ACL.
    if (req.method == "PURGE") {
        return (hash);
    }

    # Only allow BAN requests from IP addresses in the 'purge' ACL.
    if (req.method == "BAN") {
        # Logic for the ban, using the Cache-Tags header. For more info
        # see https://github.com/geerlingguy/drupal-vm/issues/397.
        if (req.http.Cache-Tags) {
            ban("obj.http.Cache-Tags ~ " + req.http.Cache-Tags);
        }
        else {
            return (synth(403, "Cache-Tags header missing."));
        }

        # Throw a synthetic page so the request won't go to the backend.
        return (synth(200, "Ban added."));
    }

    if (req.method == "URIBAN") {
      ban("req.http.host == " + req.http.host + " && req.url == " + req.url);
      # Throw a synthetic page so the request won't go to the backend.
      return (synth(200, "Ban added."));
    }

    # Only cache GET and HEAD requests (pass through POST requests).
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }

    # Pass through any administrative or AJAX-related paths.
    if (req.url ~ "^/status\.php$" ||
        req.url ~ "^/install\.php" ||
        req.url ~ "^/user" ||
        req.url ~ "^/user/.*$" ||
        req.url ~ "^/users/.*$" ||
        req.url ~ "^/info/.*$" ||
        req.url ~ "^/update\.php$" ||
        req.url ~ "^/admin$" ||
        req.url ~ "^/admin/.*$" ||
        req.url ~ "^/flag/.*$" ||
        req.url ~ "^.*/ajax/.*$" ||
        req.url ~ "^.*/ahah/.*$") {
           return (pass);
    }

    # Removing cookies for static content so Varnish caches these files.
    if (req.url ~ "(?i)\.(pdf|asc|dat|txt|doc|xls|ppt|tgz|csv|png|gif|jpeg|jpg|ico|swf|css|js)(\?.*)?$") {
        unset req.http.Cookie;
    }

    # Remove all cookies that Drupal doesn't need to know about. We explicitly
    # list the ones that Drupal does need, the SESS and NO_CACHE. If, after
    # running this code we find that either of these two cookies remains, we
    # will pass as the page cannot be cached.
    if (req.http.Cookie) {
        # 1. Append a semi-colon to the front of the cookie string.
        # 2. Remove all spaces that appear after semi-colons.
        # 3. Match the cookies we want to keep, adding the space we removed
        #    previously back. (\1) is first matching group in the regsuball.
        # 4. Remove all other cookies, identifying them by the fact that they have
        #    no space after the preceding semi-colon.
        # 5. Remove all spaces and semi-colons from the beginning and end of the
        #    cookie string.
        set req.http.Cookie = ";" + req.http.Cookie;
        set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
        set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE)=", "; \1=");
        set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

        if (req.http.Cookie == "") {
            # If there are no remaining cookies, remove the cookie header. If there
            # aren't any cookie headers, Varnish's default behavior will be to cache
            # the page.
            unset req.http.Cookie;
        }
        else {
            # If there is any cookies left (a session or NO_CACHE cookie), do not
            # cache the page. Pass it on to Apache directly.
            return (pass);
        }
    }
}

# Set a header to track a cache HITs and MISSes.
sub vcl_deliver {
    # Remove ban-lurker friendly custom headers when delivering to client.
    unset resp.http.X-Url;
    unset resp.http.X-Host;
    # Comment these for easier Drupal cache tag debugging in development.
    unset resp.http.Cache-Tags;
    unset resp.http.X-Drupal-Cache-Contexts;

    if (obj.hits > 0) {
        set resp.http.Cache-Tags = "HIT";
    }
    else {
        set resp.http.Cache-Tags = "MISS";
    }
}

# Instruct Varnish what to do in the case of certain backend responses (beresp).
sub vcl_backend_response {
    # Set ban-lurker friendly custom headers.
    set beresp.http.X-Url = bereq.url;
    set beresp.http.X-Host = bereq.http.host;

    # Cache 404s, 301s, at 500s with a short lifetime to protect the backend.
    if (beresp.status == 404 || beresp.status == 301 || beresp.status == 500) {
        set beresp.ttl = 10m;
    }

    # Don't allow static files to set cookies.
    # (?i) denotes case insensitive in PCRE (perl compatible regular expressions).
    # This list of extensions appears twice, once here and again in vcl_recv so
    # make sure you edit both and keep them equal.
    if (bereq.url ~ "(?i)\.(pdf|asc|dat|txt|doc|xls|ppt|tgz|csv|png|gif|jpeg|jpg|ico|swf|css|js)(\?.*)?$") {
        unset beresp.http.set-cookie;
    }

    # Allow items to remain in cache up to 6 hours past their cache expiration.
    set beresp.grace = 6h;
}
SanderWind commented 2 years ago

I solved this by using an environment override and defining the Docker container name as VARNISH_BACKEND_HOST:

services:
  varnish:
    type: varnish:6
    overrides:
      environment:
        VARNISH_BACKEND_HOST: <docker_container_name> # Like project_appserver_1
kaszarobert commented 2 years ago

You're right. Specifying the container name does fix it. Thanks!