I wanted to try the Traefik edge router for a long time. I have recently created a DigitalOcean managed Kubernetes cluster primarily for learning and experimenting, so it seemed the perfect opportunity to install Traefik.

This guide was tested on Kubernetes 1.18 and Traefik 2.2

Prerequisites

  • a working Kubernetes cluster
  • kubectl configured and context set to cluster

0. If you don’t have a cluster

You can get $100 in credit over 60 days using this referral link.

1. Apply custom resource and RBAC definitions

Save the following definitions to a file and apply them using kubectl apply -f [filename]
The definitions can also be downloaded from the Traefik website.

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutes.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRoute
    plural: ingressroutes
    singular: ingressroute
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutetcps.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRouteTCP
    plural: ingressroutetcps
    singular: ingressroutetcp
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: middlewares.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: Middleware
    plural: middlewares
    singular: middleware
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsoptions.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TLSOption
    plural: tlsoptions
    singular: tlsoption
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: traefikservices.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TraefikService
    plural: traefikservices
    singular: traefikservice
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsstores.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TLSStore
    plural: tlsstores
    singular: tlsstore
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressrouteudps.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRouteUDP
    plural: ingressrouteudps
    singular: ingressrouteudp
  scope: Namespaced

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller

rules:
  - apiGroups:
      - ""
    resources:
      - services
      - endpoints
      - secrets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses/status
    verbs:
      - update
  - apiGroups:
      - traefik.containo.us
    resources:
      - middlewares
      - ingressroutes
      - traefikservices
      - ingressroutetcps
      - ingressrouteudps
      - tlsoptions
      - tlsstores
    verbs:
      - get
      - list
      - watch

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller

roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik-ingress-controller
subjects:
  - kind: ServiceAccount
    name: traefik-ingress-controller
    namespace: default

2. Create a service account

Save the following service account definition to a file and apply it using kubectl apply -f [filename]

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: default
  name: traefik-ingress-controller

3. Create the Traefik deployment

Save the following deployment definition to a file, substitute the YOUR_EMAIL string (in the args section) with your email address, change the Traefik image version if needed, then apply it using kubectl apply -f [filename]

It differs a bit from the one on the Traefik website. Note in the args that the ping endpoint is enabled and set to the web entrypoint, we’ll be needing this for the load balancer’s health check configuration.

kind: Deployment
apiVersion: apps/v1
metadata:
  namespace: default
  name: traefik
  labels:
    app: traefik

spec:
  replicas: 1
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik
    spec:
      serviceAccountName: traefik-ingress-controller
      containers:
        - name: traefik
          image: traefik:v2.2
          args:
            - --api
            - --entrypoints.web.Address=:8000
            - --entrypoints.websecure.Address=:4443
            - --ping.entryPoint=web
            - --providers.kubernetescrd
            - --certificatesresolvers.default.acme.tlschallenge
            - --certificatesresolvers.default.acme.email=YOUR_EMAIL
            - --certificatesresolvers.default.acme.storage=acme.json
          ports:
            - name: web
              containerPort: 8000
            - name: websecure
              containerPort: 4443
            - name: admin
              containerPort: 8080

4. Create a Traefik service and a DigitalOcean load balancer

Save the following service definition to a file and apply it using kubectl apply -f [filename]

kind: Service
apiVersion: v1
metadata:
  name: traefik-lb
  namespace: default
  annotations:
    service.beta.kubernetes.io/do-loadbalancer-healthcheck-port: "80"
    service.beta.kubernetes.io/do-loadbalancer-healthcheck-protocol: "http"
    service.beta.kubernetes.io/do-loadbalancer-healthcheck-path: "/ping"
    service.beta.kubernetes.io/do-loadbalancer-protocol: "http"
    service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443"
    service.beta.kubernetes.io/do-loadbalancer-tls-passthrough: "true"
    service.beta.kubernetes.io/do-loadbalancer-enable-proxy-protocol: "true"
    service.beta.kubernetes.io/do-loadbalancer-redirect-http-to-https: "true"
spec:
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 8000
    - name: https
      protocol: TCP
      port: 443
      targetPort: 4443
  selector:
    app: traefik
  type: LoadBalancer

Applying the definition will automatically spin up a DigitalOcean load balancer, with health check, TLS passthrough and HTTPS redirect enabled. You can find more information on configuring DigitalOcean load balancers here.

The newly created load balancer will be visible on the DigitalOcean dashboard, but it is recommended not to edit Kubernetes load balancers there. They should only be edited by using kubectl commands (modifying the service configuration below).

5. Create a DNS record for the Traefik dashboard

The public IP address of the load balancer, created in the previous step, is visible on the DigitalOcean dashboard or can be fetched with the kubectl get svc command.

NAME         TYPE           CLUSTER-IP     EXTERNAL-IP               PORT(S)                      AGE
kubernetes   ClusterIP      10.242.0.1     <none>                    443/TCP                      21h
traefik-lb   LoadBalancer   10.242.12.47   [THIS IS THE PUBLIC IP]   80:32265/TCP,443:30086/TCP   10m

Create a DNS record for the traefik.yourdomain.com subdomain pointing to the load balancer’s IP address or make sure the wildcard DNS record for your domain points to the load balancer’s IP address.

6. Make Traefik dashboard available with basic authentication

In this step we’ll make the dashboard available by defining a route for the previously created subdomain. We’ll use a middleware to add basic authentication to the dashboard while storing the credentials in a Kubernetes secret.

Save the following service account definition to a file. Substitute the USER_SECRET string in the secret definition with an encoded user-password pair created using the following command:

htpasswd -nb user password | openssl base64

Replace yourdomain.com with your domain name in the IngressRoute definition.

Apply the file using kubectl apply -f [filename]

apiVersion: v1
kind: Secret
metadata:
  name: traefik-dashboard-login
  namespace: default
data:
  users: USER_SECRET
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: traefik-dashboard-auth
  namespace: default
spec:
  basicAuth:
    secret: traefik-dashboard-login
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: traefik-dashboard-route
  namespace: default
spec:
  entryPoints:
    - websecure
  routes:
  - match: Host(`traefik.yourdomain.com`)
    kind: Rule
    services:
    - name: api@internal
      kind: TraefikService
    middlewares:
      - name: traefik-dashboard-auth
  tls:
    certResolver: default

After applying the configuration the dashboard should be available at traefik.yourdomain.com.

Dashboard screenshot

Dashboard screenshot

Update notes

2020-12-06 - The guide was updated to use DigitalOcean’s Kubernetes resource based load balancers and Traefik v2.2.