Traefik
Traefik is an open-source Edge Router for publishing services. It receives requests on behalf of your system and finds out which components are responsible for handling them.
Traefik is natively compliant with every major cluster technology, such as Kubernetes, Docker, Docker Swarm, AWS, Mesos, Marathon, and others; and can handle many at the same time. (It even works for legacy software running on bare metal.) Containous, the company that sponsors Traefik’s development, can provide commercial support and develops an Enterprise Edition of Traefik.
With Kubernetes, Traefik supports Ingress resources as we had encountered before, as well as Traefik-specific IngressRoutes that allow to unleash the full potential of what Traefik provides.
Traefik has already been set up in our test cluster beforehand (via helm, by the way, see Setup instructions above) so
we can jump right at making use of it, cf. kubectl get all --all-namespaces -l app.kubernetes.io/name=traefik, e.g.:
NAMESPACE NAME READY STATUS RESTARTS AGE
traefik-v2 pod/traefik-69f6b585fc-4xfhr 1/1 Running 0 2d18h
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
traefik-v2 service/traefik LoadBalancer 10.0.147.225 51.145.138.238 80:31785/TCP,443:30850/TCP 2d20h
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
traefik-v2 deployment.apps/traefik 1/1 1 1 2d20h
NAMESPACE NAME DESIRED CURRENT READY AGE
traefik-v2 replicaset.apps/traefik-69f6b585fc 1 1 1 2d18hBut for now we will just use what the Ingress Controller provides without diving into too much detail on how this will be achieved.
Note
Still, you can see already that Traefik gets exposed as a LoadBalancer service, and this service’s external IP address is what we will need to access during our exercises, and we won’t see any addresses listed in our future Ingress resources, so let’s take note of this address now via export INGRESS_IP_TRAEFIK=<our_Ingress_IP>.
export INGRESS_IP_TRAEFIK=$(kubectl get service --namespace traefik-v2 traefik -o jsonpath='{.status.loadBalancer.ingress[].ip}')
should do the trick, which you can then verify via
echo $INGRESS_IP_TRAEFIK.
This variable will not persist over a logout nor will it spread to other separate sessions, so remember to set it again whenever you (re)connect to your user VM.
Namespace
With the Ingress Controller still being a pre-defined cluster-wide central solution, we again need to take care to not step on each others’ toes in the following exercises. For that we will prefix some parts of the following resources with a user-specific namespace.
Note
As we will use this personal namespace several times as a resource prefix, make it now available for easy consumption via a variable:
export NAMESPACE=$(kubectl config view --minify --output 'jsonpath={..namespace}'); echo $NAMESPACE
This variable will not persist over a logout nor will it spread to other separate sessions, so remember to set it again whenever you (re)connect to your user VM.
Exercise - Expose our sample application using Ingress via Traefik
Let’s start with our little sample application again, just like we did in the chapter concerning Ingress Controller - Nginx . In fact, we will just pick up several things that have been created in the course of that chapter, so make sure to finish that before proceeding here.
Create sampleapp-ingress-traefik.yaml by executing the following (yes, execute it all at once), utilizing your
personal namespace as a host component, and our Ingress IP by way of nip.io
wildcard DNS as a suffix, and of
course we have to give it a new name in order to not just overwrite our previous Ingress:
cat <<.EOF > sampleapp-ingress-traefik.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sampleapp-traefik
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: $NAMESPACE-stripprefix-subpath@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: hello.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sampleapp
port:
number: 8080
- path: /subpath/
pathType: Prefix
backend:
service:
name: sampleapp-subpath
port:
number: 8080
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: stripprefix-subpath
spec:
stripPrefix:
prefixes:
- /subpath
.EOFCompared to our previous Ingress resource for ingress-nginx we see quite some similarities as well as some differences:
- the basic structure is standardized, and each Ingress Controller is supposed to understand it: a spec with rules, each specifying a host and which paths are supposed to be redirected to which backend
- annotations steer which Ingress Controller shall be used, and also implementation-specific details (we advise K8s here to use the Traefik Ingress Controller)
- instead of pattern matching and rewriting applied to a subpath a Traefik-specific Middleware is used
- as Traefik has its own concept of namespaces for accessing several backend providers (e.g. Kubernetes, Mesos, Consul, …) the Kubernetes namespace will be prepended
With Traefik, Middleware is what does all tweaking of requests or responses. We will cover some examples further down.
Now, create the resources by running the following command:
kubectl apply -f sampleapp-ingress-traefik.yaml
Milestone: K8S/INGRESS/TRAEFIK-SAMPLEAPP-INGRESS
Verify that the Ingress is configured as intended:
kubectl describe ingress sampleapp-traefik
Name: sampleapp-traefik
Labels: <none>
Namespace: <your_namespace>
Address: <your_Ingress_IP>
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
hello.<your_namespace>.<your_Ingress_IP>.nip.io
/ sampleapp:8080 (172.17.0.4:8080)
/subpath/ sampleapp-subpath:8080 (172.17.0.3:8080)
Annotations: traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: <your_namespace>-stripprefix-subpath@kubernetescrd
Events: <none>(Please note that even though the Ingress resource references a service name that will also be listed for each path, the IP addresses listed there will be those of the corresponding Pods.)
Then access our application, both normally and the subpath, via the Traefik LoadBalancer service external IP:
curl http://hello.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io/hello; echo
curl http://hello.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io/subpath/hello; echowhich should yield
Hello World (from sampleapp-65779bd948-6gcm7) to somebody
Hello World (from sampleapp-subpath-7d466c4ccb-jlrcb) to everyone via Ingress from a subpathSo all in all this now provides the same results as we had achieved using ingress-nginx. Let’s do the same for our ToDo application, just like we did in the previous chapter.
Exercise - Ingress’ify our ToDo application via Traefik
We will need a redirect, some Ingress backends, basic auth settings, and a certificate for TLS termination. Parts of that we had already created in the previous chapter, first locally and then put into Kubernetes, so let’s plug it all together.
TLS certificate
Self-signed will suffice for now:
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 \
-keyout todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io.key \
-out todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io.crt \
-subj "/CN=todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io"which will output some line(s) with symbols indicating its generating data.
Verify the certificate’s CN:
openssl x509 -in todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io.crt -noout -subject
subject=CN = todo.<your_namespace>.<your_Ingress_IP>.nip.ioLoad this as a Kubernetes secret:
kubectl create secret tls todo-traefik-tls-secret --key todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io.key --cert todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io.crt
secret/todo-traefik-tls-secret createdAnd verify it is present:
kubectl describe secrets todo-traefik-tls-secret
Name: todo-traefik-tls-secret
Namespace: <your_namespace>
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tls
Data
====
tls.crt: 1180 bytes
tls.key: 1704 bytesRemember from Exercise - create a Secret how to verify the contents?
Milestone: K8S/INGRESS/TRAEFIK-TODOAPP-CERT
Basic auth
We are just going to reuse the Secret and the credentials embedded therein that we have created in the Nginx chapter, so make sure you have actually finished that.
Ingress
Thus, now it is time to plug it all together. Create a file todoapp-ingress-traefik.yaml by executing the following
(yes, execute it all at once), again utilizing your personal namespace as a prefix:
cat <<.EOF > todoapp-ingress-traefik.yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: redirect-to-https
spec:
redirectScheme:
scheme: https
permanent: true
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: todo-traefik-redirect
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: $NAMESPACE-redirect-to-https@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: todoui
port:
number: 8090
---
apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
name: minversion
spec:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_RSA_WITH_AES_256_GCM_SHA384
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: todo-traefik
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
traefik.ingress.kubernetes.io/router.tls.options: $NAMESPACE-minversion@kubernetescrd
spec:
ingressClassName: traefik
tls:
- hosts:
- todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io
secretName: todo-traefik-tls-secret
rules:
- host: todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: todoui
port:
number: 8090
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: stripprefixregex-backend
spec:
stripPrefixRegex:
regex:
- "/backend/v[0-9]+"
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: basic-auth-backend
spec:
basicAuth:
secret: todo-backend-basic-auth
realm: "Authentication Required - ToDo App Backend"
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: todo-traefik-backend-basic-auth
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.middlewares: $NAMESPACE-stripprefixregex-backend@kubernetescrd,$NAMESPACE-basic-auth-backend@kubernetescrd
traefik.ingress.kubernetes.io/router.tls: "true"
traefik.ingress.kubernetes.io/router.tls.options: $NAMESPACE-minversion@kubernetescrd
spec:
ingressClassName: traefik
tls:
- hosts:
- todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io
secretName: todo-traefik-tls-secret
rules:
- host: todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io
http:
paths:
- path: /backend/v1/
pathType: Prefix
backend:
service:
name: todobackend-v1
port:
number: 8080
- path: /backend/v2/
pathType: Prefix
backend:
service:
name: todobackend
port:
number: 8080
.EOFApply it with kubectl apply -f todoapp-ingress-traefik.yaml and verify what has been created:
kubectl describe ingress todo-traefik-redirect todo-traefik todo-traefik-backend-basic-auth
[...]
Rules:
Host Path Backends
---- ---- --------
todo.<your_namespace>.<your_Ingress_IP>.nip.io
/ todoui:8090 (172.17.0.5:8090)
Annotations: traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: <your_namespace>-redirect-to-https@kubernetescrd
[...]
Rules:
Host Path Backends
---- ---- --------
todo.<your_namespace>.<your_Ingress_IP>.nip.io
/ todoui:8090 (172.17.0.5:8090)
Annotations: traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: true
traefik.ingress.kubernetes.io/router.tls.options: <your_namespace>-minversion@kubernetescrd
[...]
Rules:
Host Path Backends
---- ---- --------
todo.<your_namespace>.<your_Ingress_IP>.nip.io
/backend/v1/ todobackend-v1:8080 (172.17.0.9:8080)
/backend/v2/ todobackend:8080 (172.17.0.7:8080)
Annotations: traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.middlewares: <your_namespace>-stripprefixregex-backend@kubernetescrd,<your_namespace>-basic-auth-backend@kubernetescrd
traefik.ingress.kubernetes.io/router.tls: true
traefik.ingress.kubernetes.io/router.tls.options: <your_namespace>-minversion@kubernetescrd
[...]Milestone: K8S/INGRESS/TRAEFIK-TODOAPP-INGRESS
Verification
Well, but does it actually work as intended? Let’s find out by first verifying TLS termination:
curl --verbose --insecure https://todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io/
[...]
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=todo.<your_namespace>.<your_Ingress_IP>.nip.io
* start date: Dec 1 13:01:46 2023 GMT
* expire date: Nov 30 13:01:46 2024 GMT
* issuer: CN=todo.<your_namespace>.<your_Ingress_IP>.nip.io
* SSL certificate verify result: self signed certificate (18), continuing anyway.
* Using HTTP2, server supports multi-use
[...]
<!DOCTYPE HTML>
<html>
<head>
<title>Schönste aller Todo Listen</title>
[...]Then let’s verify the redirect HTTP->HTTPS is present:
curl --verbose http://todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io/
[...]
< HTTP/1.1 301 Moved Permanently
< Location: https://todo.<your_namespace>.<your_Ingress_IP>.nip.io/
[...]And confirm the contents on redirect:
curl --silent --location --insecure http://todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io/ | head -n 4
<!DOCTYPE HTML>
<html>
<head>
<title>Schönste aller Todo Listen</title>So, TLS termination, including a redirect HTTP->HTTPS, seems to work just fine.
Tip
More details / checks wanted? Run
docker run --rm -it drwetter/testssl.sh:latest https://todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io/
and enjoy.
Now let’s check basic auth on backend:
curl --verbose --insecure https://todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io/backend/v2/todos/; echo
[...]
< HTTP/2 401
< content-type: text/plain
< www-authenticate: Basic realm="Authentication Required - ToDo App Backend"
[...]OK, we indeed need to authenticate, so let’s try this while inserting sample data (if none already present):
curl --request POST --insecure https://todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io/backend/v2/todos/traefiktest --user backenddebugger:password; echo
added traefiktestAnd query data from backend:
curl --silent --insecure https://todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io/backend/v2/todos/ --user backenddebugger:password; echo
["testabc", "traefiktest"]Also test the v1 backend:
curl --silent --insecure https://todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io/backend/v1/todos/ --user backenddebugger:password; echo
["testabc", "traefiktest"]Test the two different backend versions. As they only differ in handling calls to /fail let’s do just that and check their Pods afterwards:
curl --silent --insecure https://todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io/backend/v2/fail --user backenddebugger:password; echo
fixed!So, no crash, meaning we really reached the v2 backend Pod. Let’s compare this to
curl --silent --insecure https://todo.$NAMESPACE.$INGRESS_IP_TRAEFIK.nip.io/backend/v1/fail --user backenddebugger:password
Bad GatewayAnd after a few moments we see the Pod has indeed been restarted:
kubectl get pods todobackend-v1-<ReplicaSetId>-<PodId>
NAME READY STATUS RESTARTS AGE
todobackend-v1-84bc4474fd-bjpzd 1/1 Running 2 25m37sSo, the Pod has crashed, meaning we had really reached the v1 backend Pod.
All in all, it seems this Traefik-based Ingress is now working just like the ingress-nginx-based one we had created in the previous chapter. But what else can Traefik do?
Traefik extras
Going beyond the standardized Ingress interface Traefik is able to provide further services, similar to what ingress-nginx already allows, and then some extra:
- canary deployments via weighted round-robin (WRR) services defined in an IngressRoute
- circuit breakers via a fallback mechanism in case a predefined service is not reacting correctly, as e.g. measured via network error ratio, status code ratio, and/or latency
- request retrying to cover temporarily unavailable services
- rate-limiting to ensure some quality of service
- traffic mirroring for debugging / developing
Just for reference, some more options will be used when dealing with Service Meshes : Opt-In Canary .
Checking all these is beyond the scope of these exercises, so see the Traefik documentation for details. But we are going to check out one more feature as follows.
Web interface
While ingress-nginx allows us to check the generated configuration file, Traefik provides a web interface to investigate what has been configured and what might not be working.
Provide access via port-forwarding
kubectl port-forward -n traefik-v2 $(kubectl get pods -n traefik-v2 --selector "app.kubernetes.io/name=traefik" --output name) 9000:9000
Now you can follow the instructions for the port forwarding within your SSH client, cf. the instructions for Linux / MacOS or Windows . Add a mapping from your local port 9000 to localhost:9000, and then you will be able to access this through your local browser at http://localhost:9000/dashboard/ , using dual port-forwarding (first via ssh client, then via kubectl), and you should see the application like in the following pictures:
Feel free to introduce errors into your resources, e.g. via kubectl delete middleware stripprefixregex-backend and
check how this will be indicated in the web interface.


