Introduction to Traefik Ingress Controller

It has been quite a while since I wrote my last post related to Traefik. Covid-19 has changed a lot of things including my relationship with Traefik, which I did not expect at all. Because of these changes, I was reluctant to do any new contents for Traefik. However, recently one of my viewers asked me to cover this topic again, I finally made up my mind to write this long waiting post to who you how to use Traefik as Ingress Controller.

If you’ve read my previous posts you would know that Traefik has two types of configurations. The Static Configuration is used to configuration Traefik itself and the Dynamic Configuration is used to define how Traefik routes requests to different backend services. Static configurations are set during the installation time and dynamic configuration comes from Ingress, middleware, services that we can create dynamically.

Let’s get started.

Installation

Install Traefik with Helm

Let’s start by installing Traefik. If you’ve read my blog posts before, you know I tend to use the most simple installation to get you started. Below settings are the things that I believe is essential.

  • image.tag: As usual, I like to use the most recent version which is 2.5 at the moment
  • ingressRoute.dashboard.enabled: I don’t need Traefik to create a dashboard for me. I will show you how to do it later on.
  • pilot.dashboard: This is to disable the notorious Connect to Traefik Pilot button. For more discussion, you can read this GitHub issue. IMO it should be turn off by default.
  • ports.websecure.tls.enabled: This is used to enable SSL on websecure entrypoint.
  • ingressClass.enabled: ingress.class annotation had been officially deprecated, I reckon users should start using ingressClass.
1
2
3
4
5
6
7
helm upgrade -i traefik traefik/traefik -n traefik \
--set image.tag=2.5 \
--set ingressRoute.dashboard.enabled=false \
--set pilot.dashboard=false \
--set ports.websecure.tls.enabled=true \
--set ingressClass.enabled=true \
--create-namespace

This will create a LoadBalancer service for Traefik. Cloud providers like AWS or Azure normally creates a loadbalancer with an external IP automatically. I am using MetalLB in my environment which also assigns an IP for this service. Let’s call this IP <TRAEFIK_PROXY>.

Enable Traefik Dashboard

I’ve yet to find a native way to create ingress object for Traefik Dashboard so I have to use their IngressRoute CRD to access dashboard.

Please save below to dashboard.yaml.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
---
apiVersion: v1
kind: Secret
metadata:
name: traefik-dashboard-auth-secret
namespace: default
data:
users: |
YWRtaW46JGFwcjEkbEE1T21ZNU8kV2lJLlJCSTdKQWFPT2dhLkJVOS5lLwoK
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: traefik-dashboard-basicauth
namespace: default
spec:
basicAuth:
secret: traefik-dashboard-auth-secret
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: traefik-dashboard
namespace: default
spec:
routes:
- match: PathPrefix(`/api`) || PathPrefix(`/dashboard`)
kind: Rule
middlewares:
- name: traefik-dashboard-basicauth
namespace: default
services:
- name: api@internal
kind: TraefikService

First thing you will notice is that I’ve created a basicAuth middleware because I don’t want to use insecure mode to access dashboard. This middleware requires reading username and password from Kubernete secret. We will use htpasswd to generate our username and password string, base64 encoded it and then put it in secret traefik-dashboard-auth-secret.

1
htpasswd -nb admin qwer1234 | base64

Then it is the IngressRoute. In my config, it matches requests coming to /dashboard/ (the ending / is critical) and route them to api@internal service which is our dashboard. PathPrefix(`/api`) is actually a must because Traefik dashboard needs to read data from this endpoint.

Let’s run kubectl apply -f dashboard.yaml to apply this file. After that we should be able to access to dashboard at <TRAEFIK_PROXY>/dashboard/.

Usage

There are two methods to use Traefik Ingress Controller.

  • Kubernetes Ingress: Users apply Traefik annotations on native Kuberentes Ingress objects.
  • Kubernetes CRD: Users define Traefik CRD objects like IngressRoute, TraefikService or Middleware to route their requests differently. You can find these CRD references here. These CRDs provides extra features on top of native kubernetes resources. For example users can use TraefikServices for server loadbalancing or mirroring the request to different services. If you want to read how these CRDs are defined, you can checl official doc here.

To better understand how these two methods are used, I will show you how to do the same thing with these two methods below.

Preparation

We will create a httpbin deployment as demo app.

  1. Create httpbin deployment.
    1
    kubectl create deployment httpbin --image=kennethreitz/httpbin
  2. Expose this httpbin deployment with httpbin-svc service at port 80.
    1
    kubectl expose deployment httpbin --name httpbin-svc --port 80

Once the service is up, we need to route our request to this application.

Kuberenete Ingress

Annotation can be added to native Kubernetes Ingress objects to instruct Traefik how to route requests.

I notice that unlike the other ingress controllers, Traefik does not seem to care if an Ingress Object has kubernetes.io/ingress.class: traefik annotation or ingressClass. It will always route the request. With that being said, I still include ingressClassName in our ingress object.

Create Ingress

Let’s start by creating a simple ingress object. This ingress is pretty straight forward, when the request comes in with host header httpbin.li.traefik, it routes the request to httpbin-svc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpbin-route
namespace: default
spec:
ingressClassName: traefik
rules:
- host: httpbin.li.traefik
http:
paths:
- backend:
service:
name: httpbin-svc
port:
number: 80
path: /
pathType: Prefix

Please note I’ve set the IP address for httpbin.li.traefik to be the same as <TRAEFIK_PROXY> because Traefik is our entrypoint.

Now when we access this domain, we should see 200 response.

1
2
curl http://httpbin.li.traefik/anything -I
HTTP/2 200

Create Ingress with TLS

TLS management is a big topic. If you’ve read my posts before, you know how much I praise Traefik for making ACME so easy on Docker. However, I would recommend using cert-manager to manage tls certificates on kubernetes in general. You can also check my cert-manager post for more information.

  • Create Secret
    I’ve prepared a self-sign wildcard certificate for *.li.traefik, let’s create tls secret.

    1
    kubectl create secret tls traefik-cert --cert=li.traefik.ecc.cert.pem --key=li.traefik.ecc.key.pem
  • Update Ingress Object
    Let’s update our ingress object with tls we created.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    name: httpbin-route
    namespace: default
    spec:
    ingressClassName: traefik
    rules:
    - host: httpbin.li.traefik
    http:
    paths:
    - backend:
    service:
    name: httpbin-svc
    port:
    number: 80
    path: /
    pathType: Prefix
    tls:
    - hosts:
    - httpbin.li.traefik
    secretName: traefik-cert

We can access this route via both https.

1
2
curl https://httpbin.li.traefik/anything -I
HTTP/1.1 200 OK

http redirect to https

There are two methods to redirect http to https.

The first methods is to redirect at entryPoint level which is part of static configuration. You can enable it by passing this --set ports.web.redirectTo=websecure when you are deploying Traefik with helm.

The second method is to use redirectScheme middleware.

  • Define Middleware
    First we need to define the redirect middleware. I called it http-redirect-https.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    apiVersion: traefik.containo.us/v1alpha1
    kind: Middleware
    metadata:
    name: http-redirect-https
    namespace: default
    spec:
    redirectScheme:
    scheme: https
    permanent: true
  • Apply middleware to ingress
    We will use annotation to associate this middleware with our ingress object. The value of the middleware annotion is <middleware-namespace>-<middleware-name>@kubernetescrd. For more information, please check provider namespace from official doc.

    1
    kubectl annotate ingress httpbin-route traefik.ingress.kubernetes.io/router.middlewares=default-http-redirect-https@kubernetescrd

    Now when you access the route via http, you will receive 308 Permanent Redirect response.

    1
    2
    3
    4
    5
    6
    7
    8
    curl http://httpbin.li.traefik/anything -IL
    HTTP/1.1 308 Permanent Redirect
    Location: https://httpbin.li.traefik/anything
    Date: Mon, 06 Sep 2021 15:19:01 GMT
    Content-Length: 18
    Content-Type: text/plain; charset=utf-8

    HTTP/2 200

This is a very basic usage of using Traefik annotations on ingress. You can find more annotations on official documentation. Some of them can be used on Ingress object and some can be used on Service object.

Kubernetes CRD

Let’s start by creating exactly the same as above example. Please save below to traefik-crd-demo.yaml.

I’ve defined

  • Middleware redirectScheme for http to https redirect
  • IngressRoute httpbin-http-route for http traffic
  • IngressRoute httpbin-https-route for https traffic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: http-redirect-https
namespace: default
spec:
redirectScheme:
scheme: https
permanent: true
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: httpbin-http-route
namespace: default
spec:
entryPoints:
- web
routes:
- kind: Rule
match: Host(`httpbin.li.traefik`)
middlewares:
- name: http-redirect-https
namespace: default
services:
- kind: Service
name: httpbin-svc
namespace: default
port: 80
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: httpbin-https-route
namespace: default
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`httpbin.li.traefik`)
middlewares:
- name: http-redirect-https
namespace: default
services:
- kind: Service
name: httpbin-svc
namespace: default
port: 80
tls:
secretName: traefik-cert

If you have read my post about using file provider you probable can see how similar the concept of IngressRoute and file provider are.

Let’s run kubectl apply -f traefik-crd-demo.yaml to apply this file. After that we should be able to see the same http to https redirects.

1
2
3
4
5
6
7
8
curl http://httpbin.li.traefik/anything -LI
HTTP/1.1 308 Permanent Redirect
Location: https://httpbin.li.traefik/anything
Date: Wed, 08 Sep 2021 14:00:25 GMT
Content-Length: 18
Content-Type: text/plain; charset=utf-8

HTTP/2 200

In summary

Personally I prefer native Kubernetes Ingress with annotations as I believe Ingress Controller CRDs should be used to extend the functionalities of native Kubernetes objects not to replace them. Using IngressRoute to replace Ingress might cause more issues sometimes. As you can see from above example, we must create two IngressRoute object to do the same thing that we can achieve with 1 ingress object. IngressRoute CRD does not support cert-manager properly either. As stated on official website. The workaround they mentioned is to use native Kubernetes Ingress with annotations.

When using the Traefik Kubernetes CRD Provider, unfortunately Cert-Manager cannot yet interface directly with the CRDs. A workaround is to enable the Kubernetes Ingress provider to allow Cert-Manager to create ingress objects to complete the challenges.

Users can also achieve most of their features by using annotations. Let’s use official template as an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: foo
namespace: bar
spec:
entryPoints: # [1]
- foo
routes: # [2]
- kind: Rule
match: Host(`test.example.com`) # [3]
priority: 10 # [4]
middlewares: # [5]
- name: middleware1 # [6]
namespace: default # [7]
services: # [8]
- kind: Service
name: foo
namespace: default
passHostHeader: true
port: 80 # [9]
responseForwarding:
flushInterval: 1ms
scheme: https
serversTransport: transport
sticky:
cookie:
httpOnly: true
name: cookie
secure: true
sameSite: none
strategy: RoundRobin
weight: 10
tls: # [10]
secretName: supersecret # [11]
options: # [12]
name: opt # [13]
namespace: default # [14]
certResolver: foo # [15]
domains: # [16]
- main: example.net # [17]
sans: # [18]
- a.example.net
- b.example.net

For annotations that can be added to Ingress Object

  • entryPoints –> traefik.ingress.kubernetes.io/router.entrypoints
  • priority –> traefik.ingress.kubernetes.io/router.priority
  • middlewares –> traefik.ingress.kubernetes.io/router.middlewares
  • tls -> traefik.ingress.kubernetes.io/router.tls
  • certResolver–>traefik.ingress.kubernetes.io/router.tls.certresolver

To save us some time I won’t list all of them in this post but you should get the idea.

That’s all I want to share you today. My next post will be focusing on using Kong Ingress Controller. If you are interested, stay tuned.

See you next time.