{"id":13547681,"url":"https://github.com/mittwald/kube-httpcache","last_synced_at":"2025-04-05T04:15:13.203Z","repository":{"id":33898579,"uuid":"153236644","full_name":"mittwald/kube-httpcache","owner":"mittwald","description":"Varnish Reverse Proxy on Kubernetes","archived":false,"fork":false,"pushed_at":"2024-07-18T15:26:46.000Z","size":295,"stargazers_count":303,"open_issues_count":32,"forks_count":78,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-03-29T03:11:17.979Z","etag":null,"topics":["golang","kubernetes","kubernetes-controller","varnish"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mittwald.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-10-16T06:51:53.000Z","updated_at":"2025-03-09T07:19:31.000Z","dependencies_parsed_at":"2024-04-17T16:31:12.398Z","dependency_job_id":"de418fd2-3378-48f3-9daa-2083a3c064cf","html_url":"https://github.com/mittwald/kube-httpcache","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mittwald%2Fkube-httpcache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mittwald%2Fkube-httpcache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mittwald%2Fkube-httpcache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mittwald%2Fkube-httpcache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mittwald","download_url":"https://codeload.github.com/mittwald/kube-httpcache/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247284954,"owners_count":20913704,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","kubernetes","kubernetes-controller","varnish"],"created_at":"2024-08-01T12:00:59.574Z","updated_at":"2025-04-05T04:15:13.182Z","avatar_url":"https://github.com/mittwald.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Varnish on Kubernetes\n\n![GitHub Workflow Status](https://img.shields.io/github/workflow/status/mittwald/kube-httpcache/Test)\n\nThis repository contains a controller that allows you to operate a [Varnish cache](https://varnish-cache.org/) on Kubernetes.\n\n---\n:warning: **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**.\n\n---\n\n## Table of Contents\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [How it works](#how-it-works)\n- [High-Availability mode](#high-availability-mode)\n- [Getting started](#getting-started)\n  - [Create a VCL template](#create-a-vcl-template)\n  - [Create a Secret](#create-a-secret)\n  - [[Optional] Configure RBAC roles](#optional-configure-rbac-roles)\n  - [Deploy Varnish](#deploy-varnish)\n  - [Logging](#logging)\n- [Detailed how-tos](#detailed-how-tos)\n  - [Using built in signaller component](#using-built-in-signaller-component)\n  - [Proxying to external services](#proxying-to-external-services)\n- [Helm Chart installation](#helm-chart-installation)\n- [Developer notes](#developer-notes)\n  - [Build the Docker image locally](#build-the-docker-image-locally)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## How it works\n\nThis controller is not intended to be a replacement of a regular [ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress/). Instead, it is intended to be used between your regular Ingress controller and your application's service.\n\n```\n┌─────────┐       ┌─────────┐       ┌─────────────┐\n| Ingress | ----\u003e | Varnish | ----\u003e | Application |\n└─────────┘       └─────────┘       └─────────────┘\n```\n\nThe Varnish controller needs the following prerequisites to run:\n\n- A [Go-template](https://golang.org/pkg/text/template/) that will be used to generate a [VCL](https://varnish-cache.org/docs/trunk/users-guide/vcl.html) configuration file\n- An application [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) that will be used as backend for the Varnish controller\n- A Varnish [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) that will be used as frontend for the Varnish controller\n- 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\n\nAfter 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.\n\nThe 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.\n\n## High-Availability mode\n\nIt can run in high avalability mode using multiple Varnish and application pods.\n\n```\n             ┌─────────┐\n             │ Ingress │\n             └────┬────┘\n                  |\n             ┌────┴────┐\n             │ Service │\n             └───┬┬────┘\n             ┌───┘└───┐\n┌────────────┴──┐  ┌──┴────────────┐\n│   Varnish 1   ├──┤   Varnish 2   │\n│  Signaller 1  ├──┤  Signaller 2  │\n└─────────┬┬────┘  └────┬┬─────────┘\n          │└─────┌──────┘│\n          │┌─────┘└─────┐│\n┌─────────┴┴────┐  ┌────┴┴─────────┐\n│ Application 1 │  | Application 2 │\n└───────────────┘  └───────────────┘\n```\n\nThe Signaller component supports broadcasting PURGE and BAN requests to all Varnish nodes.\n\n## Getting started\n\n### Create a VCL template\n\n\u003chr\u003e\n\n:warning: **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.\n\n\u003chr\u003e\n\nStart by creating a `ConfigMap` that contains a VCL template:\n\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: vcl-template\ndata:\n  default.vcl.tmpl: |\n    vcl 4.0;\n\n    import std;\n    import directors;\n\n    // \".Frontends\" is a slice that contains all known Varnish instances\n    // (as selected by the service specified by -frontend-service).\n    // The backend name needs to be the Pod name, since this value is compared\n    // to the server identity (\"server.identity\" [1]) later.\n    //\n    //   [1]: https://varnish-cache.org/docs/6.4/reference/vcl.html#local-server-remote-and-client\n    {{ range .Frontends }}\n    backend {{ .Name }} {\n        .host = \"{{ .Host }}\";\n        .port = \"{{ .Port }}\";\n    }\n    {{- end }}\n\n    backend fe-primary {\n        .host = \"{{ .PrimaryFrontend.Host }}\";\n        .port = \"{{ .PrimaryFrontend.Port }}\";\n    }\n\n    {{ range .Backends }}\n    backend be-{{ .Name }} {\n        .host = \"{{ .Host }}\";\n        .port = \"{{ .Port }}\";\n    }\n    {{- end }}\n\n    backend be-primary {\n        .host = \"{{ .PrimaryBackend.Host }}\";\n        .port = \"{{ .PrimaryBackend.Port }}\";\n    }\n\n    acl purgers {\n        \"127.0.0.1\";\n        \"localhost\";\n        \"::1\";\n        {{- range .Frontends }}\n        \"{{ .Host }}\";\n        {{- end }}\n        {{- range .Backends }}\n        \"{{ .Host }}\";\n        {{- end }}\n    }\n\n    sub vcl_init {\n        new cluster = directors.hash();\n\n        {{ range .Frontends -}}\n        cluster.add_backend({{ .Name }}, 1);\n        {{ end }}\n\n        new lb = directors.round_robin();\n\n        {{ range .Backends -}}\n        lb.add_backend(be-{{ .Name }});\n        {{ end }}\n    }\n\n    sub vcl_recv\n    {\n        # Set backend hint for non cachable objects.\n        set req.backend_hint = lb.backend();\n\n        # ...\n\n        # Routing logic. Pass a request to an appropriate Varnish node.\n        # See https://info.varnish-software.com/blog/creating-self-routing-varnish-cluster for more info.\n        unset req.http.x-cache;\n        set req.backend_hint = cluster.backend(req.url);\n        set req.http.x-shard = req.backend_hint;\n        if (req.http.x-shard != server.identity) {\n            return(pass);\n        }\n        set req.backend_hint = lb.backend();\n\n        # ...\n\n        return(hash);\n    }\n\n    # ...\n```\n\nEnvironment variables can be used from the template. `{{ .Env.ENVVAR }}` is replaced with the\nenvironment variable value. This can be used to set for example the Host-header for the external\nservice.\n\n### Create a Secret\n\nCreate a `Secret` object that contains the secret for the Varnish administration port:\n\n```\n$ kubectl create secret generic varnish-secret --from-literal=secret=$(head -c32 /dev/urandom  | base64)\n```\n\n### [Optional] Configure RBAC roles\n\nIf RBAC is enabled in your cluster, you will need to create a `ServiceAccount` with a respective `Role`.\n\n```\n$ kubectl create serviceaccount kube-httpcache\n$ kubectl apply -f https://raw.githubusercontent.com/mittwald/kube-httpcache/master/deploy/kubernetes/rbac.yaml\n$ kubectl create rolebinding kube-httpcache --clusterrole=kube-httpcache --serviceaccount=kube-httpcache\n```\n\n### Deploy Varnish\n\n1. Create a `StatefulSet` for the Varnish controller:\n\n    ```yaml\n    apiVersion: apps/v1\n    kind: StatefulSet\n    metadata:\n      name: cache-statefulset\n      labels:\n        app: cache\n    spec:\n      serviceName: cache-service\n      replicas: 2\n      updateStrategy:\n        type: RollingUpdate\n      selector:\n        matchLabels:\n          app: cache\n      template:\n        metadata:\n          labels:\n            app: cache\n        spec:\n          containers:\n          - name: cache\n            image: quay.io/mittwald/kube-httpcache:stable\n            imagePullPolicy: Always\n            args:\n            - -admin-addr=0.0.0.0\n            - -admin-port=6083\n            - -signaller-enable\n            - -signaller-port=8090\n            - -frontend-watch\n            - -frontend-namespace=$(NAMESPACE)\n            - -frontend-service=frontend-service\n            - -frontend-port=8080\n            - -backend-watch\n            - -backend-namespace=$(NAMESPACE)\n            - -backend-service=backend-service\n            - -varnish-secret-file=/etc/varnish/k8s-secret/secret\n            - -varnish-vcl-template=/etc/varnish/tmpl/default.vcl.tmpl\n            - -varnish-storage=malloc,128M\n            env:\n            - name: NAMESPACE\n              valueFrom:\n                fieldRef:\n                  fieldPath: metadata.namespace\n            volumeMounts:\n            - name: template\n              mountPath: /etc/varnish/tmpl\n            - name: secret\n              mountPath: /etc/varnish/k8s-secret\n            ports:\n            - containerPort: 8080\n              name: http\n            - containerPort: 8090\n              name: signaller\n          serviceAccountName: kube-httpcache  # when using RBAC\n          restartPolicy: Always\n          volumes:\n          - name: template\n            configMap:\n              name: vcl-template\n          - name: secret\n            secret:\n              secretName: varnish-secret\n    ```\n\n    **NOTE**: Using a `StatefulSet` is particularly important when using a stateful, self-routed Varnish cluster. Otherwise, you could also use a `Deployment` resource, instead.\n\n2. Create a service for the Varnish controller:\n\n    ```yaml\n    apiVersion: v1\n    kind: Service\n    metadata:\n      name: cache-service\n      labels:\n        app: cache\n    spec:\n      ports:\n      - name: \"http\"\n        port: 80\n        targetPort: http\n      - name: \"signaller\"\n        port: 8090\n        targetPort: signaller\n      selector:\n        app: cache\n    ```\n\n3. 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)\n\n### Logging\nLogging uses [glog](https://github.com/golang/glog). \nDetailed logging e.g. for troubleshooting can be activated by passing command line parameter `-v7` (where 7 is requested logging level).\n\n## Detailed how-tos\n\n### Using built in signaller component\n\nThe 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.\n\nTo broadcast a `BAN` or `PURGE` request to all Varnish endpoints, run one of the following commands, respectively:\n\n    $ curl -H \"X-Url: /path\" -X BAN http://cache-service:8090\n    $ curl -H \"X-Host: www.example.com\" -X PURGE http://cache-service:8090/path\n\nWhen 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`):\n\n    $ kubectl port-forward service/cache-service 8090:8090\n\n**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.\n\n```vcl\nsub vcl_recv {\n  # ...\n\n  # Purge logic\n  if (req.method == \"PURGE\") {\n    if (client.ip !~ purgers) {\n      return (synth(403, \"Not allowed.\"));\n    }\n    if (req.http.X-Host) {\n      set req.http.host = req.http.X-Host;\n    }\n    return (purge);\n  }\n\n  # Ban logic\n  if (req.method == \"BAN\") {\n    if (client.ip !~ purgers) {\n      return (synth(403, \"Not allowed.\"));\n    }\n    if (req.http.Cache-Tags) {\n      ban(\"obj.http.Cache-Tags ~ \" + req.http.Cache-Tags);\n      return (synth(200, \"Ban added \" + req.http.host));\n    }\n    if (req.http.X-Url) {\n      ban(\"obj.http.X-Url == \" + req.http.X-Url);\n      return (synth(200, \"Ban added \" + req.http.host));\n    }\n    return (synth(403, \"Cache-Tags or X-Url header missing.\"));\n  }\n\n  # ...\n}\n```\n\n### Proxying to external services\n\n\u003chr\u003e\n\n**NOTE**: Native support for `ExternalName` services is a requested feature. Have a look at [#39](https://github.com/mittwald/kube-httpcache/issues/39) if you're willing to help out.\n\n\u003chr\u003e\n\nIn 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:\n\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: external-service\n  namespace: default\nspec:\n  type: ExternalName\n  externalName: external-service.example\n```\n\nIn 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):\n\n```yaml\nkind: ConfigMap\napiVersion: v1\nmetadata: # [...]\ndata:\n  default.vcl.tmpl: |\n    vcl 4.0;\n\n    {{ range .Frontends }}\n    backend {{ .Name }} {\n        .host = \"{{ .Host }}\";\n        .port = \"{{ .Port }}\";\n    }\n    {{- end }}\n\n    backend backend {\n        .host = \"external-service.svc\";\n    }\n\n    // ...\n```\n\nWhen starting kube-httpcache, remember to set the `--backend-watch=false` flag to disable watching the (non-existent) backend endpoints.\n\n## Helm Chart installation\n\nYou can use the [Helm chart](chart/) to rollout an instance of kube-httpcache:\n\n```\n$ helm repo add mittwald https://helm.mittwald.de\n$ helm install -f your-values.yaml kube-httpcache mittwald/kube-httpcache\n```\n\nFor possible values, have a look at the comments in the provided [`values.yaml` file](./chart/values.yaml). Take special note that you'll most likely have to overwrite the `vclTemplate` value with your own VCL configuration file.\n\nEnsure your defined backend services have a port named `http`:\n\n```\napiVersion: v1\nkind: Service\nmetadata:\n  name: backend-service\nspec:\n  ports:\n  - name: http\n    port: 80\n    protocol: TCP\n    targetPort: 8080\n  type: ClusterIP\n```\n\nAn ingress points to the kube-httpcache service which cached\nyour backend service:\n\n```\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: example-ingress\nspec:\n  rules:\n  - host: www.example.com\n    http:\n      paths:\n      - backend:\n          service:\n            name: kube-httpcache\n            port:\n              number: 80\n        path: /\n        pathType: Prefix\n```\n\nLook at the `vclTemplate` property in [chart/values.yaml](chart/values.yaml) to define\nyour own Varnish cluster rules or load with `extraVolume` an extra file\nas initContainer if your ruleset is really big.\n\n## Developer notes\n\n### Build the Docker image locally\n\nA Dockerfile for building the container image yourself is located in `build/package/docker`. Invoke `docker build` as follows:\n\n```\n$ docker build -t $IMAGE_NAME -f build/package/docker/Dockerfile .\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmittwald%2Fkube-httpcache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmittwald%2Fkube-httpcache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmittwald%2Fkube-httpcache/lists"}