Traefik in actions. The Cloud Native Edge Router

Cover image taken from traefik docs

Table of contents generated with markdown-toc

What is Traefik

Traefik is a leading modern reverse proxy and load balancer that makes deploying microservices easy. Traefik integrates with your existing infrastructure components and configures itself automatically and dynamically.

Some of the features are

Learn more on Traefik

  • Routing & load balancing
  • Security
  • Dynamic Configuration
  • Observability

Traefik also supports multiple providers & orchestrators, and today I will be talking about Kubernetes.

Why Traefik

I have using Google Kubernetes Engine (GKE) for awhile, and default Ingress for GKE is GCE Ingress, which uses Google Cloud Load Balancer under the hood. GCE Ingress is great, it is global service, has optional configuration for dashboard, logging, CDN, Anti DDOS (Cloud Armor), managed certificate and etc. But it lacks of essential features such as basic auth, https redirect, and required kubernetes service type set to NodePort to works. Furthermore, it could incur high cost. It will charge on the following:

  • it needs one static IP per Ingress, charge hourly.
  • Cloud Load Balancer
  • CDN, DDOS protection if any
  • Network egress

The cost will multiply if you have multiple ingresses running.

With all the reason above, I decide to try out other Ingress, and here comes Traefik.

Get started with Traefik

traefik-concepts-2.png

Image take from Traefik docs

Traefik is the only container that accessible from outside, in other words, it is the entrypoint of your services, possible with multiple domains, with only single IP address

Installation

I will walk through the demo on Google Cloud (GKE and Google DNS) with terraform.

The source code can be found at cncf-demo

If you wish to follow along, make sure you have Google Cloud project created, and download service account with appropriate IAM roles assigned, to be used with terraform.

Traefik can be configured to use an ACME provider (like Let's Encrypt) for automatic certificate generation. So we need to attach Persistence Disk to Kubernetes pods, to persist the certificate configuration. Additional configuration can be view at values.yaml.

git clone https://github.com/WLun001/cncf-demo
cd terraform/gke-traefik
terraform init
# it will provision GKE cluster
# and install traefik with helm
terraform apply

optionally you can create terraform.tfvars to store variables for terraform

Get Traefik resouces

Once terraform process is completed, you can view the deployment on GKE dashboard or via kubectl

Make sure kubectl is authenticated to access your cluster

$ kubectl get deploy,svc -n traefik -L=app.kubernetes.io/instance: traefik


NAME                      READY   UP-TO-DATE   AVAILABLE   AGE     INSTANCE:
deployment.apps/traefik   1/1     1            1           2d17h

NAME              TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                      AGE     INSTANCE:
service/traefik   LoadBalancer   10.3.247.160   SOME_EXTERNAL_IP   80:32757/TCP,443:30535/TCP   2d17h

You should be able to access Traefik on the external IP on port 80

Create sample apps

Let's create some sample apps, to be routed by Traefik

cd kubernetes
kubectl apply -f whoami.yaml -f zone-printer.yaml

Create Traefik routes

create a new file call myroutes.yaml add the following


apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: myingressroute
  namespace: default

spec:
  entryPoints:
    - web
  routes:
    - match: PathPrefix(`/`)
      kind: Rule
      services:
        - name: zone-printer-service
          port: 80
    - match:  PathPrefix(`/whoami`)
      kind: Rule
      services:
        - name: whoami
          port: 80
    - match: (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
      kind: Rule
      services:
        - name: api@internal
          kind: TraefikService

apply the routes

kubectl apply -f myroutes.yaml

Now you should able to access the following routes

  • / - it will print the cluster zone, if you are on GKE
  • /whoami - it will print some information from whoami
  • /dashboard/ - Traefik dashboard

Screenshot 2020-10-29 at 3.34.25 PM.png

Bind Traefik routes with custom domain

Add the external IP to your DNS providers,

and edit the following for myroutes.yaml, replace traefik.example.com with your domain

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: myingressroute
  namespace: default

spec:
  entryPoints:
    - web
  routes:
    - match: Host(`traefik.example.com`) && PathPrefix(`/`)
      kind: Rule
      services:
        - name: zone-printer-service
          port: 80
    - match: Host(`traefik.example.com`) && PathPrefix(`/whoami`)
      kind: Rule
      services:
        - name: whoami
          port: 80
    - match: Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
      kind: Rule
      services:
        - name: api@internal
          kind: TraefikService

apply the routes

kubectl apply -f myroutes.yaml

After a few minutes, wait for DNS update, you can only access the service via domain, can't access via external IP anymore.

Automatic HTTPS

Traefik support automatic https generation with multiple providers, more on docs. I will walk through Google DNS as DNS provider, the flow would be similar.

  1. Create service account for with DNS Administrator role assigned
  2. Download the service account
  3. Create kubernetes secret with the service account
kubectl create secret generic google-dns-sa \
  --from-file=key.json=your-service-account.json -n traefik

Edit Traefik deployment yaml

This can be done through helm charts, or edit the file manually with kubectl edit or kubectl patch

note that this example we using let's encrypt staging server,

  • Let's Encrypt production server: https://acme-v02.api.letsencrypt.org/directory
  • Let's Encrypt staging server: https://acme-staging-v02.api.letsencrypt.org/directory

Using Helm charts

add the following to existing values.yaml, complete yaml file can be found on values.yaml

additionalArguments:
  - "--certificatesResolvers.myresolver.acme.dnsChallenge.provider=gcloud"
  - "--certificatesresolvers.myresolver.acme.email=youremail@email.com"
  - "--certificatesresolvers.myresolver.acme.storage=/data/acme.json"
    # Please note that this is the staging Let's Encrypt server.
  # Once you get things working, you should remove that whole line altogether.
  # if you dont persistence enabled
  - "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"

env:
  - name: GCE_PROJECT
    value: YOUR_PROJECT_ID
  - name: GCE_SERVICE_ACCOUNT_FILE
    value: /var/secret/google/key.json
  - name: GOOGLE_APPLICATION_CREDENTIALS
    value: /var/secret/google/key.json

volumes:
  - name: google-dns-sa
    mountPath: /var/secret/google
    type: secret

Using kubectl edit or kubectl patch

The follow yaml file highlights the changes required, complete yaml file can be view at example-traefik.yaml

apiVersion: apps/v1
kind: Deployment
....
spec: 
  template: 
     spec: 
       containers: 
         args: 
           ...
            - --certificatesResolvers.myresolver.acme.dnsChallenge.provider=gcloud
            - --certificatesresolvers.myresolver.acme.email=youremail@email.com
            - --certificatesresolvers.myresolver.acme.storage=/data/acme.json
            # Please note that this is the staging Let's Encrypt server.
#             Once you get things working, you should remove that whole line altogether.
             - --certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
          env: 
            - name: GCE_PROJECT
              value: YOUR_PROJECT_ID
            - name: GCE_SERVICE_ACCOUNT_FILE
              value: /var/secret/google/key.json
            - name: GOOGLE_APPLICATION_CREDENTIALS
              value: /var/secret/google/key.json
         ....
         volumeMounts: 
           ...
           - mountPath: /var/secret/google
             name: google-credentials-volume
      ...
     volumes:
       ...
       - name: google-credentials-volume
          secret:
            secretName: google-dns-sa

Add https routes

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: myingressroute-secure
  namespace: default

spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`traefik.example.com`) && PathPrefix(`/`)
      kind: Rule
      services:
        - name: zone-printer-service
          port: 80
    - match: Host(`traefik.example.com`) && PathPrefix(`/whoami`)
      kind: Rule
      services:
        - name: whoami
          port: 80
    - match: Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))

  tls:
    certResolver: myresolver
    domains:
      - main: "*.example.com"

apply the routes

kubectl apply -f myroutes.yaml

If you need to see the error log , if there is any, can check with kubectl. If any error with https generation, it will be visible here.

kubectl logs -f deployment/traefik -n traefik

Wait a few minutes, you should see the cert provision successfully.

If you are using staging server, the root cert will be from Fake LE Intermediate X1. I am using production server, so the browser showing the certificate is verify. Browser will complain not secure for staging server.

Screenshot 2020-10-29 at 3.58.15 PM.png

Multiple domains

you can add more domains by adding config to routes array on yaml, and match by domain

routes:
    - match: Host(`domain1`) && PathPrefix(`/`)
      kind: Rule
      services:
        - name: zone-printer-service
          port: 80
    - match: Host(`domain2`) && PathPrefix(`/whoami`)
      kind: Rule
      services:
        - name: whoami
          port: 80

HTTPS redirect

Traefik provides middleware feature, one of the use case is https redierct

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: redirect-https
spec:
  redirectScheme:
    scheme: https

add to the service

    - match: Host(`example.com`) && PathPrefix(`/`)
      kind: Rule
      services:
        - name: whoami
          port: 80
      middlewares:
        - name: redirect-https

if you open your domain, it should auto redirect to https

Complete working example of IngressRoute can be found ingress-route.yaml

Clean up

All cloud resources cost money, make sure you clean up after experiment.

cd terraform/gke-traefik
terraform destroy

The source code can be view at cncf-demo. That's all for today, if you have any idea, feel free to comment below.

Reference

No Comments Yet