January 25, 2022 •
written by:
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 CustomResourceDefinitions
, ClusterRole
and 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 traefik-api.yaml
.
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 PersistentVolume
and 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: ServiceAccount
, Deployment
and Service
.
ServiceAccount
we create an account to authenticate the Traefik pod, that speaks for itself.Deployment
and not a DaemonSet
because for a single-node cluster it is completely sufficient: the Deployment
will 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.Service
will allow the pods to be accessible through the DNS-name of the Service
.There are few things you need to note and might need to edit in the yaml below:
ServiceAccount
in the Traefik deployment (line 24).Deployment
just under ServiceAccount
you should enter the name for PersistentVolume
and PersistentVolumeClaim
.args
in te Deployment
we specify, among other things, the name of our certificate resolver. In our case it is certResolver
. We will use this name in the IngressRoute
of our test application to reference the certificate resolver.ports
in the Deployment we have three endpoints: web
for normal HTTP requests, websecure
for encrypted connections using TLS (here the Let's Encrypt certificates are used), and admin
for the Traefk dashboard.Service
section 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.acme.email=mail@example.com
- --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 IngressRoute
manifest.
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:
websecure
under entryPoints
in this case the IngressRoute will use the webSecure
entry point we configured in our Traefik installation.match
(line 10) to your domain name.services
there should be the name you gave to the application service.certResolver
.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 entryPoints
to web
and skip
the 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 IngressRoutes
.
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.