In my infrastructure, I have:

  • MinIO (running directly on FreeNAS) to provide S3-capabilities
    • MinIO itself is pulled into my Kubernetes cluster (i.e. as an Endpoint and associated Service)
  • Traefik is deployed as an Kubernetes Ingress controller

The goal will be to serve a static website hosted in MinIO with Traefik. Traefik will receive the external requests, process them as necessary and then proxy them to MinIO. MinIO will respond with the necessary static files; this then passes back to Traefik before going to the external requester.

MinIO Bucket Setup

  • Create a bucket using the MinIO mc client and set the appropriate policies
mc alias set s3 http://minio
mc mb s3/blog-frbncis
mc policy download s3/blog-frbncis
  • Upload your static site to the bucket with mc (or via the GUI or with any other S3 upload tool)
mc cp -r public_html/* s3/blog-frbncis

Traefik Kubernetes Ingress Configuration

Middlewares

You’ll have to tweak this based on your desired setup, but I wanted to service requests for https://blog.frbnc.is with the bucket contents. I prefer (for whatever reason) to configure Traefik using the Kubernetes Ingress resources as opposed to their CRDs, so the sample YAMLs take that format.

Traefik will have to apply some rewrites on the incoming request before passing upstream to MinIO.

To figure out what is needed, let’s take a look at requests for index.html in the bucket root. You can request this file directly from MinIO as http://minio/blog-frbncis/index.html. But external incoming request would take the form of https://blog.frbnc.is/index.html. This one is easy, we just need to add a prefix of blog-frbncis to the upstream request.

With Traefik, we create a middleware to handle this for us.

---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: prefix
  namespace: static-sites
spec:
  addPrefix:
    prefix: "blog-frbncis"

The second thing we will need to do is handle cases where we might not have index.html specified in the URL - for example, if we do a request for https://blog.frbnc.is/. In it’s unmodified form, the request is passed to MinIO as http://minio/blog-frbncis. MinIO handles this as a ListObjects call and will return an XML response - this isn’t what we want!

Thus, we will need to append index.html. This is done using a regex to determine if the request path ends in a trailing slash and append accordingly.

---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: index-rewrite
  namespace: static-sites
spec:
  replacePathRegex:
    regex: "(.*)/$"
    replacement: "$1/index.html"

Kubernetes Ingress

Finally, we chain all this together into an Ingress resource that is applied to the cluster. The middlewares are referenced in the annotation traefik.ingress.kubernetes.io/router.middlewares.

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: blog-frbncis
  namespace: static-sites
  annotations:
    traefik.ingress.kubernetes.io/frontend-entry-points: https
    traefik.ingress.kubernetes.io/redirect-permanent: "true"
    traefik.ingress.kubernetes.io/router.middlewares: "static-sites-index-rewrite@kubernetescrd,static-sites-prefix@kubernetescrd"
spec:
  ingressClassName: "traefik"
  rules:
  - host: blog.frbnc.is
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: minio
            port:
             number: 9000

All this put together (and with DNS records pointing to Traefik) is how I serve https://blog.frbnc.is off a bucket in MinIO.