Deploying Traefik as Ingress Controller on Kubernetes

Traefik is a powerful cloud-native edge router that can be utilised in many different environments, the most popular being Kubernetes and Docker. The built-in ACME client, amongst other features, makes Traefik a great choice as an edge router.

Traefik has two types of configuration: Dynamic configuration, which is set on the Kubernetes services we want to expose as annotations, and static configuration, which configures Traefik itself.

The first configuration that needs to be defined is the static configuration, used when Helm installs Traefik. The static configuration can be declared using CLI, YAML or TOML. In this case, we will use YAML.

First, set Traefik as the default Ingress Controller for Kubernetes:

ingressClass:
  enabled: true
  isDefaultClass: true

Disable the Traefik dashboard. The dashboard is not something we want to expose to the internet:

ingressRoute:
  dashboard:
    enabled: false

Configure Traefik to obtain SSL certificates from LetsEncrypt. Because we are using HTTP verification, ensure your domain name is already pointing to the IP address your Kubernetes cluster will be exposed on.

additionalArguments:
  - --certificatesresolvers.letsencrypt.acme.caServer=https://acme-v02.api.letsencrypt.org/directory
  - --certificatesresolvers.letsencrypt.acme.httpChallenge.entryPoint=web
  - --certificatesresolvers.letsencrypt.acme.storage=/ssl-certs/acme-generic.json

The certificate issued by LetsEncrypt is saved in /ssl-certs/acme-generic.json. Use a persistent volume to save and re-use this certificate across new Traefik containers:

persistence:
  enabled: true
  name: ssl-certs
  size: 1Gi
  path: /ssl-certs

Sometimes, there can be permission issues when Traefik tries to save or read the certificate from the persistent volume. Deploying this busybox container will ensure the permissions are correct within the volume:

deployment:
  initContainers:
    - name: volume-permissions
      image: busybox:1.31.1
      command: ["sh", "-c", "chmod -Rv 600 /ssl-certs/*"]
      volumeMounts:
        - name: ssl-certs
          mountPath: /ssl-certs 

Lastly, the ports used to connect to Traefik from the internet need to be configured. By default, the Traefik Helm chart refers to port 443 as websecure, and port 80 as web. We can configure web traffic on port 80 to be redirected to port 443, to ensure all web traffic to Traefik is encrypted. We can also define which certificate resolver to use on port 443:

ports:
  web:
    redirectTo: websecure
  websecure:
    tls:
      enabled: true
      certResolver: letsencrypt

Additional configuration options and default values for Traefik’s static configuration when installing with Helm can be found in the Traefik Helm chart GitHub.

To install Traefik with your static configuration YAML file with Helm:

  • Add the Traefik Helm repo: helm repo add traefik https://helm.traefik.io/traefik
  • Update the Helm chart repository: helm repo update
  • Install the Traefik Helm chart with your static configuration file: helm install traefik/traefik -values traefik-static-configuration.yaml

After Traefik has been installed, we can use Traefik to expose Kubernetes services.

First of all, we need to define a Kubernetes deployment. For example, an Nginx deployment exposed on port 5000:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 5000

Next, we need to define a ClusterIP service:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: ClusterIP
  ports:
  - port: 5000
    targetPort: 5000
  selector:
    app: nginx

Lastly, we create an Ingress definition which includes the Traefik dynamic configuration:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
spec:
  rules:
  - host: "cloudcover.ch" 
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx
            port:
              number: 5000

A big thank you to The Digital Life on YouTube, who provide templates for this deployment method on their GitHub page.