GithubHelp home page GithubHelp logo

mittwald / kube-httpcache Goto Github PK

View Code? Open in Web Editor NEW
293.0 17.0 70.0 271 KB

Varnish Reverse Proxy on Kubernetes

License: MIT License

Dockerfile 6.59% Go 88.77% Smarty 4.64%
kubernetes varnish kubernetes-controller golang

kube-httpcache's Introduction

Varnish on Kubernetes

GitHub Workflow Status

This repository contains a controller that allows you to operate a Varnish cache on Kubernetes.


⚠️ COMPATIBILITY NOTICE: As of version v0.3, the image tag name of this project was renamed from quay.io/spaces/kube-httpcache to quay.io/mittwald/kube-httpcache. The old image will remain available (for the time being), but only the new image name will receive any updates. Please remember to adjust the image name when upgrading.


Table of Contents

How it works

This controller is not intended to be a replacement of a regular ingress controller. Instead, it is intended to be used between your regular Ingress controller and your application's service.

┌─────────┐       ┌─────────┐       ┌─────────────┐
| Ingress | ----> | Varnish | ----> | Application |
└─────────┘       └─────────┘       └─────────────┘

The Varnish controller needs the following prerequisites to run:

  • A Go-template that will be used to generate a VCL configuration file
  • An application Kubernetes service that will be used as backend for the Varnish controller
  • A Varnish Kubernetes service that will be used as frontend for the Varnish controller
  • If RBAC is enabled in your cluster, you'll need a ServiceAccount with a role that grants WATCH access to the endpoints resource in the respective namespace

After starting, the Varnish controller will watch the configured Varnish service's endpoints and application service's endpoints; on startup and whenever these change, it will use the supplied VCL template to generate a new Varnish configuration and load this configuration at runtime.

The controller does not ship with any preconfigured configuration; the upstream connection and advanced features like load balancing are possible, but need to be configured in the VCL template supplied by you.

High-Availability mode

It can run in high avalability mode using multiple Varnish and application pods.

             ┌─────────┐
             │ Ingress │
             └────┬────┘
                  |
             ┌────┴────┐
             │ Service │
             └───┬┬────┘
             ┌───┘└───┐
┌────────────┴──┐  ┌──┴────────────┐
│   Varnish 1   ├──┤   Varnish 2   │
│  Signaller 1  ├──┤  Signaller 2  │
└─────────┬┬────┘  └────┬┬─────────┘
          │└─────┌──────┘│
          │┌─────┘└─────┐│
┌─────────┴┴────┐  ┌────┴┴─────────┐
│ Application 1 │  | Application 2 │
└───────────────┘  └───────────────┘

The Signaller component supports broadcasting PURGE and BAN requests to all Varnish nodes.

Getting started

Create a VCL template


⚠️ NOTE: The current implementation (supplying a VCL template as ConfigMap) may still be subject to change. Future implementations might for example use a Kubernetes Custom Resource for the entire configuration set.


Start by creating a ConfigMap that contains a VCL template:

apiVersion: v1
kind: ConfigMap
metadata:
  name: vcl-template
data:
  default.vcl.tmpl: |
    vcl 4.0;

    import std;
    import directors;

    // ".Frontends" is a slice that contains all known Varnish instances
    // (as selected by the service specified by -frontend-service).
    // The backend name needs to be the Pod name, since this value is compared
    // to the server identity ("server.identity" [1]) later.
    //
    //   [1]: https://varnish-cache.org/docs/6.4/reference/vcl.html#local-server-remote-and-client
    {{ range .Frontends }}
    backend {{ .Name }} {
        .host = "{{ .Host }}";
        .port = "{{ .Port }}";
    }
    {{- end }}

    backend fe-primary {
        .host = "{{ .PrimaryFrontend.Host }}";
        .port = "{{ .PrimaryFrontend.Port }}";
    }

    {{ range .Backends }}
    backend be-{{ .Name }} {
        .host = "{{ .Host }}";
        .port = "{{ .Port }}";
    }
    {{- end }}

    backend be-primary {
        .host = "{{ .PrimaryBackend.Host }}";
        .port = "{{ .PrimaryBackend.Port }}";
    }

    acl purgers {
        "127.0.0.1";
        "localhost";
        "::1";
        {{- range .Frontends }}
        "{{ .Host }}";
        {{- end }}
        {{- range .Backends }}
        "{{ .Host }}";
        {{- end }}
    }

    sub vcl_init {
        new cluster = directors.hash();

        {{ range .Frontends -}}
        cluster.add_backend({{ .Name }}, 1);
        {{ end }}

        new lb = directors.round_robin();

        {{ range .Backends -}}
        lb.add_backend(be-{{ .Name }});
        {{ end }}
    }

    sub vcl_recv
    {
        # Set backend hint for non cachable objects.
        set req.backend_hint = lb.backend();

        # ...

        # Routing logic. Pass a request to an appropriate Varnish node.
        # See https://info.varnish-software.com/blog/creating-self-routing-varnish-cluster for more info.
        unset req.http.x-cache;
        set req.backend_hint = cluster.backend(req.url);
        set req.http.x-shard = req.backend_hint;
        if (req.http.x-shard != server.identity) {
            return(pass);
        }
        set req.backend_hint = lb.backend();

        # ...

        return(hash);
    }

    # ...

Environment variables can be used from the template. {{ .Env.ENVVAR }} is replaced with the environment variable value. This can be used to set for example the Host-header for the external service.

Create a Secret

Create a Secret object that contains the secret for the Varnish administration port:

$ kubectl create secret generic varnish-secret --from-literal=secret=$(head -c32 /dev/urandom  | base64)

[Optional] Configure RBAC roles

If RBAC is enabled in your cluster, you will need to create a ServiceAccount with a respective Role.

$ kubectl create serviceaccount kube-httpcache
$ kubectl apply -f https://raw.githubusercontent.com/mittwald/kube-httpcache/master/deploy/kubernetes/rbac.yaml
$ kubectl create rolebinding kube-httpcache --clusterrole=kube-httpcache --serviceaccount=kube-httpcache

Deploy Varnish

  1. Create a StatefulSet for the Varnish controller:

    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: cache-statefulset
      labels:
        app: cache
    spec:
      serviceName: cache-service
      replicas: 2
      updateStrategy:
        type: RollingUpdate
      selector:
        matchLabels:
          app: cache
      template:
        metadata:
          labels:
            app: cache
        spec:
          containers:
          - name: cache
            image: quay.io/mittwald/kube-httpcache:stable
            imagePullPolicy: Always
            args:
            - -admin-addr=0.0.0.0
            - -admin-port=6083
            - -signaller-enable
            - -signaller-port=8090
            - -frontend-watch
            - -frontend-namespace=$(NAMESPACE)
            - -frontend-service=frontend-service
            - -frontend-port=8080
            - -backend-watch
            - -backend-namespace=$(NAMESPACE)
            - -backend-service=backend-service
            - -varnish-secret-file=/etc/varnish/k8s-secret/secret
            - -varnish-vcl-template=/etc/varnish/tmpl/default.vcl.tmpl
            - -varnish-storage=malloc,128M
            env:
            - name: NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            volumeMounts:
            - name: template
              mountPath: /etc/varnish/tmpl
            - name: secret
              mountPath: /etc/varnish/k8s-secret
            ports:
            - containerPort: 8080
              name: http
            - containerPort: 8090
              name: signaller
          serviceAccountName: kube-httpcache  # when using RBAC
          restartPolicy: Always
          volumes:
          - name: template
            configMap:
              name: vcl-template
          - name: secret
            secret:
              secretName: varnish-secret

    NOTE: Using a StatefulSet is particularly important when using a stateful, self-routed Varnish cluster. Otherwise, you could also use a Deployment resource, instead.

  2. Create a service for the Varnish controller:

    apiVersion: v1
    kind: Service
    metadata:
      name: cache-service
      labels:
        app: cache
    spec:
      ports:
      - name: "http"
        port: 80
        targetPort: http
      - name: "signaller"
        port: 8090
        targetPort: signaller
      selector:
        app: cache
  3. Create an Ingress to forward requests to cache service. Typically, you should only need an Ingress for the Services http port, and not for the signaller port (if for some reason you do, make sure to implement proper access controls)

Logging

Logging uses glog. Detailed logging e.g. for troubleshooting can be activated by passing command line parameter -v7 (where 7 is requested logging level).

Detailed how-tos

Using built in signaller component

The signaller component is responsible for broadcasting HTTP requests to all nodes of a Varnish cluster. This is useful in HA cluster setups, when BAN or PURGE requests should be broadcast across the entire cluster.

To broadcast a BAN or PURGE request to all Varnish endpoints, run one of the following commands, respectively:

$ curl -H "X-Url: /path" -X BAN http://cache-service:8090
$ curl -H "X-Host: www.example.com" -X PURGE http://cache-service:8090/path

When running from outside the cluster, you can use kubectl port-forward to forward the signaller port to your local machine (and then send your requests to http://localhost:8090):

$ kubectl port-forward service/cache-service 8090:8090

NOTE: Specific headers for PURGE/BAN requests depend on your Varnish configuration. E.g. X-Host header is set for convenience, because signaller is listening on other URL than Varnish. However, you need to support such headers in your VCL.

sub vcl_recv {
  # ...

  # Purge logic
  if (req.method == "PURGE") {
    if (client.ip !~ purgers) {
      return (synth(403, "Not allowed."));
    }
    if (req.http.X-Host) {
      set req.http.host = req.http.X-Host;
    }
    return (purge);
  }

  # Ban logic
  if (req.method == "BAN") {
    if (client.ip !~ purgers) {
      return (synth(403, "Not allowed."));
    }
    if (req.http.Cache-Tags) {
      ban("obj.http.Cache-Tags ~ " + req.http.Cache-Tags);
      return (synth(200, "Ban added " + req.http.host));
    }
    if (req.http.X-Url) {
      ban("obj.http.X-Url == " + req.http.X-Url);
      return (synth(200, "Ban added " + req.http.host));
    }
    return (synth(403, "Cache-Tags or X-Url header missing."));
  }

  # ...
}

Proxying to external services


NOTE: Native support for ExternalName services is a requested feature. Have a look at #39 if you're willing to help out.


In some cases, you might want to cache content from a cluster-external resource. In this case, create a new Kubernetes service of type ExternalName for your backend:

apiVersion: v1
kind: Service
metadata:
  name: external-service
  namespace: default
spec:
  type: ExternalName
  externalName: external-service.example

In your VCL template, you can then simply use this service as static backend (since there are no dynamic endpoints, you do not need to iterate over .Backends in your VCL template):

kind: ConfigMap
apiVersion: v1
metadata: # [...]
data:
  default.vcl.tmpl: |
    vcl 4.0;

    {{ range .Frontends }}
    backend {{ .Name }} {
        .host = "{{ .Host }}";
        .port = "{{ .Port }}";
    }
    {{- end }}

    backend backend {
        .host = "external-service.svc";
    }

    // ...

When starting kube-httpcache, remember to set the --backend-watch=false flag to disable watching the (non-existent) backend endpoints.

Helm Chart installation

You can use the Helm chart to rollout an instance of kube-httpcache:

$ helm repo add mittwald https://helm.mittwald.de
$ helm install -f your-values.yaml kube-httpcache mittwald/kube-httpcache

For possible values, have a look at the comments in the provided values.yaml file. Take special note that you'll most likely have to overwrite the vclTemplate value with your own VCL configuration file.

Ensure your defined backend services have a port named http:

apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 8080
  type: ClusterIP

An ingress points to the kube-httpcache service which cached your backend service:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
spec:
  rules:
  - host: www.example.com
    http:
      paths:
      - backend:
          service:
            name: kube-httpcache
            port:
              number: 80
        path: /
        pathType: Prefix

Look at the vclTemplate property in chart/values.yaml to define your own Varnish cluster rules or load with extraVolume an extra file as initContainer if your ruleset is really big.

Developer notes

Build the Docker image locally

A Dockerfile for building the container image yourself is located in build/package/docker. Invoke docker build as follows:

$ docker build -t $IMAGE_NAME -f build/package/docker/Dockerfile .

kube-httpcache's People

Contributors

alikhil avatar andrein avatar bbetter173 avatar blackjid avatar dannypas00 avatar davidzisky avatar dependabot[bot] avatar elenz97 avatar eumel8 avatar fnkr avatar hensur avatar hermsi1337 avatar huppertzl avatar jfcoz avatar jkmw avatar kamsz avatar lukehandle avatar madnight avatar martin-helmich avatar mattshin avatar mittwald-machine avatar mqmr avatar robert7 avatar rouja avatar sarasensible avatar spielkind avatar teemuvesala avatar testwill avatar thylong avatar yariksheptykin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kube-httpcache's Issues

Allow for watching and templating the kube-httpcache service

Thank you for building this and making it available to the rest of us!

I'm looking to build a varnish cluster that will self route to other varnish nodes using the hash director. To do so I need kube-httpcache to monitor its own endpoint, watch it for updates, and update my vcl template with the backends that represent the varnish nodes.

My first thought was to add an additional flag to tell the controller what service represents the controller pods. Something like, -varnish-backend-service and {{ .Varnish.Hosts }}. However, if you think it's possible the number of watched services will continue to expand there may be better solutions.

Also, it wasn't clear to me what .PrimaryBackend vs .Backends was for but I'm still getting my round-robin test setup.

only expose varnishstatus for kubernetes health checks

Is your feature request related to a problem? Please describe.
varnishstatus is being exposed to the service endpoint

Describe the solution you'd like
varnishstatus exposed on a private port, or otherwise not accessible by the service endpoint, but remains open for kubernetes health checks

readinessProbe

I know this has been tried to be solved before but I think this warrants an open issue. Currently we're running a livenessProbe on a synthetic route which solves most of the problems, but as you know it doesn't really work with a readinessProbe due to the catch-22.

if (req.url == "/varnish_healthz") {
    return (synth(200, "OK"));
}

I don't have the knowledge of go to implement something myself but if I can help in any other way I'm very eager to have this feature. This is such a great project in every other way - thanks for creating this!

Describe the solution you'd like
I would like to be able to run a readinessProbe on this StatefulSet.

Describe alternatives you've considered
One idea I was toying with was to run a shell script on the pod that returns OK for the first minute and then does a curl to :80/varnish_healthz after that but I couldn't install any packages in my derivative Dockerfile, I guess this is because of how the pod is cleaned to optimize the size.

Occasional resets on v0.6.1

Describe the bug

I1117 00:18:59.170253 1 endpoints_watch.go:73] endpoints did not change
W1117 00:19:18.867979 1 run.go:53] error while watching for updates: error while loading VCL (code 106): Message from C-compiler:
gcc: internal compiler error: Killed (program cc1)
Please submit a full bug report,
with preprocessed source if appropriate.
See <file:///usr/share/doc/gcc-6/README.Bugs> for instructions.
Running C-compiler failed, exited with 4
VCL compilation failed
E1117 00:19:18.960014 1 run.go:84] signal broadcast error: Purge "http://10.100.102.107:6081/": read tcp 10.100.102.107:60068->10.100.102.107:6081: read: connection reset by peer
I1117 00:19:18.960033 1 run.go:85] retrying in 30s
Error: Child (31) not responding to CLI, killed it.
Error: Unexpected reply from ping: 400 CLI communication error (hdr)
Error: Child (31) died signal=9
Debug: Child cleanup complete
Debug: Child (282) Started
Info: Child (282) said Child starts
E1117 00:19:26.953294 1 run.go:84] signal broadcast error: Purge "http://10.100.97.79:6081/": read tcp 10.100.102.107:51126->10.100.97.79:6081: read: connection reset by peer
I1117 00:19:26.953309 1 run.go:85] retrying in 30s

Expected behavior
The varnish server to come up and stay up

Environment:

  • Kubernetes version: 1.19
  • kube-httpcache version: v0.6.1

Configuration
If applicable, add your VCL configuration file and other relevant configuration settings

Additional context
Add any other context about the problem here.

Varnishd does not start, defauly.vcl is not updated, No errors in logs.

Describe the bug
Note: This is probably not a bug but we have marked as bug for lack of a better classification.

We are trying to deploy kube-httpcache into GKE (1.16.13-gke.1).

We have deployed as per the instructions and unfortunately the varnishd is never started. We assume this is because there is potentially an error in the config map template and when this is being converted to the default.vcl the issue arises. However we are using the config map as per instructions (we do have our own VCL but we have not deployed that at this stage).

We note that the default.vcl contents is never updated and it contains the default values.

We note that we have also tried with and without the varnish-vcl-template-poll with no luck.

Pod Logs:

I0903 14:47:12.360019       1 main.go:31] running kube-httpcache with following options: {Kubernetes:{Config: RetryBackoffString:30s RetryBackoff:30s} Frontend:{Address:0.0.0.0 Port:80 Watch:false Namespace:aphrodite1994-m2-aphrodite1994-com Service:frontend-service PortName:http} Backend:{Watch:true Namespace:aphrodite1994-m2-aphrodite1994-com Service:backend-service Port: PortName:http} Signaller:{Enable:true Address:0.0.0.0 Port:8090 WorkersCount:1 MaxRetries:5 RetryBackoffString:30s RetryBackoff:30s} Admin:{Address:0.0.0.0 Port:6083} Varnish:{SecretFile:/etc/varnish/k8s-secret/secret Storage:malloc,128M AdditionalParameters: VCLTemplate:/etc/varnish/tmpl/default.vcl.tmpl VCLTemplatePoll:true} Readiness:{Enable:true Address:0.0.0.0:9102}}
I0903 14:47:12.360266       1 main.go:38] using in-cluster configuration
I0903 14:47:12.362767       1 run.go:15] waiting for initial configuration before starting Varnish
E0903 14:47:12.371195       1 endpoints_watch.go:29] error while establishing watch: unknown (get endpoints)
I0903 14:47:12.371216       1 endpoints_watch.go:30] retrying after 30s
E0903 14:47:42.374661       1 endpoints_watch.go:29] error while establishing watch: unknown (get endpoints)
I0903 14:47:42.374703       1 endpoints_watch.go:30] retrying after 30s

To Reproduce
Steps to reproduce the behavior:

  1. Create yaml files provided
  2. Deploy yaml files provided
  3. Note that varnishd service is never started.

Expected behavior

  1. kube-httpcache to start varnishd as expected.

Environment:

  • Kubernetes version: 1.16.13-gke.1
  • kube-httpcache version: master

Configuration

varnish.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: cache-statefulset
  labels:
    app: cache
spec:
  serviceName: cache-service
  replicas: 1
  selector:
    matchLabels:
      app: cache
  template:
    metadata:
      labels:
        app: cache
    spec:
      containers:
      - name: cache
        image: quay.io/mittwald/kube-httpcache:stable
        imagePullPolicy: Always
        args:
        - -admin-addr=0.0.0.0
        - -admin-port=6083
        - -signaller-enable
        - -signaller-port=8090
        - -frontend-namespace=$(NAMESPACE)
        - -frontend-service=frontend-service
        - -backend-namespace=$(NAMESPACE)
        - -backend-service=backend-service
        - -varnish-secret-file=/etc/varnish/k8s-secret/secret
        - -varnish-vcl-template=/etc/varnish/tmpl/default.vcl.tmpl
        - -varnish-vcl-template-poll=true
        - -varnish-storage=malloc,128M
        - -log_dir=/var/log/varnish
        env:
        - name: NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        volumeMounts:
        - name: template
          mountPath: /etc/varnish/tmpl
        - name: secret
          mountPath: /etc/varnish/k8s-secret
      restartPolicy: Always
      volumes:
      - name: template
        configMap:
          name: vcl-template
      - name: secret
        secret:
          secretName: varnish-secret

varnish-config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: vcl-template
data:
  default.vcl.tmpl: |
    vcl 4.0;

    import std;
    import directors;

    // ".Frontends" is a slice that contains all known Varnish instances
    // (as selected by the service specified by -frontend-service).
    // The backend name needs to be the Pod name, since this value is compared
    // to the server identity ("server.identity" [1]) later.
    //
    //   [1]: https://varnish-cache.org/docs/6.4/reference/vcl.html#local-server-remote-and-client
    {{ range .Frontends }}
    backend {{ .Name }} {
        .host = "{{ .Host }}";
        .port = "{{ .Port }}";
    }
    {{- end }}

    backend fe-primary {
        .host = "{{ .PrimaryFrontend.Host }}";
        .port = "{{ .PrimaryFrontend.Port }}";
    }

    {{ range .Backends }}
    backend be-{{ .Name }} {
        .host = "{{ .Host }}";
        .port = "{{ .Port }}";
    }
    {{- end }}

    backend be-primary {
        .host = "{{ .PrimaryBackend.Host }}";
        .port = "{{ .PrimaryBackend.Port }}";
    }

    acl purgers {
        "127.0.0.1";
        "localhost";
        "::1";
        {{- range .Frontends }}
        "{{ .Host }}";
        {{- end }}
        {{- range .Backends }}
        "{{ .Host }}";
        {{- end }}
    }

    sub vcl_init {
        new cluster = directors.hash();

        {{ range .Frontends -}}
        cluster.add_backend({{ .Name }}, 1);
        {{ end }}

        new lb = directors.round_robin();

        {{ range .Backends -}}
        lb.add_backend(be-{{ .Name }});
        {{ end }}
    }

    sub vcl_recv
    {
        # Set backend hint for non cachable objects.
        set req.backend_hint = lb.backend();

        # ...

        # Routing logic. Pass a request to an appropriate Varnish node.
        # See https://info.varnish-software.com/blog/creating-self-routing-varnish-cluster for more info.
        unset req.http.x-cache;
        set req.backend_hint = cluster.backend(req.url);
        set req.http.x-shard = req.backend_hint;
        if (req.http.x-shard != server.identity) {
            return(pass);
        }
        set req.backend_hint = lb.backend();

        # ...

        return(hash);
    }

Any help and/or insight is greatly appreciated

Mutli-pod HA deployments

So I'm wanting to setup a HA varnish deployment, and have a single endpoint to send purge requests to: At the moment, as far as I can tell, we need to hit each pod replica in the varnish deployment.

e.g.

Varnish Deployment:
-----------------
----Service ----
-----------------
  | |        \ \ 
----------  ----------
- Pod 1 -   - Pod 2 -
----------  ----------
 | |    \ \    / /   \ \
-------------------   ----------------------
----Backend 1 ---   ----- Backend 2 ----
-------------------   ----------------------

What I'm wanting to do is issue a PURGE request to a single endpoint and all pods then purge. At the moment, I'm only running a single varnish pod.

We could use varnish broadcaster to achieve this; but we need to know the pod IP of each currently running pod, which would need to be updated on Deployment scale.
https://docs.varnish-software.com/varnish-broadcaster/examples/

I'm thinking we could build out a second deployment that runs varnish broadcaster that updates it's nodes on change of the first deployment, with a Service that links into that.

That setup would allow true HA (2 nodes of varnish running on 2 nodes, with 2 broadcasters running on 2 nodes ) If any 1 node destroyed, the service would remain up, with full capacity for purging.

Error Deploy statefulSet

Hi:

after creating the configmap and the secret, I have the following error when I want to display the StatefulSet

[ValidationError(StatefulSet.spec.template.spec.containers[0]): unknown field "restartPolicy" in io.k8s.api.core.v1.Container, ValidationError(StatefulSet.spec.template.spec): unknown field "env" in io.k8s.api.core.v1.PodSpec, ValidationError(StatefulSet.spec): missing required field "selector" in io.k8s.api.apps.v1.StatefulSetSpec]; if you choose to ignore these errors, turn validation off with --validate=false

don't uninstall `apt-transport-https` in docker build

Is your feature request related to a problem? Please describe.
I cannot use this a pinned version of this docker image as a base image and use apt

Because the docker image uses the apt source https://packagecloud.io/varnishcache/varnish60lts/debian/dists/stretch/InRelease, we cannot run apt update:

$ apt update
...
Hit:4 http://deb.debian.org/debian stretch Release        
Get:5 http://security.debian.org/debian-security stretch/updates/main amd64 Packages [722 kB]
Fetched 775 kB in 1s (575 kB/s)                          
Reading package lists... Done
E: The method driver /usr/lib/apt/methods/https could not be found.
N: Is the package apt-transport-https installed?
E: Failed to fetch https://packagecloud.io/varnishcache/varnish60lts/debian/dists/stretch/InRelease  

This is probably fine as long as we are using the latest docker image (and the latest docker image is regularly updated with the most recent packages), but we would like to pin to an exact version for stability purposes. If the image is old enough, we cannot install apt-transport-https ourselves (using apt) because it is out-of-date.

Describe the solution you'd like
The docker image does not purge the apt-transport-https, allowing users to run apt[-get] update out-of-the-box without having to install packages from source.

We definitely understand purging build packages from the docker image, but in this case, it is definitely helpful to keep around and i think the 5mb package size is justified

Additional context
Luckily, apt-update does update the deb.debian.org repos first, so we can technically run apt-get update || true, then update apt-transport-https, then proceed as normal, but this seems like a bit of a hack.

Occasional panic when WATCH'ing

panic: Get https://10.10.10.1:443/api/v1/namespaces/XXX/endpoints?fieldSelector=metadata.name%3Dproduction-app&watch=true: unexpected EOF

goroutine 19 [running]:
github.com/martin-helmich/kube-httpcache/watcher.(*BackendWatcher).watch(0xc000235c20)
	/app/watcher/types.go:59 +0xe6a
created by github.com/martin-helmich/kube-httpcache/watcher.(*BackendWatcher).Run
	/app/watcher/types.go:47 +0x3f

Use current pod namespace as the default one

When the backend namespace is not set, but backend service is set there is an error message:

an empty namespace may not be set when a resource name is provided

In case if namespace is empty, it would be nice to set a namespace automatically to the one that Varnish pod is currently running in, this will make deployment config more reusable.

My use case is that I am using same deployment config for different environments (dev, stage, etc...) and even on demand environments created on the fly (which are associated with their own namespace).

Template Watcher only fired once

Describe the bug
Template Watcher only fired once. I will change VCL template many time, but only fist template change will fired. And I can ensure this by see kubectl logs StatefulSet/cache-statefulset.

To Reproduce
Steps to reproduce the behavior:

  1. Create a new Ctl;
  2. Modify the ConfigMap of the VCL template once;
  3. You can make sure that our modification has been applied by kubectl logs;
  4. Modify the ConfigMap AGAIN, and apply the new ConfigMap;
  5. You can make sure our ConfigMap has been modified by cat /etc/varnish/tmpl/default.vcl.tmpl;
  6. And finally you will NEVER see any Watcher will fired by kubectl logs.

Expected behavior
We can apply any times of VCL template.

Environment:

  • Kubernetes version: 1.17.9
  • kube-httpcache version: v0.3.3

{{ range .Backends -}} doesn't de-register on pod delete

I've got a VCL running health checks:

{{ range .Backends }}
backend be-{{ .Name }} {
  .host = "{{ .Host }}";
  .port = "{{ .Port }}";
  .first_byte_timeout = 600s;
  .probe = {
      .url = "/health_check.php";
      .timeout = 2s;
      .interval = 5s;
      .window = 10;
      .threshold = 5;
  }
}
{{- end }}

Now even with 1 backend in the service to discover, we're getting multiple health checks (to the point it's overloading our server). (this is a single pod's logs here). I'm assuming it's been registered more than once in the range.Backends, but I could be wrong.

127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:43 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:43 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:43 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:43 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:43 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:44 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:44 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:44 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:44 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:44 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:44 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:44 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:44 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:44 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"
127.0.0.1 -  29/Jan/2020:23:40:46 +0000 "GET /health_check.php" 200
10.0.1.179:80 10.0.1.139 - - [29/Jan/2020:23:40:44 +0000] "GET /health_check.php HTTP/1.1" 200 517 "-" "-"

Varnish shared memory logging

I'm currently working on a helm chart and ran into an issue with shared memory. I want to setup my helm chart with an additional pod that will run varnishncsa and write json logs to stdout. To do so I need varnishd and varnishncsa to use the same working directory. This can be done by passing a flag, -n, at run-time. I also need to set the VSM_NOPID env var in varnishncsa.

Since kube-httpcache is running varnishd it will have to pass that flag along. What are you thoughts on solving this?

  1. My first thought was to add a flag and pass it to varnishd.
  2. My second thought was to stop running varnishd from kube-httpcache and instead run both from the same pod. I'm assuming that the only requirement is that the two can communicate via the admin port. If that's correct I think that might be a better option. This way kube-httpcache doesn't have to get in the middle of everything.

Graceful cache redistribution when using hash director

I have multiple Varnish frontends powered by the hash directory. When number of frontends change, hash is recalculated which makes a lot of objects out of cache. I am wondering if there is a way to gracefully move objects to a new frontend, while serving rest of the from the old ones.

I use similar config to one described here.

Throttle config updates

Is your feature request related to a problem? Please describe.
Aggressive autoscaling can trigger config updates multiple times rendering Varnish unable to respond.

Describe the solution you'd like
Have a configurable grace period before issuing a config update and discard old ones if another change was registered.

Describe alternatives you've considered
Less aggressive autoscaling or just living with it, but both don't seem that hot.

Additional context
We have pretty aggressive autoscaling since we have to get from 0-10000 Requests per Second in 2 Minutes. However, because that autoscaling is so aggressive, new varnish instances keep coming up, which results in config updates. Each config update adds a bit of latency, and if a lot of varnishes go online in a short period, the latency can shoot up to ~1s, while Varnish itself can serve the request in ~3ms.

Stateful set fails and cannot restart with frontend-watch enabled

Describe the bug
When the pods within the stateful set fall to zero (due to a crash of nodes for instance), the stateful set cannot restart with frontend-watch enabled

To Reproduce
Steps to reproduce the behavior:

  1. Create Stateful Set
  2. set replicas to 2
  3. delete all 2 pods
  4. Stateful set stuck in not started state.

Expected behavior
Stateful set will be available if all pods get terminated.

Environment:

  • Kubernetes version: 1.17
  • kube-httpcache version: v0.3.4

Configuration
n/a

Additional context

I1126 00:49:40.914736       1 main.go:31] running kube-httpcache with following options: {Kubernetes:{Config: RetryBackoffString:30s RetryBackoff:30s} Frontend:{Address:0.0.0.0 Port:6081 Watch:true Namespace:staging Service: frontend-output PortName:http} Backend:{Watch:true Namespace:staging Service:app-no-cache Port: PortName:http} Signaller:{Enable:true Address:0.0.0.0 Port:8090 WorkersCount:1 MaxRetries:5 RetryBackoffString:30s RetryBackoff:30s} Admin:{Address:0.0.0.0 Port:6083} Varnish:{SecretFile:/etc/varnish/k8s-secret/secret Storage:malloc,128M AdditionalParameters: VCLTemplate:/etc/varnish/tmpl/default.vcl.tmpl VCLTemplatePoll:false} Readiness:{Enable:true Address:0.0.0.0:9102}}
I1126 00:49:40.914861       1 main.go:38] using in-cluster configuration
I1126 00:49:40.916198       1 run.go:15] waiting for initial configuration before starting Varnish
W1126 00:49:40.924043       1 endpoints_watch.go:50] service 'frontend-output' has no endpoints
I1126 00:50:37.989322       1 main.go:143] received signal terminated

varnish-exporter resource consumption issues

Describe the bug

We are running your latest image as a StatefulSet (without Helm), and manually rebuilt your master-state that includes the varnish-exporter.
We did the following steps to get there:

  • Regular deployment of kube-httpcache(see config below)
  • We pushed a docker image that bundles the varnish exporter, which is a 1:1 copy of your version - also see the Dockerfile below

Everything is working seemingly fine, but the memory and CPU consumption of the exporter-container is steadily rising.
We increased its resource limits several times with no success, after a couple of days it always reaches its limits and will stop working.
It also does not produce any suspicious logs.

CPU Consumption (we had a short metrics outage on the system, and the drop at the end is a restart of the STS):
image

Memory Consumption:
image

Expected behavior
A reasonably constant usage of resources.

Configuration

our StatefulSet-Config:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: varnish
  namespace: gatekeeper
  labels:
    app: varnish
spec:
  serviceName: varnish
  # replicas: <KUSTOMIZE>
  updateStrategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: varnish
  template:
    metadata:
      labels:
        app: varnish
    spec:
      imagePullSecrets:
        - name: staffbase-registry
      containers:
        - name: varnish
          image: quay.io/mittwald/kube-httpcache:v0.4.2
          imagePullPolicy: IfNotPresent
          args:
            - -admin-addr=0.0.0.0
            - -admin-port=6083
            - -signaller-enable
            - -signaller-port=8090
            - -frontend-watch
            - -frontend-namespace=$(NAMESPACE)
            - -frontend-service=varnish
            - -frontend-port=8080
            - -backend-watch
            - -backend-namespace=$(NAMESPACE)
            - -backend-service=frontend-cache-service
            - -varnish-secret-file=/etc/varnish/k8s-secret/secret
            - -varnish-vcl-template=/etc/varnish/tmpl/default.vcl.tmpl
            - -varnish-storage=malloc,256M
          ports:
            - containerPort: 8080
              name: http
            - containerPort: 8090
              name: signaller
          # resources:
          #   requests:
          #     memory: <KUSTOMIZE>
          #     cpu: <KUSTOMIZE>
          #   limits:
          #     memory: <KUSTOMIZE>
          #     cpu: <KUSTOMIZE>
          env:
            - name: NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: CACHE_INFO_HEADERS
              # value: <KUSTOMIZE>
            - name: VSM_NOPID
              value: "true"
          readinessProbe:
            httpGet:
              path: /healthz
              port: http
            initialDelaySeconds: 30
          volumeMounts:
            - name: template
              mountPath: /etc/varnish/tmpl
            - name: secret
              mountPath: /etc/varnish/k8s-secret
            - name: var
              mountPath: /var/lib/varnish
          securityContext:
            runAsUser: 100
            runAsGroup: 101
        - name: exporter
          securityContext:
            runAsUser: 100
            runAsGroup: 101
          image: registry.staffbase.com/private/prometheus_varnish_exporter:1.0.0
          imagePullPolicy: IfNotPresent
          args:
            - -web.health-path=/healthz
            - -no-exit
            - -verbose
          env:
            - name: VSM_NOPID
              value: "true"
          ports:
            - name: metrics
              containerPort: 9131
          resources:
            requests:
              memory: 128Mi
              cpu: 200m
            limits:
              memory: 256Mi
              cpu: 400m
          volumeMounts:
            - name: var
              mountPath: /var/lib/varnish
          readinessProbe:
            httpGet:
              path: /healthz
              port: metrics
            initialDelaySeconds: 30
      serviceAccountName: varnish
      restartPolicy: Always
      volumes:
        - name: template
          configMap:
            name: vcl-template
        - name: secret
          secret:
            secretName: varnish-secret
        - name: var
          emptyDir: { }

The exact docker image we build for the exporter:

FROM        debian:stretch-slim

ENV         EXPORTER_VERSION=1.6

WORKDIR     /
RUN         set -x \
            && \
            apt-get -qq update && apt-get -qq upgrade \
            && \
            apt-get -qq install \
                debian-archive-keyring \
                curl \
                gnupg \
                apt-transport-https \
            && \
            curl -Ss -L https://packagecloud.io/varnishcache/varnish60lts/gpgkey | apt-key add - \
            && \
            printf "%s\n%s" \
                "deb https://packagecloud.io/varnishcache/varnish60lts/debian/ stretch main" \
                "deb-src https://packagecloud.io/varnishcache/varnish60lts/debian/ stretch main" \
            > "/etc/apt/sources.list.d/varnishcache_varnish60lts.list" \
            && \
            apt-get -qq update && apt-get -qq install varnish \
            && \
            apt-get -qq purge curl gnupg apt-transport-https && \
            apt-get -qq autoremove && apt-get -qq autoclean && \
            rm -rf /var/cache/*

RUN         mkdir /exporter && \
            chown varnish /exporter

ADD         --chown=varnish https://github.com/jonnenauha/prometheus_varnish_exporter/releases/download/${EXPORTER_VERSION}/prometheus_varnish_exporter-${EXPORTER_VERSION}.linux-amd64.tar.gz /tmp

RUN         cd /exporter && \
            tar -xzf /tmp/prometheus_varnish_exporter-${EXPORTER_VERSION}.linux-amd64.tar.gz && \
            ln -sf /exporter/prometheus_varnish_exporter-${EXPORTER_VERSION}.linux-amd64/prometheus_varnish_exporter prometheus_varnish_exporter

ENTRYPOINT  [ "/exporter/prometheus_varnish_exporter" ]

Kubernetes Version: 1.18.15

Thanks for looking into this 🤝 🚀

Import modules

Hi,

Hopefully you guys can help or point me in the right direction....

We are looking to use VMOD vsthrottle to take advantage of the rate limiting features. How can we import modules? Is there any way currently to do this?

I've been very impressed with this solution so far, however struggling to see how we can import modules.

Let me know if you need more information.

Thank you,

`docker build` setup is... strange?

ATM, the docker images seem to be built both by quay.io's automatic builds, and goreleaser in the Travis CI pipeline (with the latter one being broken).

@Hermsi1337 since you were the last one to touch this, could you have a look when you find the time?

Several vulnerabilities found with Trivy

Hello,

According to Trivy scanner, there is several vulnerabilities found in the docker image, and some can be fixed with newer version of packages.

quay.io/mittwald/kube-httpcache:stable (debian 9.13)
====================================================
Total: 589 (UNKNOWN: 6, LOW: 29, MEDIUM: 290, HIGH: 229, CRITICAL: 35)

Is there any change to use newer version of golang and debian inside the Dockerfile to reduce as maximum as possible the number of CVE ?

Thanks,
ecniiv

Streamline Dockerfiles & assert that varnish-exporter is present in all images

Currently, we have two different Dockerfiles for this project:

  1. build/package/docker/Dockerfile for "regular" docker build builds
  2. build/package/docker/GoReleaser.Dockerfile for Docker builds triggered by Goreleaser.

These images differ in the features they're offering (for example, the "regular" Dockerfile contains the jonnenauha/prometheus_varnish_exporter binary, whereas the Goreleaser Dockerfile does not, as noted in #81 (comment)) and make any kind of change unnecessarily complex and error prone.

We should investigate if we can use a single Dockerfile for all use-cases.

Readiness Probe

Is your feature request related to a problem? Please describe.
Without a readiness probe, when upgrading, the new servers produce a front facing downtime between the time they first start up, and the time they're ready to serve (and checking that that backend health checks pass).

Describe the solution you'd like
A readiness + liveliness probe setup so that when a rolling update is applied, no downtime can be ensured.

Describe alternatives you've considered
Not alternatives are possible within this context, other than planned downtime schedules to work around this issue.

VCL Template in ConfigMap for Helm Chart

Describe the solution you'd like
I'd like to be able to specify a ConfigMap that the VCL Template is in when installing via the Helm Chart, instead of passing in the whole template text.

Image Scan Failed - Known Vulnerabilities to fix

Hey,

I've just checked over the results for the vulnerability scan, and noticed that rpm and python have known vulnerabilities that are fixable.

image

Any chance we can get some attention on this?

Thanks,
Tony

Purge request has not been distributed among all kube-httpcache pods in StatefulSet

Describe the bug
Purge request has not been distributed among all kube-httpcache pods in StatefulSet. Only 2 pods in StatefulSet are receiving PURGE request and varnishlog is clean on others.

To Reproduce
Steps to reproduce the behavior:

  1. Apply proposed configurations with VCL config for Magento2 (see "Configuration" section)
  2. Scale kube-httpcache StatefulSet with more than 2 replicas: kubectl scale sts cache-statefulset --replicas=4
  3. Configure Magento2 to communicate with cache-service Kubernetes Service
  4. Run magento cache:flush command to send purge request to kube-httpcache
  5. Watch for varnishlog on each pod in StatefulSet

Expected behavior
All pods in k8s kube-httpcache StatefulSet should receive a Purge request

Environment:

  • Kubernetes version: v1.14.10-gke.36
  • kube-httpcache version: v0.3.3

Configuration
VCL template: https://gist.github.com/voxmaster/26d45f370f55b49c9e589604ee82fc14
Logs (pod logs and varnishlog output): https://gist.github.com/voxmaster/196479cca8c894792f1fe52728c03af0

Helm chart not available

Describe the bug
mittwald/kube-httpcache helm chart is not available in the helm repository

To Reproduce
Steps to reproduce the behavior:

  • helm install kube-httpcache mittwald/kube-httpcache
    Error: failed to download "mittwald/kube-httpcache" (hint: running helm repo update may help)

Expected behavior
Helm chart should be installed

Environment:
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.2", GitCommit:"8b5a19147530eaac9476b0ab82980b4088bbc1b2", GitTreeState:"clean", BuildDate:"2021-09-15T21:31:32Z", GoVersion:"go1.16.8", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.4", GitCommit:"3cce4a82b44f032d0cd1a1790e6d2f5a55d20aae", GitTreeState:"clean", BuildDate:"2021-08-11T18:10:22Z", GoVersion:"go1.16.7", Compiler:"gc", Platform:"linux/amd64"}

Add the Helm Chart to a repository

Is your feature request related to a problem? Please describe.
Deploying kube-httpcache with Helm would be a lot easier if the chart was available to download from a repository.

Describe the solution you'd like
For kube-httpcache to be indexed in a repository. Something like https://github.com/helm/chart-releaser could be used to deploy the chart to GitHub Pages.

VLC updated but not working

When I update the configmap with the vcl, I get the event log about the template being updated, thought the server is not using the new configuration.

This is the log, at the end there are some error I haven't being able to identify the cause.

main.go:31] running kube-httpcache with following options: {Kubernetes:{Config: RetryBackoffString:30s RetryBackoff:30s} Frontend:{Address:0.0.0.0 Port:80 Watch:true Namespace:services Service:kube-httpcache-headless PortName:http} Backend:{Watch:true Namespace:surbtc-staging Service:api Port: PortName:http} Signaller:{Enable:true Address:0.0.0.0 Port:8090 WorkersCount:1 MaxRetries:5 RetryBackoffString:30s RetryBackoff:30s} Admin:{Address:0.0.0.0 Port:6083} Varnish:{SecretFile:/etc/varnish/k8s-secret/secret Storage:malloc,128M AdditionalParameters: VCLTemplate:/etc/varnish/tmpl/default.vcl.tmpl VCLTemplatePoll:false} Readiness:{Enable:true Address:0.0.0.0:9102}}
main.go:38] using in-cluster configuration
run.go:15] waiting for initial configuration before starting Varnish
run.go:35] creating initial VCL config
wait.go:12] probing admin port until it is available
Warnings:
VCL compiled.
Debug: Version: varnish-6.0.7 revision 525d371e3ea0e0c38edd7baf0f80dc226560f26e
Debug: Platform: Linux,4.19.112+,x86_64,-junix,-smalloc,-sdefault,-hcritbit
Debug: Child (29) Started
Info: Child (29) said Child starts
dial.go:41] authentication required. challenge string: "pgsijtgprdlexoqsiivwqwpvwdjxbzdk"
wait.go:23] admin port is available
watch.go:35] received new frontend configuration: &{Endpoints:[{Name:varnish-kube-httpcache-0 Host:10.0.0.180 Port:80 Probe:<nil>} {Name:varnish-kube-httpcache-1 Host:10.0.2.217 Port:80 Probe:<nil>}] Primary:0xc000109580}
dial.go:41] authentication required. challenge string: "ioljpurbdczdxndgmdqybujfqptjbaiy"
endpoints_watch.go:58] endpoints did not change
endpoints_watch.go:58] endpoints did not change
endpoints_watch.go:58] endpoints did not change
endpoints_watch.go:58] endpoints did not change
watch.go:35] received new frontend configuration: &{Endpoints:[{Name:varnish-kube-httpcache-1 Host:10.0.2.217 Port:80 Probe:<nil>}] Primary:0xc000049fc0}
dial.go:41] authentication required. challenge string: "ahxktyozmzdprprgaaaocmmcvzjoztyb"
watch.go:35] received new frontend configuration: &{Endpoints:[{Name:varnish-kube-httpcache-0 Host:10.0.0.93 Port:80 Probe:<nil>} {Name:varnish-kube-httpcache-1 Host:10.0.2.217 Port:80 Probe:<nil>}] Primary:0xc000049fc0}
dial.go:41] authentication required. challenge string: "trcmjlnqogtgtigycxibpatjxerxvcim"
endpoints_watch.go:58] endpoints did not change
watch.go:22] VCL template was updated
dial.go:41] authentication required. challenge string: "cowyknlmtzwpggaevjsxqnlgvnkasxqe"
Security: CLI Authentication failure from telnet 127.0.0.1 37614 127.0.0.1 6083
watch.go:22] VCL template was updated
run.go:53] error while watching for updates: response code was 500, expected 200
dial.go:41] authentication required. challenge string: "jznsdmsaticwukxqqoenhcmhnttwcmca"
Security: CLI Authentication failure from telnet 127.0.0.1 37616 127.0.0.1 6083
run.go:53] error while watching for updates: response code was 500, expected 200

Expected behavior
I would expect that after the VCL template was updated logline the server should respond with the new configuration

Environment:

  • Kubernetes version: 1.15
  • kube-httpcache version: latest

error while establishing watch: unknown (get endpoints)

I am seeing error messages when running kube-httpcache in our cluster (everything works on locally though).

2/28/2020 2:37:23 PM I0228 19:37:23.256711       1 main.go:22] running kube-httpcache with following options: {Kubernetes:{Config: RetryBackoffString:30s RetryBackoff:30s} Frontend:{Address:0.0.0.0 Port:80} Backend:{Namespace:on-demand-test Service:varnish-service Port:http} Admin:{Address:0.0.0.0 Port:6083} Varnish:{SecretFile:/etc/varnish/scrt/secret Storage:malloc,192M VCLTemplate:/etc/varnish/tmpl/default.vcl.tmpl VCLTemplatePoll:false}}
2/28/2020 2:37:23 PM I0228 19:37:23.256802       1 main.go:29] using in-cluster configuration
2/28/2020 2:37:23 PM I0228 19:37:23.257551       1 run.go:11] waiting for initial configuration before starting Varnish
2/28/2020 2:37:26 PM E0228 19:37:26.268720       1 backends_watch.go:28] error while establishing watch: unknown (get endpoints)
2/28/2020 2:37:26 PM I0228 19:37:26.268743       1 backends_watch.go:29] retrying after 30s
2/28/2020 2:37:56 PM E0228 19:37:56.269940       1 backends_watch.go:28] error while establishing watch: unknown (get endpoints)
2/28/2020 2:37:56 PM I0228 19:37:56.269957       1 backends_watch.go:29] retrying after 30s

Role:

kubectl describe Role -n on-demand-test kube-httpcache 
Name:         kube-httpcache
Labels:       app=varnish
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"rbac.authorization.k8s.io/v1","kind":"Role","metadata":{"annotations":{},"labels":{"app":"varnish"},"name":"kube-httpcache"...
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  endpoints  []                 []              [watch get]
  pods       []                 []              [watch get]

Role binding:

kubectl describe RoleBinding -n on-demand-test varnish-httpcache 
Name:         varnish-httpcache
Labels:       app=varnish
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"rbac.authorization.k8s.io/v1","kind":"RoleBinding","metadata":{"annotations":{},"labels":{"app":"varnish"},"name":"varnish-...
Role:
  Kind:  Role
  Name:  kube-httpcache
Subjects:
  Kind  Name            Namespace
  ----  ----            ---------
  User  kube-httpcache  

StatefulSet:

kubectl describe StatefulSet -n on-demand-test varnish-statefulset 
Name:               varnish-statefulset
Namespace:          on-demand-test
CreationTimestamp:  Thu, 27 Feb 2020 16:55:39 -0500
Selector:           app=varnish
Labels:             app=varnish
Annotations:        field.cattle.io/publicEndpoints:
                      [{"addresses":["10.0.197.10"],"port":80,"protocol":"HTTP","serviceName":"on-demand-test:varnish-service","ingressName":"on...
                    kubectl.kubernetes.io/last-applied-configuration:
                      {"apiVersion":"apps/v1","kind":"StatefulSet","metadata":{"annotations":{},"labels":{"app":"varnish"},"name":"varnish-statefulset","namespa...
Replicas:           1 desired | 1 total
Update Strategy:    RollingUpdate
Pods Status:        1 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:           app=varnish
  Annotations:      kubectl.kubernetes.io/restartedAt: 2020-02-27T18:02:37-05:00
  Service Account:  kube-httpcache
  Containers:
   varnish:
    Image:     registry.example.org/kube-httpcache
    Port:       <none>
    Host Port:  <none>
    Args:
      -admin-addr=0.0.0.0
      -admin-port=6083
      -backend-namespace=$(NAMESPACE)
      -backend-service=varnish-service
      -backend-port=http
      -varnish-secret-file=/etc/varnish/scrt/secret
      -varnish-vcl-template=/etc/varnish/tmpl/default.vcl.tmpl
      -varnish-storage=malloc,192M
    Limits:
      cpu:     1
      memory:  256Mi
    Requests:
      cpu:     1
      memory:  256Mi
    Environment:
      NAMESPACE:   (v1:metadata.namespace)
    Mounts:
      /etc/varnish/scrt from varnish-credentials (rw)
      /etc/varnish/tmpl from varnish-configmap (rw)
  Volumes:
   varnish-configmap:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      varnish-configmap
    Optional:  false
   varnish-credentials:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  varnish-credentials-7dbfmbb4cc
    Optional:    false
Volume Claims:   <none>
Events:
  Type    Reason            Age                From                    Message
  ----    ------            ----               ----                    -------
  Normal  SuccessfulDelete  46m (x4 over 21h)  statefulset-controller  delete Pod varnish-statefulset-0 in StatefulSet varnish-statefulset successful
  Normal  SuccessfulCreate  46m (x5 over 22h)  statefulset-controller  create Pod varnish-statefulset-0 in StatefulSet varnish-statefulset successful

Any ideas what it could be?

ARM support

Is your feature request related to a problem? Please describe.
ARM support for AWS gravtion2 processors

Describe the solution you'd like
should be able to be achieved within the build pipeline with the following:

docker buildx install
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 . -t quay.io/mittwald/kube-httpcache:v0.5.0 --push

Describe alternatives you've considered
Running different nodes and running kube-httpcache on a separate node group, this isn't ideal.

"Service 'kube-httpcache' has no endpoints" when installing via Helm Chart

Describe the bug
I'm installing kube-httpcache via the Helm chart: helm install kube-httpcache ./chart. However it looks like it has issues starting, showing the error service 'kube-httpcache' has no endpoints:

kubectl logs kube-httpcache-0

I0601 06:34:17.607402       1 main.go:31] running kube-httpcache with following options: {Kubernetes:{Config: RetryBackoffString:30s RetryBackoff:30s} Frontend:{Address:0.0.0.0 Port:80 Watch:true Namespace:default Service:kube-httpcache PortName:http} Backend:{Watch:true Namespace:default Service:backend-service Port: PortName:http} Signaller:{Enable:true Address:0.0.0.0 Port:8090 WorkersCount:1 MaxRetries:5 RetryBackoffString:30s RetryBackoff:30s} Admin:{Address:0.0.0.0 Port:6083} Varnish:{SecretFile:/etc/varnish/k8s-secret/secret Storage:malloc,128M AdditionalParameters: VCLTemplate:/etc/varnish/tmpl/default.vcl.tmpl VCLTemplatePoll:false} Readiness:{Enable:true Address:0.0.0.0:9102}}
I0601 06:34:17.607529       1 main.go:38] using in-cluster configuration
I0601 06:34:17.611383       1 run.go:15] waiting for initial configuration before starting Varnish
W0601 06:34:17.635837       1 endpoints_watch.go:50] service 'kube-httpcache' has no endpoints

While my cluster does have RBAC enabled, it looks like the Helm chart handles setting up the ServiceAccount etc.

To Reproduce
helm install kube-httpcache ./chart --set replicaCount=2
kubectl logs kube-httpcache-0

Expected behavior
Should start without issues.

Environment:

  • Kubernetes version:
Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.7", GitCommit:"1dd5338295409edcfff11505e7bb246f0d325d15", GitTreeState:"clean", BuildDate:"2021-01-13T13:23:52Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.14", GitCommit:"4d62230f10af41cec2851b5b113874f6d2098297", GitTreeState:"clean", BuildDate:"2021-03-22T23:01:55Z", GoVersion:"go1.13.15", Compiler:"gc", Platform:"linux/amd64"}
  • kube-httpcache version: latest on master branch

Configuration
For examples sake I'm using the default vcl, I have the exact same issue when using my own configuration.

Image could not be built

Describe the bug
Not able to build image using Dockerfile provided.

To Reproduce
Steps to reproduce the behavior:

➜  kube-httpcache git:(master) docker build -t test build/package/docker/

[+] Building 28.8s (12/16)                                                                                                             
 => [internal] load build definition from Dockerfile                                                                              0.0s
 => => transferring dockerfile: 2.06kB                                                                                            0.0s
 => [internal] load .dockerignore                                                                                                 0.0s
 => => transferring context: 2B                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/debian:stretch-slim                                                            1.1s
 => [internal] load metadata for docker.io/library/golang:1.14                                                                    1.1s
 => https://github.com/jonnenauha/prometheus_varnish_exporter/releases/download/1.6/prometheus_varnish_exporter-1.6.linux-amd64.  0.0s
 => [final 1/7] FROM docker.io/library/debian:stretch-slim@sha256:828b649e84b713d1b11a1bcf88e832625e99d74aecfeeb5bec1b0846bb40dc  5.1s
 => => resolve docker.io/library/debian:stretch-slim@sha256:828b649e84b713d1b11a1bcf88e832625e99d74aecfeeb5bec1b0846bb40dce1      0.0s
 => => sha256:828b649e84b713d1b11a1bcf88e832625e99d74aecfeeb5bec1b0846bb40dce1 1.21kB / 1.21kB                                    0.0s
 => => sha256:f3bacc9654b39a1aead2b3ec694ad449817b71d53566fd08c4dcd21ac38d12f8 529B / 529B                                        0.0s
 => => sha256:e59fcdf363c23ddb2d02f3297cc4a330f342b9d5da45132a4774494175732b1c 1.46kB / 1.46kB                                    0.0s
 => => sha256:fa1690ae92289fb310aa27b793a164bf8bbedc7ddd00ca079a31bb8bb315b616 22.53MB / 22.53MB                                  3.6s
 => => extracting sha256:fa1690ae92289fb310aa27b793a164bf8bbedc7ddd00ca079a31bb8bb315b616                                         1.4s
 => [internal] load build context                                                                                                 0.0s
 => => transferring context: 3.20kB                                                                                               0.0s
 => [builder 1/4] FROM docker.io/library/golang:1.14@sha256:1a7173b5b9a3af3e29a5837e0b2027e1c438fd1b83bbee8f221355087ad416d6     26.2s
 => => resolve docker.io/library/golang:1.14@sha256:1a7173b5b9a3af3e29a5837e0b2027e1c438fd1b83bbee8f221355087ad416d6              0.0s
 => => sha256:1a7173b5b9a3af3e29a5837e0b2027e1c438fd1b83bbee8f221355087ad416d6 2.36kB / 2.36kB                                    0.0s
 => => sha256:6a39a02f74ffee82a169f2d836134236dc6f69e5946779da13deb3c7c6fedafd 1.79kB / 1.79kB                                    0.0s
 => => sha256:21a5635903d69da3c3d928ed429e3610eecdf878cbd763aabf5db9693016ffbb 7.03kB / 7.03kB                                    0.0s
 => => sha256:0ecb575e629cd60aa802266a3bc6847dcf4073aa2a6d7d43f717dd61e7b90e0b 50.40MB / 50.40MB                                  9.2s
 => => sha256:7467d1831b6947c294d92ee957902c3cd448b17c5ac2103ca5e79d15afb317c3 7.83MB / 7.83MB                                    1.6s
 => => sha256:feab2c490a3cea21cc051ff29c33cc9857418edfa1be9966124b18abe1d5ae16 10.00MB / 10.00MB                                  3.4s
 => => sha256:f15a0f46f8c38f4ca7daecf160ba9cdb3ddeafda769e2741e179851cfaa14eec 51.83MB / 51.83MB                                 10.4s
 => => sha256:1517911a35d7939f446084c1d4c31afc552678e81b3450c2af998b57f72099c2 68.72MB / 68.72MB                                 13.1s
 => => sha256:48bbd1746d63c372e12f884178053851d87f3ea4b415f3d5e7d4eceb9aab6baf 124.14MB / 124.14MB                               19.8s
 => => extracting sha256:0ecb575e629cd60aa802266a3bc6847dcf4073aa2a6d7d43f717dd61e7b90e0b                                         3.0s
 => => sha256:944903612fdd2364b4647cf3c231c41103d1fd378add43fc85f6931f1d6a8276 126B / 126B                                       10.5s
 => => extracting sha256:7467d1831b6947c294d92ee957902c3cd448b17c5ac2103ca5e79d15afb317c3                                         0.4s
 => => extracting sha256:feab2c490a3cea21cc051ff29c33cc9857418edfa1be9966124b18abe1d5ae16                                         0.3s
 => => extracting sha256:f15a0f46f8c38f4ca7daecf160ba9cdb3ddeafda769e2741e179851cfaa14eec                                         3.2s
 => => extracting sha256:1517911a35d7939f446084c1d4c31afc552678e81b3450c2af998b57f72099c2                                         3.5s
 => => extracting sha256:48bbd1746d63c372e12f884178053851d87f3ea4b415f3d5e7d4eceb9aab6baf                                         5.5s
 => => extracting sha256:944903612fdd2364b4647cf3c231c41103d1fd378add43fc85f6931f1d6a8276                                         0.0s
 => CANCELED [final 2/7] RUN         set -x             &&             apt-get -qq update && apt-get -qq upgrade             &&  22.5s
 => [builder 2/4] WORKDIR     /workspace                                                                                          1.0s
 => [builder 3/4] COPY        . .                                                                                                 0.0s
 => ERROR [builder 4/4] RUN         CGO_ENABLED=0 GOOS=linux             go build                 -installsuffix cgo              0.3s
------                                                                                                                                 
 > [builder 4/4] RUN         CGO_ENABLED=0 GOOS=linux             go build                 -installsuffix cgo                 -o kube-httpcache                 -a cmd/kube-httpcache/main.go:                                                                                
#15 0.266 can't load package: package cmd/kube-httpcache/main.go: cannot find package "cmd/kube-httpcache/main.go" in any of:          
#15 0.266       /usr/local/go/src/cmd/kube-httpcache/main.go (from $GOROOT)                                                            
#15 0.266       /go/src/cmd/kube-httpcache/main.go (from $GOPATH)                                                                      
------
executor failed running [/bin/sh -c CGO_ENABLED=0 GOOS=linux             go build                 -installsuffix cgo                 -o kube-httpcache                 -a cmd/kube-httpcache/main.go]: exit code: 1

Expected behavior
Image should be built.

Environment:

  • kube-httpcache version: master

Support for ExternalName services

Is your feature request related to a problem? Please describe.
I am trying to setup Varnish for caching calls in between a pod in k8s and an external service which is called via ExternalName service so it doesn't have an Endpoint. Your solutions requires specifying service name and is looking for Endpoints for that Service so I guess it won't work with ExternalName.

Describe the solution you'd like
The best would be to make native support for ExternalName services. So it should detect is Service is type of ExternalName and if it is then don't try to find it's Endpoint but use External-IP

Describe alternatives you've considered
I would be happy to hardcode backend for now. Please explain if I can just put it somewhere in vcl template or so?

Additional context

vmod-querystring

Is your feature request related to a problem? Please describe.
I would like to use vmod-querystring to easier remove marketing query params from incoming URLs.

It would be nice to be able to do this:

import std;
import querystring;

sub vcl_init {
    new tracking_params_filter = querystring.filter();
    tracking_params_filter.add_string("gclid");
    tracking_params_filter.add_glob("utm_*");

}

sub vcl_recv {
    std.log("tracking_params_filter:" + tracking_params_filter.extract(req.url, mode = keep));
    set req.url = tracking_params_filter.apply(req.url);
}

I also read it takes less memory than doing it manually with regsuball

Describe the solution you'd like
I'd like vmod-querystring to be bundled with this project

Describe alternatives you've considered
Currently I'm having a lot of regexps in my VCL-file

arm64 image is built with amd64 exporter

This is a follow-up to #101.

The arm64v8 image is built with an amd64 build of the varnish-exporter binary, which is a bad idea for obvious reasons.

Apparently, the upstream project does not release an amd64 build that we could just use out of the box, so we'll need to think of something. I remember we'd already discussed forking and building the exporter by ourselves in #81, so maybe that thought needs to be picked up again.

Provide another Dockerfile to build image not using Gorelease.

Is there a way we can have two Dockerfiles - one that relies on Gorelease and one that does not. Here is a use case: I build images by myself, I also use Tilt system for local development. Unfortunately Tilt only supports docker build command for building images and is not compatible with Gorelease:

docker_build(
  "kube-httpcache",
  "kube-httpcache",
  dockerfile="kube-httpcache/Dockerfile",
  ignore=[
    '.git',
    'README.md',
  ]
)

Another use case is building image from Docker compose.

I am not very familiar with Goreleaser, so I don't know if there are any workarounds, simply running docker build produces following error:

Step 7/8 : COPY        kube-httpcache /usr/bin/kube-httpcache
COPY failed: stat /var/lib/docker/tmp/docker-builder373320276/kube-httpcache: no such file or directory

Originally posted by @dealancer in #21 (comment)

Secret volume mount does not work

I am seeing this error message:

Error: failed to start container "varnish": Error response from daemon: OCI runtime create failed: container_linux.go:346: starting container process caused "process_linux.go:449: container init caused \"rootfs_linux.go:58: mounting \\\"/var/lib/kubelet/pods/91577ebe-37e6-11ea-803a-025000000001/volumes/kubernetes.io~secret/secret\\\" to rootfs \\\"/var/lib/docker/overlay2/ebb6aca1feaa9bc32308fb66911c9119e7b82492ab6daf5d1c7e5279c9cc1776/merged\\\" at \\\"/var/lib/docker/overlay2/ebb6aca1feaa9bc32308fb66911c9119e7b82492ab6daf5d1c7e5279c9cc1776/merged/etc/varnish/secret\\\" caused \\\"not a directory\\\"\"": unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type

Looks like something is wrong with secret volume mount. Any ideas how to fix it?

fatal error: concurrent map read and map write

Describe the bug
Pod crashes and when I check the logs the first error is this:

fatal error: concurrent map read and map write

goroutine 9 [running]:
runtime.throw(0x115bcf3, 0x21)
	/opt/hostedtoolcache/go/1.14.7/x64/src/runtime/panic.go:1116 +0x72 fp=0xc002ec94e0 sp=0xc002ec94b0 pc=0x432f92
runtime.mapaccess1_faststr(0x1096d20, 0xc0035e9440, 0x114b2bc, 0xf, 0x11c8ce8)
	/opt/hostedtoolcache/go/1.14.7/x64/src/runtime/map_faststr.go:21 +0x43c fp=0xc002ec9550 sp=0xc002ec94e0 pc=0x41277c
net/textproto.MIMEHeader.Get(0xc0035e9440, 0x114b2bc, 0xf, 0xc000001a01, 0xc002ec97c8)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/textproto/header.go:34 +0x5d fp=0xc002ec9588 sp=0xc002ec9550 pc=0x7606ed
net/http.Header.Get(...)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/header.go:46
net/http.(*persistConn).roundTrip(0xc003a9bb00, 0xc0035e97d0, 0x0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:2438 +0xdf2 fp=0xc002ec9898 sp=0xc002ec9588 pc=0x7f4542
net/http.(*Transport).roundTrip(0x1a5a240, 0xc003835000, 0xc0002b3880, 0xc002ec9b28, 0x40ca38)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:565 +0xad9 fp=0xc002ec9ae0 sp=0xc002ec9898 pc=0x7e8c69
net/http.(*Transport).RoundTrip(0x1a5a240, 0xc003835000, 0x1a5a240, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/roundtrip.go:17 +0x35 fp=0xc002ec9b18 sp=0xc002ec9ae0 pc=0x7cf395
net/http.send(0xc003835000, 0x12ad580, 0x1a5a240, 0x0, 0x0, 0x0, 0xc000764ed8, 0x0, 0x1, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/client.go:252 +0x43e fp=0xc002ec9c80 sp=0xc002ec9b18 pc=0x79251e
net/http.(*Client).send(0xc0003087e0, 0xc003835000, 0x0, 0x0, 0x0, 0xc000764ed8, 0x0, 0x1, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/client.go:176 +0xfa fp=0xc002ec9d00 sp=0xc002ec9c80 pc=0x791f3a
net/http.(*Client).do(0xc0003087e0, 0xc003835000, 0x0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/client.go:699 +0x44a fp=0xc002ec9ed8 sp=0xc002ec9d00 pc=0x793c0a
net/http.(*Client).Do(...)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/client.go:567
github.com/mittwald/kube-httpcache/pkg/signaller.(*Signaller).ProcessSignalQueue(0xc00029efc0)
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/signaller/run.go:59 +0xc6 fp=0xc002ec9fd8 sp=0xc002ec9ed8 pc=0xef9696
runtime.goexit()
	/opt/hostedtoolcache/go/1.14.7/x64/src/runtime/asm_amd64.s:1373 +0x1 fp=0xc002ec9fe0 sp=0xc002ec9fd8 pc=0x462b41
created by github.com/mittwald/kube-httpcache/pkg/signaller.(*Signaller).Run
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/signaller/run.go:21 +0x11c

goroutine 1 [chan receive, 347 minutes]:
github.com/mittwald/kube-httpcache/pkg/controller.(*VarnishController).Run(0xc00012e2a0, 0x12d7240, 0xc00028f5c0, 0xc00029d710, 0xc00029d710)
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/controller/run.go:58 +0x39d
main.main()
	/home/runner/work/kube-httpcache/kube-httpcache/cmd/kube-httpcache/main.go:147 +0x562

goroutine 18 [chan receive, 2 minutes]:
github.com/golang/glog.(*loggingT).flushDaemon(0x1a644c0)
	/home/runner/go/pkg/mod/github.com/golang/[email protected]/glog.go:882 +0x8b
created by github.com/golang/glog.init.0
	/home/runner/go/pkg/mod/github.com/golang/[email protected]/glog.go:410 +0x26f

goroutine 19 [chan receive, 45 minutes]:
github.com/mittwald/kube-httpcache/pkg/watcher.(*EndpointWatcher).watch(0xc000285a40, 0xc000102360, 0xc0001023c0)
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/watcher/endpoints_watch.go:37 +0x375
created by github.com/mittwald/kube-httpcache/pkg/watcher.(*EndpointWatcher).Run
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/watcher/endpoints_watch.go:17 +0x98

goroutine 20 [chan receive, 34 minutes]:
github.com/mittwald/kube-httpcache/pkg/watcher.(*EndpointWatcher).watch(0xc000285a90, 0xc000102420, 0xc000102480)
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/watcher/endpoints_watch.go:37 +0x375
created by github.com/mittwald/kube-httpcache/pkg/watcher.(*EndpointWatcher).Run
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/watcher/endpoints_watch.go:17 +0x98

goroutine 21 [syscall, 347 minutes]:
syscall.Syscall6(0xe8, 0x7, 0xc00040fb74, 0x7, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/syscall/asm_linux_amd64.s:41 +0x5
golang.org/x/sys/unix.EpollWait(0x7, 0xc00040fb74, 0x7, 0x7, 0xffffffffffffffff, 0x0, 0x0, 0x0)
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/unix/zsyscall_linux_amd64.go:1641 +0x72
github.com/fsnotify/fsnotify.(*fdPoller).wait(0xc0001338c0, 0x0, 0x0, 0x0)
	/home/runner/go/pkg/mod/github.com/fsnotify/[email protected]/inotify_poller.go:86 +0x91
github.com/fsnotify/fsnotify.(*Watcher).readEvents(0xc000285ae0)
	/home/runner/go/pkg/mod/github.com/fsnotify/[email protected]/inotify.go:192 +0x1f8
created by github.com/fsnotify/fsnotify.NewWatcher
	/home/runner/go/pkg/mod/github.com/fsnotify/[email protected]/inotify.go:59 +0x1a5

goroutine 22 [chan receive, 347 minutes]:
github.com/mittwald/kube-httpcache/pkg/watcher.(*fsnotifyTemplateWatcher).watch(0xc0002be8a0, 0xc000102660, 0xc0001026c0)
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/watcher/template_watch.go:18 +0x76
created by github.com/mittwald/kube-httpcache/pkg/watcher.(*fsnotifyTemplateWatcher).Run
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/watcher/template_watch.go:12 +0x98

goroutine 23 [IO wait]:
internal/poll.runtime_pollWait(0x7f4127306d28, 0x72, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/runtime/netpoll.go:203 +0x55
internal/poll.(*pollDesc).wait(0xc00030c698, 0x72, 0x0, 0x0, 0x1145bf1)
	/opt/hostedtoolcache/go/1.14.7/x64/src/internal/poll/fd_poll_runtime.go:87 +0x45
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.14.7/x64/src/internal/poll/fd_poll_runtime.go:92
internal/poll.(*FD).Accept(0xc00030c680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/internal/poll/fd_unix.go:384 +0x1d4
net.(*netFD).accept(0xc00030c680, 0xa7e873307a7c1c8b, 0x0, 0xa7e873307a7c1c8b)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/fd_unix.go:238 +0x42
net.(*TCPListener).accept(0xc00000c480, 0x5fbde082, 0xc00010ed90, 0x487636)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/tcpsock_posix.go:139 +0x32
net.(*TCPListener).Accept(0xc00000c480, 0xc00010ede0, 0x18, 0xc000270f00, 0x7db84c)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/tcpsock.go:261 +0x64
net/http.(*Server).Serve(0xc00003a0e0, 0x12d5040, 0xc00000c480, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/server.go:2930 +0x25d
net/http.(*Server).ListenAndServe(0xc00003a0e0, 0x11c7ca0, 0xc00029efc0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/server.go:2859 +0xb7
github.com/mittwald/kube-httpcache/pkg/signaller.(*Signaller).Run(0xc00029efc0, 0x0, 0x0)
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/signaller/run.go:24 +0x13e
main.main.func1(0xc00029efc0, 0xc000279f30)
	/home/runner/work/kube-httpcache/kube-httpcache/cmd/kube-httpcache/main.go:93 +0x2b
created by main.main
	/home/runner/work/kube-httpcache/kube-httpcache/cmd/kube-httpcache/main.go:92 +0x792

goroutine 24 [select, 347 minutes]:
main.main.func2(0xc0001023c0, 0xc000102480, 0xc0001026c0, 0xc000102780)
	/home/runner/work/kube-httpcache/kube-httpcache/cmd/kube-httpcache/main.go:102 +0x145
created by main.main
	/home/runner/work/kube-httpcache/kube-httpcache/cmd/kube-httpcache/main.go:100 +0x2c7

goroutine 26 [syscall, 347 minutes]:
os/signal.signal_recv(0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/runtime/sigqueue.go:147 +0x9c
os/signal.loop()
	/opt/hostedtoolcache/go/1.14.7/x64/src/os/signal/signal_unix.go:23 +0x22
created by os/signal.Notify.func1
	/opt/hostedtoolcache/go/1.14.7/x64/src/os/signal/signal.go:127 +0x44

goroutine 6264 [select, 288 minutes]:
net/http.(*persistConn).readLoop(0xc001579440)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:2099 +0x99e
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:1647 +0xc56

goroutine 71 [select, 213 minutes]:
github.com/mittwald/kube-httpcache/pkg/controller.(*VarnishController).watchConfigUpdates(0xc00012e2a0, 0x12d7240, 0xc00028f5c0, 0xc00026e6e0, 0xc000044300)
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/controller/watch.go:20 +0x199
created by github.com/mittwald/kube-httpcache/pkg/controller.(*VarnishController).Run
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/controller/run.go:48 +0x355

goroutine 69 [select, 347 minutes]:
net/http.(*persistConn).writeLoop(0xc0001ef200)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:2277 +0x11c
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:1648 +0xc7b

goroutine 33 [chan receive, 347 minutes]:
main.main.func3(0xc0002da540, 0xc00029d710)
	/home/runner/work/kube-httpcache/kube-httpcache/cmd/kube-httpcache/main.go:141 +0x41
created by main.main
	/home/runner/work/kube-httpcache/kube-httpcache/cmd/kube-httpcache/main.go:140 +0x537

goroutine 30 [IO wait, 34 minutes]:
internal/poll.runtime_pollWait(0x7f4127306e08, 0x72, 0xffffffffffffffff)
	/opt/hostedtoolcache/go/1.14.7/x64/src/runtime/netpoll.go:203 +0x55
internal/poll.(*pollDesc).wait(0xc00030c618, 0x72, 0x3800, 0x38b1, 0xffffffffffffffff)
	/opt/hostedtoolcache/go/1.14.7/x64/src/internal/poll/fd_poll_runtime.go:87 +0x45
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.14.7/x64/src/internal/poll/fd_poll_runtime.go:92
internal/poll.(*FD).Read(0xc00030c600, 0xc00049a000, 0x38b1, 0x38b1, 0x0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/internal/poll/fd_unix.go:169 +0x19b
net.(*netFD).Read(0xc00030c600, 0xc00049a000, 0x38b1, 0x38b1, 0x203000, 0x7f414dfddd30, 0xc8)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/fd_unix.go:202 +0x4f
net.(*conn).Read(0xc00000e050, 0xc00049a000, 0x38b1, 0x38b1, 0x0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/net.go:184 +0x8e
crypto/tls.(*atLeastReader).Read(0xc000561900, 0xc00049a000, 0x38b1, 0x38b1, 0x66b, 0x38ac, 0xc0004a19c8)
	/opt/hostedtoolcache/go/1.14.7/x64/src/crypto/tls/conn.go:760 +0x60
bytes.(*Buffer).ReadFrom(0xc00003d058, 0x12ac260, 0xc000561900, 0x40a115, 0x1029d60, 0x1105ec0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/bytes/buffer.go:204 +0xb1
crypto/tls.(*Conn).readFromUntil(0xc00003ce00, 0x12ad4c0, 0xc00000e050, 0x5, 0xc00000e050, 0x8)
	/opt/hostedtoolcache/go/1.14.7/x64/src/crypto/tls/conn.go:782 +0xec
crypto/tls.(*Conn).readRecordOrCCS(0xc00003ce00, 0x0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/crypto/tls/conn.go:589 +0x115
crypto/tls.(*Conn).readRecord(...)
	/opt/hostedtoolcache/go/1.14.7/x64/src/crypto/tls/conn.go:557
crypto/tls.(*Conn).Read(0xc00003ce00, 0xc00042d000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/crypto/tls/conn.go:1233 +0x15b
bufio.(*Reader).Read(0xc00042a5a0, 0xc00012e498, 0x9, 0x9, 0x46c70c, 0xc000158040, 0x651)
	/opt/hostedtoolcache/go/1.14.7/x64/src/bufio/bufio.go:226 +0x24f
io.ReadAtLeast(0x12ac120, 0xc00042a5a0, 0xc00012e498, 0x9, 0x9, 0x9, 0xc000158030, 0x11c8ce0, 0xc000158028)
	/opt/hostedtoolcache/go/1.14.7/x64/src/io/io.go:310 +0x87
io.ReadFull(...)
	/opt/hostedtoolcache/go/1.14.7/x64/src/io/io.go:329
golang.org/x/net/http2.readFrameHeader(0xc00012e498, 0x9, 0x9, 0x12ac120, 0xc00042a5a0, 0x0, 0x0, 0xc002b2c0f0, 0x0)
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/http2/frame.go:237 +0x87
golang.org/x/net/http2.(*Framer).ReadFrame(0xc00012e460, 0xc002b2c0f0, 0x0, 0x0, 0x0)
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/http2/frame.go:492 +0xa1
golang.org/x/net/http2.(*clientConnReadLoop).run(0xc0004a1fa8, 0xc000060fb0, 0x7fa872)
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/http2/transport.go:1616 +0x8d
golang.org/x/net/http2.(*ClientConn).readLoop(0xc000271c80)
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/http2/transport.go:1544 +0x6f
created by golang.org/x/net/http2.(*Transport).newClientConn
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/http2/transport.go:619 +0x64a

goroutine 4443 [select, 315 minutes]:
net/http.(*persistConn).writeLoop(0xc00132f560)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:2277 +0x11c
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:1648 +0xc7b

goroutine 154 [select, 345 minutes]:
net/http.(*persistConn).writeLoop(0xc00013a5a0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:2277 +0x11c
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:1648 +0xc7b

goroutine 54 [syscall, 347 minutes]:
syscall.Syscall6(0xf7, 0x1, 0x13, 0xc000112de0, 0x1000004, 0x0, 0x0, 0x4d2603, 0xc00042a300, 0xc000112e20)
	/opt/hostedtoolcache/go/1.14.7/x64/src/syscall/asm_linux_amd64.s:41 +0x5
os.(*Process).blockUntilWaitable(0xc0005ac990, 0x0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/os/wait_waitid.go:31 +0x98
os.(*Process).wait(0xc0005ac990, 0x11c8880, 0x11c8888, 0x11c8878)
	/opt/hostedtoolcache/go/1.14.7/x64/src/os/exec_unix.go:22 +0x39
os.(*Process).Wait(...)
	/opt/hostedtoolcache/go/1.14.7/x64/src/os/exec.go:125
os/exec.(*Cmd).Wait(0xc00026e6e0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/os/exec/exec.go:507 +0x60
os/exec.(*Cmd).Run(0xc00026e6e0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/os/exec/exec.go:341 +0x5c
github.com/mittwald/kube-httpcache/pkg/controller.(*VarnishController).startVarnish.func1(0xc00026e6e0, 0xc0005ae4e0)
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/controller/run.go:75 +0x2b
created by github.com/mittwald/kube-httpcache/pkg/controller.(*VarnishController).startVarnish
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/controller/run.go:74 +0x149

goroutine 55 [select, 347 minutes]:
os/exec.(*Cmd).Start.func2(0xc00026e6e0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/os/exec/exec.go:449 +0xc4
created by os/exec.(*Cmd).Start
	/opt/hostedtoolcache/go/1.14.7/x64/src/os/exec/exec.go:448 +0x6d0

goroutine 216 [select, 347 minutes]:
net/http.(*persistConn).writeLoop(0xc0001a0c60)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:2277 +0x11c
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:1648 +0xc7b

goroutine 72 [chan receive, 213 minutes]:
github.com/mittwald/kube-httpcache/pkg/controller.(*VarnishController).Run.func1(0xc000044300)
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/controller/run.go:51 +0x41
created by github.com/mittwald/kube-httpcache/pkg/controller.(*VarnishController).Run
	/home/runner/work/kube-httpcache/kube-httpcache/pkg/controller/run.go:50 +0x377

Then it's about 2MB of spam and finally:

goroutine 15124 [IO wait]:
internal/poll.runtime_pollWait(0x7f4126fe51d0, 0x72, 0xffffffffffffffff)
	/opt/hostedtoolcache/go/1.14.7/x64/src/runtime/netpoll.go:203 +0x55
internal/poll.(*pollDesc).wait(0xc003a81198, 0x72, 0x1000, 0x1000, 0xffffffffffffffff)
	/opt/hostedtoolcache/go/1.14.7/x64/src/internal/poll/fd_poll_runtime.go:87 +0x45
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.14.7/x64/src/internal/poll/fd_poll_runtime.go:92
internal/poll.(*FD).Read(0xc003a81180, 0xc003b92000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/internal/poll/fd_unix.go:169 +0x19b
net.(*netFD).Read(0xc003a81180, 0xc003b92000, 0x1000, 0x1000, 0x10, 0x45de00, 0xc00385b380)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/fd_unix.go:202 +0x4f
net.(*conn).Read(0xc000764ee0, 0xc003b92000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/net.go:184 +0x8e
net/http.(*persistConn).Read(0xc003a9bb00, 0xc003b92000, 0x1000, 0x1000, 0x404fad, 0x60, 0x0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:1825 +0x75
bufio.(*Reader).fill(0xc0039db2c0)
	/opt/hostedtoolcache/go/1.14.7/x64/src/bufio/bufio.go:100 +0x103
bufio.(*Reader).Peek(0xc0039db2c0, 0x1, 0xc003a95380, 0x40c1c6, 0xc0039db200, 0x60, 0x60)
	/opt/hostedtoolcache/go/1.14.7/x64/src/bufio/bufio.go:138 +0x4f
net/http.(*persistConn).readLoop(0xc003a9bb00)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:1978 +0x1a8
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:1647 +0xc56

goroutine 15125 [runnable]:
net/http.(*persistConn).writeLoop(0xc003a9bb00)
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:2274
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.14.7/x64/src/net/http/transport.go:1648 +0xc7b

I couldn't add the whole log but let me know if you need it and I can upload it somewhere

This locks up my whole site until i do kubectl delete pod --force.

To Reproduce
I'm not really sure why this happens. This happened first now during Black Week and with increased traffic.

Expected behavior
Either for this not to happen or for the process to recover from it.

Environment:

  • Kubernetes version: 1.17
  • kube-httpcache version: stable (how can I check exactly?)

Configuration

varnish.vcl
vcl 4.0;

import std;
# The minimal Varnish version is 4.0
# For SSL offloading, pass the following header in your proxy server or load balancer: 'X-Forwarded-Proto: https'

backend default {
    .host = "localhost";
    .port = "8080";
    .first_byte_timeout = 600s;
    .probe = {
        .url = "/pub/health_check.php";
        .timeout = 2s;
        .interval = 5s;
        .window = 10;
        .threshold = 5;
   }
}

acl purge {
    "localhost";
}

sub vcl_recv {
    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") {
        return (pass);
    }

    # Bypass health check requests
    if (req.url ~ "/pub/health_check.php") {
        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 {
            # unknown 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"

    # Static files caching
    if (req.url ~ "^/(pub/)?(media|static)/") {
        # 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
    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 !~ "^/(pub/)?(media|static)/") {
        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-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 (fetch);
        }
    } else {
        # server is not healthy, retrieve from cache
        set req.http.grace = "unlimited (unhealthy server)";
        return (deliver);
    }
}

StatefulSet args:

args: [
  '-admin-addr=0.0.0.0',
  '-admin-port=6083',
  '-signaller-enable',
  '-signaller-port=8090',
  '-frontend-watch',
  '-frontend-namespace=$(NAMESPACE)',
  '-frontend-service=varnish-service',
  '-backend-watch',
  '-backend-namespace=$(NAMESPACE)',
  '-backend-service=$(BACKEND)',
  '-varnish-secret-file=/etc/varnish/k8s-secret/secret',
  '-varnish-vcl-template=/etc/varnish/tmpl/default.vcl.tmpl',
  '-varnish-storage=malloc,512M',
],

Pod Crashing

Describe the bug

I1009 07:50:55.018020       1 watch.go:46] received new backend configuration: &{Endpoints:[{Name:application-deployment-756ccbd74d-56pbb Host:10.100.100.101 Port:80 Probe:<nil>} {Name:application-deployment-756ccbd74d-mjrg9 Host:10.100.102.89 Port:80 Probe:} {Name:application-deployment-756ccbd74d-72g7p Host:10.100.103.154 Port:80 Probe:} {Name:application-deployment-756ccbd74d-mc2ls Host:10.100.98.162 Port:80 Probe:} {Name:application-deployment-84f7849d6c-2kltp Host:10.100.99.187 Port:80 Probe:}] Primary:0xc000117680}
W1009 07:50:55.018361       1 run.go:53] error while watching for updates: context canceled
W1009 07:50:55.018673       1 run.go:53] error while watching for updates: context canceled
W1009 07:50:55.019120       1 run.go:53] error while watching for updates: context canceled
panic: signal: killed
goroutine 1 [running]:
main.main()
	/home/runner/work/kube-httpcache/kube-httpcache/cmd/kube-httpcache/main.go:149 +0xd12

To Reproduce
Steps to reproduce the behavior:
add in liveness probe, restricted CPU to 100m, and memory to 128mib

Expected behavior
A clear and concise description of what you expected to happen.

Environment:

  • Kubernetes version: 1.16
  • kube-httpcache version: v0.3.1

Configuration
If applicable, add your VCL configuration file and other relevant configuration settings

Additional context
Add any other context about the problem here.

Get the environment variables to the template parsing

Is your feature request related to a problem? Please describe.

In some cases we need external services where the Host-header must be correct. At the moment there isn't way to do that. By default the Varnish uses the same Host-header which it was called with. So that header must be different in different environments. It would simplify life a lot if I could use environment variables for that.

So we'd had at template:
set req.http.Host = "{{ .Env.CORRECT_EXT_HOST }}";

And at the Helm template I'd had environment variables defined correctly. This is not the only place where I'd use it. I've got some special requirements for the URL rewriting. This would help also at that.

Describe alternatives you've considered

Additional solution would be the script which pre-parses the template, but then we'd have too many levels of parsing. It would be error prone and waste of time to maintain.

How does it work with sticky sessions in AWS ALB Ingress

Just wonder how it works with ALB Ingress with sticky sessions enabled in ip mode - in that case requests go directly to pods. How Varnish can be used here? The ingress backend should point to varnish instead? And VCL should be defined to enable sticky sessions too?

Unable to start new statefulset due to readiness probe

Describe the bug
When setting a Kubernetes readiness probe, Varnish will never start when creating a new stateful set or the pod count goes to 0. The first pod will wait for the front end service to get a ready endpoint, but the ready endpoint depends on the server being active.

The readiness probe will work properly if you disable it on the first deploy, then enable it after that. The stateful set will however get into a bad state if all pods are restarted.

My settings could be wrong, but my understanding is that frontend-service should be set to the service in front of the Varnish cache?

W0824 17:09:56.720436       1 endpoints_watch.go:50] service 'api-varnish' has no endpoints

To Reproduce
Steps to reproduce the behavior:

  1. Create a new statefulset with a liveness and readiness probe:
    livenessProbe:
      failureThreshold: 3
      httpGet:
        path: /
        port: 9102
        scheme: HTTP
      initialDelaySeconds: 30
      periodSeconds: 10
      successThreshold: 1
      timeoutSeconds: 5
    readinessProbe:
      failureThreshold: 3
      httpGet:
        path: /
        port: 9102
        scheme: HTTP
      initialDelaySeconds: 5
      periodSeconds: 10
      successThreshold: 1
      timeoutSeconds: 5
  1. The first pod will never become ready because it is watching its own frontend service and waiting for it to be active.

Expected behavior
On first deploy or when pod count goes to 0, the first pod should become ready by either ignoring the front end service check or skipping it. This would allow subsequent pods to start up

Environment:

  • Kubernetes version: 1.16
  • kube-httpcache version: v0.3.2 (Though this issue existed pre 0.3.2 if you set the readiness probe to port 80)

Dynamic backend generation is not working.

Describe the bug
Dynamic backend generation is not working

To Reproduce
Steps to reproduce the behavior:

  1. use a large vcl template (>1MB , provided as volume)
  2. respawn/recreate multiple (example 4 backends at once
  3. old PODs will not be removed from backend
  4. new PODs will not be added to backend

Expected behavior
kube-httpcache adds/removes backends

Environment:

  • Kubernetes version: 1.18
  • kube-httpcache version: [latest master]

Logs

panic: runtime error: index out of range [0] with length 0

goroutine 8 [running]:
github.com/mittwald/kube-httpcache/pkg/watcher.(*EndpointWatcher).watch(0xc0002e5a90, 0xc00004a480, 0xc00004a4e0)
	/workspace/pkg/watcher/endpoints_watch.go:93 +0xf1d
created by github.com/mittwald/kube-httpcache/pkg/watcher.(*EndpointWatcher).Run
	/workspace/pkg/watcher/endpoints_watch.go:17 +0x98

Or

W0219 16:27:58.037023       1 run.go:53] error while watching for updates: error while loading VCL (code 106): Message from C-compiler:
gcc: internal compiler error: Killed (program cc1)
Please submit a full bug report,
with preprocessed source if appropriate.
See <file:///usr/share/doc/gcc-6/README.Bugs> for instructions.
Running C-compiler failed, exited with 4
VCL compilation failed

Varnish responds with 503 after a few hours

Describe the bug
Having Varnish and the backend (Magento 2.3.5) running for a few hours, after a while, Varnish responds with 503.

To Reproduce
Steps to reproduce the behavior:

  1. Deploy Magento 2.3.5 from Bitnami Helm Charts.
  2. Deploy Varnish as follows:
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: magento-varnish
  namespace: production
  labels:
    app: cache
spec:
  serviceName: magento-varnish
  replicas: 1
  updateStrategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: cache
  template:
    metadata:
      labels:
        app: cache
    spec:
      containers:
        - name: cache
          image: quay.io/mittwald/kube-httpcache:stable
          imagePullPolicy: Always
          args:
            - -admin-addr=0.0.0.0
            - -admin-port=6083
            - -signaller-enable=true
            - -signaller-port=8090
            - -frontend-watch=false
            - -frontend-namespace=$(NAMESPACE)
            - -frontend-service=magento-varnish
            - -backend-watch=false
            - -backend-namespace=$(NAMESPACE)
            - -backend-service=magento
            - -varnish-secret-file=/etc/varnish/k8s-secret/secret
            - -varnish-vcl-template=/etc/varnish/tmpl/default.vcl.tmpl
            - -varnish-storage=malloc,512M
            - -varnish-additional-parameters=http_resp_size=98304
          env:
            - name: NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          volumeMounts:
            - name: template
              mountPath: /etc/varnish/tmpl
            - name: secret
              mountPath: /etc/varnish/k8s-secret
          readinessProbe:
            httpGet:
              port: 80
              httpHeaders:
                - name: Host
                  value: example.com
            initialDelaySeconds: 10
            periodSeconds: 5
          livenessProbe:
            httpGet:
              port: 80
              httpHeaders:
                - name: Host
                  value: example.com
            initialDelaySeconds: 10
            periodSeconds: 5
      serviceAccountName: magento-varnish
      restartPolicy: Always
      volumes:
        - name: template
          configMap:
            name: magento-varnish-configuration
        - name: secret
          secret:
            secretName: magento-varnish
apiVersion: v1
kind: ConfigMap
metadata:
  name: magento-varnish-configuration
  namespace: production
data:
  default.vcl.tmpl: |
    # VCL version 5.0 is not supported so it should be 4.0 even though actually used Varnish version is 6
    vcl 4.0;

    import std;
    # The minimal Varnish version is 6.0
    # For SSL offloading, pass the following header in your proxy server or load balancer: 'X-Forwarded-Proto: https'

    backend default {
        .host = "magento";
        .port = "80";
        .connect_timeout = 5s;
        .first_byte_timeout = 10s;
        .between_bytes_timeout = 10s;
        .probe = {
            .url = "/pub/health_check.php";
            .timeout = 2s;
            .interval = 10s;
            .window = 50;
            .threshold = 5;
        }
    }

    acl purge {
        "localhost";
    }

    sub vcl_recv {
        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") {
            return (pass);
        }

        # Bypass health check requests
        if (req.url ~ "/pub/health_check.php") {
            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 {
                # unknown algorithm
                unset req.http.Accept-Encoding;
            }
        }

        # Remove all marketing get parameters to minimize the cache objects
        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, "[?|&]+$", "");
        }

        # Static files caching
        if (req.url ~ "^/(pub/)?(media|static)/") {
            # 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);
        }


        if (req.url ~ "/graphql") {
            call process_graphql_headers;
        }
    }

    sub process_graphql_headers {
        if (req.http.Store) {
            hash_data(req.http.Store);
        }
        if (req.http.Content-Currency) {
            hash_data(req.http.Content-Currency);
        }
    }

    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
        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 !~ "^/(pub/)?(media|static)/") {
            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-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 (restart);
            }
        } else {
            # server is not healthy, retrieve from cache
            set req.http.grace = "unlimited (unhealthy server)";
            return (deliver);
        }
    }

Both backend and Varnish are running just fine - it seems:

I0720 09:53:18.226042       1 main.go:31] running kube-httpcache with following options: {Kubernetes:{Config: RetryBackoffString:30s RetryBackoff:30s} Frontend:{Address:0.0.0.0 Port:80 Watch:false Namespace:production Service:magento-varnish PortName:http} Backend:{Watch:false Namespace:production Service:magento Port: PortName:http} Signaller:{Enable:true Address:0.0.0.0 Port:8090 WorkersCount:1 MaxRetries:5 RetryBackoffString:30s RetryBackoff:30s} Admin:{Address:0.0.0.0 Port:6083} Varnish:{SecretFile:/etc/varnish/k8s-secret/secret Storage:malloc,512M AdditionalParameters:http_resp_size=98304 VCLTemplate:/etc/varnish/tmpl/default.vcl.tmpl VCLTemplatePoll:false}}
I0720 09:53:18.226207       1 main.go:38] using in-cluster configuration
I0720 09:53:18.227507       1 run.go:15] waiting for initial configuration before starting Varnish
I0720 09:53:18.227848       1 run.go:35] creating initial VCL config
I0720 09:53:18.227959       1 wait.go:12] probing admin port until it is available
Debug: Version: varnish-6.0.6 revision 29a1a8243dbef3d973aec28dc90403188c1dc8e7
Debug: Platform: Linux,4.19.123-coreos,x86_64,-junix,-smalloc,-sdefault,-hcritbit
Debug: Child (29) Started
Info: Child (29) said Child starts
I0720 09:53:21.228543       1 dial.go:41] authentication required. challenge string: "iqtcmzvvljtjtzouppcegioirulcyxqz"
I0720 09:53:21.228560       1 wait.go:23] admin port is available

Environment:

  • Kubernetes version: 1.17.0
  • kube-httpcache version: quay.io/mittwald/kube-httpcache:stable

Could not find the problem. Another interesting thing for me is that even though I set the following checks:

readinessProbe:
            httpGet:
              port: 80
              httpHeaders:
                - name: Host
                  value: example.com
            initialDelaySeconds: 10
            periodSeconds: 5
          livenessProbe:
            httpGet:
              port: 80
              httpHeaders:
                - name: Host
                  value: example.com

Varnish would still not get restarted by Kubernetes, health checks pass. If Varnish would get restarted as soon as it starts to throw 503s, the site would be down only for a minute.

Thanks for help!

Possible memory leak?

Describe the bug
kube-httpcache container is slowly eating memory. 2Gi memory limit is being eaten within 48 hours.

image

To Reproduce
Run kube-httpcache.

Expected behavior
Memory usage staying rather stable or at least not increasing steadily over time.

Environment:

  • Kubernetes version: 1.18
  • kube-httpcache version: latest master

Configuration
If that's necessary I can share VCL, but that's pretty much 90% regular Magento 2 VCL.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.