January 25, 2022 •
In this article we will install and configure Traefik 2.5 as an ingress controller to enable IngressRoutes with TLS on a self-managed Kubernetes cluster.
There is a list of quite a few third party ingress controllers supported by Kubernetes on their official website Kubernetes Ingress Controllers. Our choice fell on Traefik because this proxy, among other nice features, supports Let's Encrypt certificates. Let's Encrypt is a Certificate Authority which provides free SSL certificates, so we can esatblish secure connections to our applications. For demonstration, we are going to deploy a test application using this feature in this article.
We are going to use Kubernetes custom resource definitions (CRDs) and IngressRoute objects instead of Kubernetes native Ingress. The difference between the latter two is that IngressRoute is not a native Kubernetes object. Its functionality is more extendable, as it is defined by Traefik, in this case, and not by Kubernetes.
Most parts from the code below can be copied just as they are, we will separately point out what can/should be altered to use in your environment.
First, we need to create a manifest with
ClusterRoleBinding. With these objects we tell Kubernetes what functionality we want to use from Traefik. We took the manifests with the CustomResourceDefinitions (CRD), ClusterRoleBindings (CRB) and ClusterRole (CR) from the official Traefik documentation.
Through the link above, you can also find descriptions to every configuration parameter.
We have created a single yaml-file with all the above-mentioned objects and named it
In order to apply it to your kubernetes cluster, use the following command:
kubectl apply -f traefik-api.yaml
In one of the next steps we are going to create an IngressRoute for secure connections - which means that Traefik needs some place where it can store certificates and keys and where they will stay even if the Traefik pod will be recreated.
For this purpose we will create a
PersistentVolumeClaim that will be used by the Traefik pod:
apiVersion: v1 kind: PersistentVolume metadata: name: taefik-pv spec: capacity: storage: 32Mi accessModes: - ReadWriteOnce hostPath: path: /data/traefik --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: traefik-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 32Mi
In the example above (see lines 10-11) certificate data will be stored under
/data/traefik on the host. Make sure you remember how you named your PersistentVolume (line 4) and PersistentVolumeClaim (line 17), you will need these names when deploying Traefiks pod.
You can adjust these lines if required and then create the resources:
kubectl apply -f traefik-pvc.yaml
Now we can deploy a Traefik pod. In the following manifest we include three types of objects:
ServiceAccountwe create an account to authenticate the Traefik pod, that speaks for itself.
Deploymentand not a
DaemonSetbecause for a single-node cluster it is completely sufficient: the
Deploymentwill create one pod on the node. If you have provisioned a multi-node cluster you might consider using a
DaemonSet, so that on every node a copy of a Traefik pod is created. If you add a node later, a pod will be created on it automatically.
Servicewill allow the pods to be accessible through the DNS-name of the
There are few things you need to note and might need to edit in the yaml below:
ServiceAccountin the Traefik deployment (line 24).
ServiceAccountyou should enter the name for
Deploymentwe specify, among other things, the name of our certificate resolver. In our case it is
certResolver. We will use this name in the
IngressRouteof our test application to reference the certificate resolver.
portsin the Deployment we have three endpoints:
webfor normal HTTP requests,
websecurefor encrypted connections using TLS (here the Let's Encrypt certificates are used), and
adminfor the Traefk dashboard.
Servicesection have the same names and matching port numbers - this is how Traefik will forward incoming traffic to the specific ports.
apiVersion: v1 kind: ServiceAccount metadata: name: traefik-ingress-controller namespace: default --- kind: Deployment apiVersion: apps/v1 metadata: name: traefik-deployment namespace: default labels: app: traefik-deployment spec: replicas: 1 selector: matchLabels: app: traefik-deployment template: metadata: labels: app: traefik-deployment spec: serviceAccountName: traefik-ingress-controller volumes: - name: traefik-pv persistentVolumeClaim: claimName: traefik-pvc containers: - name: traefik-application image: traefik:v2.5 volumeMounts: - name: traefik-pv mountPath: "/acmeStorage" args: - --api - --api.insecure - --api.dashboard=true - --accesslog - --entrypoints.web.Address=:80 - --entrypoints.websecure.Address=:443 - --providers.kubernetescrd - --certificatesresolvers.certResolver.acme.storage=/acmeStorage/acme.json - --certificatesresolvers.certResolver.acme.httpchallenge - --certificatesresolvers.certResolver.acme.httpchallenge.entrypoint=web - --certificatesresolvers.certResolver.firstname.lastname@example.org - --providers.kubernetescrd=true - --entrypoints.web.http.redirections.entryPoint.to=websecure - --entrypoints.websecure.http.tls.certResolver=default ports: - name: web containerPort: 80 - name: websecure containerPort: 443 - name: admin containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: traefik-service namespace: default spec: ports: - protocol: TCP name: web port: 80 - protocol: TCP name: admin port: 8080 - protocol: TCP name: websecure port: 443 selector: app: traefik-deployment
Now Traefik is installed and configured, and we can deploy a test application.
We will create a simple Nginx "Hello World" deployment. Both the deployment object and the application service are included in the same manifest.
apiVersion: apps/v1 kind: Deployment metadata: name: helloworld-deployment namespace: default labels: app: helloworld-deployment spec: replicas: 1 selector: matchLabels: app: helloworld-deployment template: metadata: labels: app: helloworld-deployment spec: containers: - name: helloworld-application image: nginxdemos/hello ports: - name: web containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: helloworld-service namespace: default spec: selector: app: helloworld-deployment ports: - protocol: TCP name: web port: 80
Note the name of the service (line 29) - you will need to specify it later it in the
Before we can create an IngressRoute for the newly deployed application, we need to make the Cluster available from the internet. For more details on port-forwarding, please see Traefik documentation on port-forwarding. The Traefik ACME provider will try to issue certificates as soon as it detects our IngressRoutes, and it will not work because if our pods cannot be reached from the outside.
kubectl port-forward --address 0.0.0.0 service/traefik 80:80 8080:8080 443:4443 -n default
Another way to do, which can be more practical for tests, is to save the command as bash-script and to run it in the background:
#!/bin/bash nohup kubectl port-forward --address 0.0.0.0 service/traefik-service 80:80 8080:8080 443:4443 &
Keep in mind, that this way is not suitable for a production environments - if suddenly the script crashes the whole cluster will be cut from the Internet.
In this final step we will create an IngressRoutes which uses TLS for a secure connection to the application. Here, we need to specify the name of the application service so that Traefik knows where to forward the incoming traffic to.
There are a couple of points you might want to change here:
entryPointsin this case the IngressRoute will use the
webSecureentry point we configured in our Traefik installation.
match(line 10) to your domain name.
servicesthere should be the name you gave to the application service.
apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: helloworld-ingress namespace: default spec: entryPoints: - websecure routes: - match: Host(`your.domain.com`) kind: Rule services: - name: helloworld-service port: 80 tls: certResolver: certResolver
After a couple of seconds after applying the script to the cluster, it should be possible to access your application online.
If you want to have an insecure connection just change
web and skip
tls part. If
tls part is specified Traefik will ignore non TLS requests, so if you want to have both HTTP and HTTPS requests to be handled, you will need to specify two
We have deployed and configured Traefik 2.5 in Kubernetes and afterwords deployed and exposed a test application using a secured connection with the help of Let's Encrypt certificates.