Traefik can be used as an Ingress Controller for your Kubernetes Cluster. In this guide I demonstrate how to deploy Traefik quickly and simply, for you to evaluate and start using.
All configuration demonstrated here, is available on github.
Why do I need an Ingress Controller?
If you want to route traffic from outside your cluster, to a service running inside your cluster, you could use the concept of Ingresses and Ingress Controllers.
Ingress is the resource which defines the routing rules. That is, which external traffic routes to which internal service.
Ingress Controllers are the software required to implement those ingress rules. Kubernetes does not come with an Ingress Controller out of the box, so one must be installed in order to use the Ingress resource.
There are other options for routing traffic into your cluster. These include NodePort type Services or a LoadBalancer Service, and this other Medium article does a superb job at explaining the differences between the three different approaches.
Development Environment
To begin getting started and installing Traefik, you will need a running Kubernetes cluster. If you do not have one available, kind is a good option. If you do make use of kind, here is a cluster.yml definition which will enable traffic to pass from your host into the cluster.
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
noteRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
You can make use of this cluster config (assuming saved as cluster.yml, if not, adjust command appropriately) by running:
kind create cluster --config cluster.yml --name traefik-demo
Getting started
In this example, we will deploy Traefik with the Daemonset resource type. A Daemonset is a simple way to get started and will deploy an instance of Traefik on every node.
We will also declare each resource required, in yaml.
Namespace
To begin with, create a new namespace just for Traefik. This makes it a bit easier to see what is going on with Traefik by isolating the resources only associated with Traefik.
kind: Namespace
apiVersion: v1
metadata:
name: traefik
Custom Resource Definitions
Next, we will declare some Custom Resource Definitions that Traefik offer. These provide more advanced functionality, for example the IngressRoute provides enhanced functionality compared to the out of the box Ingress, but is conceptually the same thing.
In this guide, we only make use of the IngressRoute, but if one Custom Resource Definition is defined, they all must be.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressroutes.traefik.containo.usspec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRoute
plural: ingressroutes
singular: ingressroute
scope: Namespaced---apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: middlewares.traefik.containo.usspec:
group: traefik.containo.us
version: v1alpha1
names:
kind: Middleware
plural: middlewares
singular: middleware
scope: Namespaced---apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressroutetcps.traefik.containo.usspec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRouteTCP
plural: ingressroutetcps
singular: ingressroutetcp
scope: Namespaced---apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressrouteudps.traefik.containo.usspec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRouteUDP
plural: ingressrouteudps
singular: ingressrouteudp
scope: Namespaced---apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: tlsoptions.traefik.containo.usspec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TLSOption
plural: tlsoptions
singular: tlsoption
scope: Namespaced---apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: tlsstores.traefik.containo.usspec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TLSStore
plural: tlsstores
singular: tlsstore
scope: Namespaced---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: traefikservices.traefik.containo.usspec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TraefikService
plural: traefikservices
singular: traefikservice
scope: Namespaced
RBAC
Next, define the RBAC resources required to grant Traefik the necessary privileges to function as the Ingress Controller.
apiVersion: v1
kind: ServiceAccount
metadata:
name: traefik
namespace: traefik---kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: traefik
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- update
- apiGroups:
- traefik.containo.us
resources:
- middlewares
- ingressroutes
- traefikservices
- ingressroutetcps
- ingressrouteudps
- tlsoptions
- tlsstores
verbs:
- get
- list
- watch---kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: traefik
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik
subjects:
- kind: ServiceAccount
name: traefik
namespace: traefik
Daemonset
Next, we define a Daemonset to initiate a pod running Traefik.
Importantly, we define a hostPort to containerPort mapping on ports 80 and 443, which allows traffic to flow from the host through to the pods running Traefik. The traffic has to reach Traefik so it can then route it appropriately internally to the cluster.
Traefik itself must also be configured to listen on those ports as well, and this is done by specifying entryPoints as part of the arguments passed to Traefik, as seen in the args section of the configuration.
kind: DaemonSet
apiVersion: apps/v1
metadata:
name: traefik
namespace: traefik
spec:
selector:
matchLabels:
name: traefik
template:
metadata:
labels:
name: traefik
spec:
serviceAccountName: traefik
terminationGracePeriodSeconds: 60
containers:
- image: 'traefik:v2.3'
name: traefik-ingress
ports:
- name: web
containerPort: 80
hostPort: 80
- name: websecure
containerPort: 443
hostPort: 443
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
args:
- '--accesslog=true'
- '--api.dashboard'
- '--entryPoints.web.address=:80'
- '--entryPoints.websecure.address=:443'
- '--log.level=info'
- '--providers.kubernetescrd'
To confirm the pod is up and running, check the status with:
kubectl get pods -n traefik
And look for an output such as:
NAME READY STATUS RESTARTS AGE
traefik-q6rb8 1/1 Running 0 30s
Once the Traefik pod is running, we can check Traefik is working by trying to visit https://localhost in the browser. You should receive a 404 page not found error. This is a good sign as we have not configured any IngressRoutes, but we are at least getting some response.
If you are running on a remote Kubernetes cluster, you will need to go to the domain name configured with your remote cluster.
IngressRoute
Finally, we can define an IngressRoute. This IngressRoute opens up the Traefik dashboard over HTTP, allowing you to see how Traefik is currently configured to route traffic. Once the IngressRoute is installed, you will be able to access the dashboard on: http://localhost/dashboard/ (The trailing slash is required).
Similar to before, if you are running on a remote Kubernetes cluster, you will need to adjust the Host matching rule for that cluster’s domain name.
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: traefik-gui
namespace: traefik
spec:
entryPoints:
- web
routes:
- match: Host(`localhost`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))
kind: Rule
services:
- name: api@internal
kind: TraefikService
Alternatively, if you wish to enable HTTPS to access the dashboard, you can configure your IngressRoute as follows. Change the entryPoint to websecure, which is the name assigned to port 443 in the Daemonset, and define an empty tls block. This empty block instructs Traefik to revert to default, self-signed certificates, and as a result may trigger warnings in the browser when you navigate to the dashboard.
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: traefik-gui
namespace: traefik
spec:
entryPoints:
- websecure
routes:
- match: Host(`localhost`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))
kind: Rule
services:
- name: api@internal
kind: TraefikService
tls: {}
The api@internal named service is a special service that is created under the hood by Traefik if you specify the api.dashboard to the pod in the daemonset, as we did.
Installing a demo service
We can further demo Traefik, by routing traffic to an installed service on the cluster.
This is a Google sample Service and Deployment, which installs an image that simply returns a webpage that reads “Hello Kubernetes!”, that you can install.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-kubernetes
namespace: traefik
spec:
selector:
matchLabels:
name: hello-kubernetes
replicas: 1
template:
metadata:
labels:
name: hello-kubernetes
spec:
containers:
- name: hello-world
image: gcr.io/google-samples/node-hello:1.0
ports:
- containerPort: 8080
protocol: TCP---apiVersion: v1
kind: Service
metadata:
name: hello-kubernetes
namespace: traefik
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
selector:
name: hello-kubernetes
Now, we can adjust our IngressRoute to define a new Match Rule.
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: traefik-gui
namespace: traefik
spec:
entryPoints:
- websecure
routes:
- match: Host(`localhost`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))
kind: Rule
services:
- name: api@internal
kind: TraefikService
- match: Host(`localhost`) && PathPrefix(`/hello-kubernetes`)
kind: Rule
services:
- name: hello-kubernetes
kind: Service
port: 8080
tls: {}
Unlike the special api@internal service, here we specifically reference the name and port pair that we defined in the hello-kubernetes service.
This IngressRoute now matches for traffic going to the Traefik dashboard and our hello-kubernetes service. The hello-kubernetes Service is now available on https://localhost/hello-world
Conclusion
This is a quick way to set up Traefik v2 as an Ingress Controller for your Kubernetes cluster. If you wish to run Traefik in production, you may want to consider using a Deployment/Service instead of a Daemonset to give you the benefit of scaling.
As mentioned initially, all configuration demonstrated here, is available on github.