Hybrid Ingress Routing with AWS ALB and Traefik

Traefik is an excellent declarative ingress controller for Kubernetes. If you don't need all the features of cloud-managed Application Load Balancers, Traefik can be a fantastic alternative.

However, there are certain limitations of Traefik on cloud platforms. It can handle the TLS/SSL layer only through Kubernetes Secret or Let's Encrypt, meaning it cannot leverage AWS ACM for resolving SSL certificates. This limitation stems from AWS ACM itself, as it can only be used with resources like ALB and CloudFront.

However, you may want to have both 1) SSL termination by ACM and 2) support for Traefik-specific features (Middleware, Circuit Breaker, etc.) all in one place. Fortunately, I have tested a system that allows you to continue using an AWS ACM-provided SSL certificate while handling ingress routing with Traefik. Here is the high-level architecture of the proposed system:

To set up this system, you'll first need an EKS cluster with the AWS Load Balancer Controller add-on. You can bootstrap the cluster using eksctl.

Once the cluster is ready, deploy Traefik in web mode only, disabling the websecure entrypoint.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: traefik
spec:
  selector:
    matchLabels:
      app: traefik
  template: 
    metadata:
      labels:
        app: traefik
    spec:
      serviceAccountName: traefik
      containers:
      - image: docker.io/traefik:v3.0
        name: traefik
        ports:
        - name: "web"
          containerPort: 80
        args:
          - "--entrypoints.web.address=:80/tcp"
          - "--api.dashboard=false"
          - "--providers.kubernetescrd"

Next, create a service for the Traefik deployment/daemonset and set its type to NodePort (ALB requires the service to be of NodePort type for its backend).

apiVersion: v1
kind: Service
metadata:
  name: traefik
spec:
  ports:
  - name: web
    nodePort: 30080
    port: 80
    protocol: TCP
    targetPort: web
  selector:
    app: traefik
  type: NodePort

Next, create an ALB Ingress with the appropriate annotations for ACM UUID, Name, and Class.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    alb.ingress.kubernetes.io/backend-protocol: HTTP
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-southeast-1:0000000000:certificate/cb6cf41e-a6f4-4fd3-9aa5-44503f317420
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
    alb.ingress.kubernetes.io/load-balancer-name: my-lb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/success-codes: 200-404
    alb.ingress.kubernetes.io/target-type: instance
  name: traefik
spec:
  ingressClassName: alb
  rules:
  - http:
      paths:
      - backend:
          service:
            name: traefik
            port:
              name: web
        path: /*
        pathType: ImplementationSpecific

Once applied, an ALB will be created. You can then route any HTTPS traffic through the ALB, and ultimately, it will be routed using Traefik within your Kubernetes Cluster. A sample IngressRoute object would look like this:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: my-apps
spec:
  entryPoints:
  - web
  routes:
  - kind: Rule
    match: Host(`a.example.com`)
    services:
    - name: app-a
      port: 80
  - kind: Rule
    match: Host(`b.example.com`)
    services:
    - name: app-b
      port: 80