Small migration guide from Traefik 1 to Traefik 2
I recently moved the front of this blog from Traefik 1 to Traefik 2, and to say the least, it's no picnic.
My Traefik use case
I use Traefik as a load balancer/reverse proxy front in a Kubernetes infrastructure. My use is very basic. Depending on certain path and/or domain, I redirect to separate pods. In the case below, I will consider that I have only one pod, this blog. I also manage my certificates with Traefik via Let's Encrypt.
Although this use case may seem basic, it took me several hours to upgrade from Traefik 1.7.20 to version 2.1.1.
Where the documentation promises a simple migration, it is unfortunately not the case...
Why not? Because the documentation takes a lot of shortcuts on some points and is very succinct on others. And that's where the problem is, if the implementation of Traefik 1 was simple, the multiplicity of possibilities with the new version made the configuration confusing.
So I propose you below to highlight the points that I changed between the two versions. I won't go back to the installation from scratch of Traefik, which you can find on their website.
Important information about the YAML code blocks below: I made the choice to install Traefik in a dedicated namespace. The codes below contain a reference to this namespace. Be careful in case you want to copy/paste to update this point.
Prerequisites for installation: CRDs (Custom Resources Definitions) and ClusterRole update
To be able to implement the new version of Traefik in a simple way, it is first necessary to create CRDs, which will, very roughly, define new types of resources that the Traefik controller will be able to control. I won't go further on CRDs here, a full article would be necessary.
So we will create a traefik2-prereq.yml file with the content below, you can find this file on the official documentation :
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressroutes.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRoute
plural: ingressroutes
singular: ingressroute
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressroutetcps.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRouteTCP
plural: ingressroutetcps
singular: ingressroutetcp
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: middlewares.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: Middleware
plural: middlewares
singular: middleware
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: tlsoptions.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TLSOption
plural: tlsoptions
singular: tlsoption
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: traefikservices.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TraefikService
plural: traefikservices
singular: traefikservice
Once this is done, we can load it into our Kubernetes cluster using the kubectl apply command:
kubectl apply -f ./traefik2-prereq.yml
Then, because of all these new resources, it is necessary to update our existing ClusterRole to allow Traefik to access this new information. So I modified my Clusterrole.yml file, which I replaced with the following content :
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
namespace: 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
verbs:
- get
- list
- watch
- apiGroups:
- traefik.containo.us
resources:
- ingressroutes
verbs:
- get
- list
- watch
- apiGroups:
- traefik.containo.us
resources:
- ingressroutetcps
verbs:
- get
- list
- watch
- apiGroups:
- traefik.containo.us
resources:
- tlsoptions
verbs:
- get
- list
- watch
- apiGroups:
- traefik.containo.us
resources:
- traefikservices
verbs:
- get
- list
- watch
Once again, once completed, we apply this change:
kubectl apply -f ./clusterrole.yml
Deployment of the new version of Traefik
Now we will create the configuration file to deploy the pod containing Traefik 2. I have chosen to deploy Traefik as a DaemonSet, but the installation is similar if you want to deploy it in a pod. So I have the daemonset.yml file which has the following content :
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: traefik-ingress-controller
namespace: traefik
labels:
k8s-app: traefik-ingress-lb
kubernetes.io/cluster-service: "true"
spec:
template:
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
spec:
hostNetwork: true
serviceAccountName: traefik-ingress-controller
terminationGracePeriodSeconds: 60
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- image: traefik:v2.1.1
name: traefik-ingress-lb
imagePullPolicy: Always
resources:
requests:
cpu: 100m
memory: 20Mi
volumeMounts:
- mountPath: "/cert/"
name: cert
args:
- --providers.kubernetescrd
- --accesslog
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --certificatesresolvers.le.acme.email=masuperadressemail@monmail.com
- --certificatesresolvers.le.acme.storage=acme.json
- --certificatesresolvers.le.acme.storage=/cert/acme.json
- --certificatesResolvers.le.acme.httpChallenge.entryPoint=web
ports:
- name: web
containerPort: 80
- name: websecure
containerPort: 443
- name: admin
containerPort: 8080
volumes:
- name: cert
hostPath:
path: /home/kube/traefik/certs/
type: Directory
As you will have understood, some points are to be adapted according to your case of use. Once again, I am not making this post to copy the official documentation, to which I refer you if you have any doubt.
Note that it is possible to activate the traefik dashboard from the command line if necessary by adding the parameter "--api.insecure" in the arguments of the launch line.
You will note the presence of the necessary setting for Let's Encrypt. It is this setting that allows me to have a TLS certificate automatically generated by Traefik. Moreover, I created a volume in hostPath to store these certificates and avoid to request them each time I update my pod.
From there, I'm in V2... but I don't have a backend anymore...
That's why we don't apply the change right away, and we will first create the necessary to communicate with our pods.
Updating of services and creation of IngressRoutes
Traefik 2 changes the way it works compared to the previous version.
Indeed, in the first version, there was this configuration :
Frontend -> Backend
Pretty basic, but enough. The new version brings new functionality, especially middleware, which interfaces between the frontend and the backend. The latter allows for example to add basic auth on an application. As a result, we now have this configuration
Frontend -> Middleware -> Backend
Moreover, with the new features brought by Traefik, it is necessary to switch from Ingress with IngressRoutes, which we created via our CRDs earlier. It is this new Kubernetes object that carries all the necessary configuration to define the routing to our backend.
So I'm going to put below the two configuration files, which I had in version 1.7.20 and 2.1.1, so that you can see the differences directly.
Configuration file 1.7.20
apiVersion: v1
kind: Service
metadata:
name: traefik-web-ui
namespace: traefik
spec:
selector:
k8s-app: traefik-ingress-lb
ports:
- port: 8080
targetPort: 8080
name: api
- port: 443
targetPort: 443
name: https
- port: 80
targetPort: 80
name: http
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: traefik-web-ui
namespace: default
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- http:
paths:
- path: /
backend:
serviceName: ghost-tfe-fr
servicePort: 2368
Here is the equivalent on the new version:
apiVersion: v1
kind: Service
metadata:
name: traefik-web-ui
namespace: traefik
spec:
selector:
k8s-app: traefik-ingress-lb
app: traefik-ingress-lb
ports:
- port: 8080
targetPort: 8080
name: api
- port: 443
targetPort: 443
name: websecure
- port: 80
targetPort: 80
name: web
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: traefik-web-ui
namespace: default
spec:
entryPoints:
- web
routes:
- kind: Rule
match: Host(`tferdinand.net`) && PathPrefix(`/`)
services:
- name: ghost-tfe-fr
port: 2368
helthcheck:
path: /
host: tferdinand.net
intervalSeconds: 10
timeoutSeconds: 5
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: traefik-web-ui-tls
namespace: default
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`tferdinand.net`) && PathPrefix(`/`)
services:
- name: ghost-tfe-fr
port: 2368
helthcheck:
path: /
host: tferdinand.net
intervalSeconds: 10
timeoutSeconds: 5
tls:
certResolver: le
As you can see, the new IngressRoute object carries much more information, including everything related to routing. Note that the hosts defined in the configuration files are the ones that will be used to generate the Let's Encrypt certificates at Traefik pod startup.
Now that everything is ready, we can apply our latest :
kubectl apply -f ./daemonset.yml
kubectl apply -f ./service_ingress.yml
The new version of Traefik is now normally deployed. The Let's Encrypt certificate takes one to two minutes to be imported. Please note that if you are testing configurations, I advise you to disable the TLS, indeed, Let's Encrypt has a maximum query limit, limit that you may reach, preventing you from generating certificates.
And then... ?
Now that I've finished my migration to version 2, I plan to add a finer management of authorized TLS ciphers, in order to increase the security level of my applications. I'll probably write an article about it soon.
It's completely possible to set up a much finer configuration, I haven't done a complete overview of all the new features brought by version 2.
I made this article after the observation that I made by migrating from version, indeed, although it is announced breaking changes. I must admit that I did not expect such differences.
Note that there is a migration tool made available by Traefik on GitHub, which allows to convert Kubernetes configuration files from V1 to V2. However, when I tested it :
- Some configuration points do not work and refer to the documentation
- The generated configuration was not functional
I hope this article will be useful for your Traefik migration, don't hesitate to react in the comments!
Comments ()