If you searched for this article I chances are you know what LKE, Traefik and Let's Encrypt are, else here is a quick primer:
"The Linode Kubernetes Engine (LKE) is a fully-managed container orchestration engine for deploying and managing containerized applications and workloads.". For more information please have a look at "Deploy and manage a cluster with Linode Kubernetes Engine"
Traefik "is an open source edge router" specifically designed for clusters in order to provide dynamic routing configuration and service discovery and is "natively compilant with every major cluster technology".
Let's Encrypt was brought to live by the Internet Security Research Group in order to provide free and autoconfigurable SSL certificates for everyone.
https://letsencrypt.org/about/
In this guide you will learn how you can provide Let's Encrypt SSL certificates for you web application in LKE using Traefik's dynamic configuration and routing.
Traefik's documentation does already provide enough information to deploy working traefik routing onto your cluster. We can directly use their CRD and RBAC definitions which you can find in Kubernetes CRD or directly from Github:
- Deploy Custom Resource Definitions
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/v2.2/docs/content/reference/dynamic-configuration/kubernetes-crd-definition.yml
- Deploy RBAC
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/v2.2/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml
These resources configure Kubernetes to allow us to create new resources which handle routing for your services. You can find an example of available resources here.
Before we can use the new traefik resources we need to deploy traefik itself, but instead of using their example deployment we will create a new definition that is a little bit more elaborate.
Instead of configuring traefik with container arguments, we will deploy traefik with a ConfigMap.
manifests/configmap.yml
:apiVersion: v1 kind: ConfigMap metadata: name: traefik namespace: default data: traefik.yaml: | log: level: DEBUG providers: kubernetescrd: {} certificatesResolvers: myresolver: acme: email: [email protected] storage: /traefik/acme/acme.json caserver: https://acme-staging-v02.api.letsencrypt.org/directory tlsChallenge: {} entryPoints: web: address: ":8000" http: redirections: entryPoint: to: ":443" scheme: https websecure: address: ":4443"
This configuration tells traefik to create two endpoints, one named web
and the other websecure
.
The web
endpoint listens on port :8000
and redirects any http
traffic to port :443
with the https
schema.
The second endpoint, websecure
listens on port :4443
and accepts incoming traffic.
Note that the loglevel is set to DEBUG
and the acme caserver
is pointed to the staging server.
Change the log.level
to INFO
and remove the caserver
field once you know that everything is working.
The staging server does not issue valid SSL certificates but has a much higher rate limit for testing purposes.
The redirect from web
to port 443
is a little bit confusing here because we didn't talk about how traffic gets routed to your cluster anyway.
As exaplained in What is Traefik?, traffic handled by trefik is routed based upon dynamic configuration which traefik receives from Kubernetes, however unlike general implementations of traefik, we do not deploy Traefik with a NodePort
nor as a DaemonSet
because we rely on the LoadBalancer
resource to create a Linode Node Balancer to point a domain to.
Here is a small sketch of the flow:
|
Public Web | Linode Cloud
|
+---------------+ |:80 +-----------------------+
| http +--------> |
+----------------+ | | TCP Load Balancer |
| https +--------> |
+--------^-------+ |:443 +-----------------------+
¦ | ¦http:8000 ¦https:4443
¦ | ¦ ¦
¦ |--------¦-----------¦-------------------
¦ | +--v--+ +----v--------+ LKE |
+----------------+ web | | websecure | |
| +-----+ +----+--------+ |
| ¦ |
| +--------------v--------+ |
| | | |
| | dynamic services | |
| | | |
| +-----------------------+ |
In other words, we will create a Service
resource of type LoadBalancer
configured to proxy all TCP traffic from public port :80
to Traefik and all TCP traffic from port :443
to Traefik on port :4443
.
manifests/service.yml
:apiVersion: v1 kind: Service metadata: name: traefik-lb spec: type: LoadBalancer ports: - name: web port: 80 protocol: TCP targetPort: 8000 - name: websecure port: 443 protocol: TCP targetPort: 4443 selector: app: traefik sessionAffinity: None
The Linode CCM will use this resource to create a Linode NodeBalancer which you can use later to point your domain(s) to.
Last but not least we will create the actual traefik deployment. For simplicity this example won't properly store assigned acme certificates. If you need to store those certificates you can either use a shared volume you created or an etcd key value backend.
manifests/deployment.yml
:kind: Deployment apiVersion: apps/v1 metadata: name: traefik namespace: default labels: app: traefik spec: replicas: 1 selector: matchLabels: app: traefik template: metadata: labels: app: traefik spec: serviceAccountName: traefik-ingress-controller containers: - image: traefik:v2.2 name: traefik ports: - name: web containerPort: 8000 - name: websecure containerPort: 4443 args: - --configfile=/config/traefik.yaml volumeMounts: - mountPath: /config name: config - mountPath: /traefik/acme name: acme volumes: - name: config configMap: name: traefik - name: acme emptyDir: {}
Now just deploy the resources:
kubectl apply -f manifests/configmap.yaml
kubectl apply -f manifests/deployment.yaml
kubectl apply -f manifests/service.yaml
Once all the pods have been deployed we can move on to deploy a sample backend.
If you do not already have an application at hand that you deployed to your cluster you can follow this handy guide.
You can follow the guide completely up until you create the Service
resource. As we already have a LoadBalancer
configured (which points to traefik), we only need to create a headless service and a traefik resource.
manifests/static-site-service.yml
:apiVersion: v1 kind: Service metadata: name: static-site-service labels: app: static-site spec: ports: - name: http port: 80 protocol: TCP targetPort: 80 selector: app: static-site clusterIP: None
manifests/ingress-route.yml
:apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: static-site-route spec: entryPoints: - websecure routes: - match: Host(`www.example.com`) kind: Rule services: - name: static-site-service passHostHeader: true port: 80 tls: certResolver: myresolver
The IngressRoute
resource will instruct traefik to use its settings for routing traffic from the outside to a kubernetes service. The tls.certResolver
options instructs traefik to fetch an SSL certificate from Let's Encrypt as configured in traefik's ConfigMap
.
Now when you open your site you should see your new certificate. Once you validated the certificate you can remove the caserver
entry from the ConfigMap
and redeploy manifests/deployment.yml
Just for your reference, https://medium.com/dev-genius/setup-traefik-v2-for-ha-on-kubernetes-20311204fa6f