Lately, I have been migrating the public services I have running on a DigitalOcean Droplet to a cluster of Raspberry Pis running K3s at home. To simplify the development workflow for my projects I have converted many of them to OpenFaaS functions that run on the cluster. As many of these projects are exposed as subdomains, I have deployed OpenFaaS with its IngressOperator which helps me manage the mapping from
example.com/function/nodeinfo through the use of the FunctionIngress CRD.
Using the latest stable version of K3s (v1.20.6+k3s1) I ran into an issue where paths on a subdomain would be incorrectly mapped to functions. For example,
nodeinfo.example.com/test would be mapped to
example.com/function/nodeinfotest, effectively stripping a slash. This is a known issue with the
rewrite-target annotation that the OpenFaaS IngressOperator uses to map function paths.
Unfortunately, K3s v1.20.6+k3s1 comes bundled with Traefik v1.7.19, and the fix for this issue was not released until v1.7.21. Normally I would just upgrade the bundled Helm chart to the required version, but Rancher had just released K3s v1.21+k3s1 which came with the much-anticipated upgrade to Traefik v2.4.8, so I opted to upgrade my cluster.
There are significant changes in Traefik v1 to v2, the most relevant of which is the replacement of ingress annotations in Kubernetes with CRDs. Currently, the OpenFaaS IngressOperator is not compatible with Traefik v2, so to recreate the functionality of the
rewrite-target annotation, we would need to define a new ReplacePathRegex Middleware. Using the
nodeinfo example from earlier, we will create the following resource.
apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: nodeinfo-replacepathregex namespace: openfaas spec: replacePathRegex: regex: ^/(.*) replacement: /function/nodeinfo/$1
To use this Middleware, we will add a custom annotation to our normal FunctionIngress definition.
apiVersion: openfaas.com/v1alpha2 kind: FunctionIngress metadata: name: nodeinfo namespace: openfaas annotations: traefik.ingress.kubernetes.io/router.middlewares: [email protected] spec: domain: "nodeinfo.example.com" function: "nodeinfo" ingressType: "traefik"
Now, assuming you have your DNS configured correctly, paths on your subdomains will be properly mapped to the function defined by your FunctionIngress. Going forward, I hope that the OpenFaaS IngressOperator implements Traefik v2 support or, though unlikely, Traefik reimplements inline middleware definitions for Kubernetes similar to what they have for other platforms with labels.