Kubernetes : Utiliser Traefik comme LoadBalancer

Pour mes besoins, j’ai commencé à travailler il y a quelques semaines sur la création de clusters Kubernetes totalement automatisés. Pour cela, j’ai eu besoin de plusieurs composants, le premier étant un loadbalancer qui communique avec Kubernetes pour réaliser le service discovery.
Une solution que j’affectionne particulièrement est Traefik, car elle est simple à prendre en mains et propose suffisamment d’options pour ce que je veux faire sans être un enfer à configurer.
Je vous propose donc de vous exposer ici la démarche réalisée afin d’installer Traefik dans un cluster Kubernetes.

Les méthodes d’installation de Traefik

J’ai voulu démarrer simplement et utiliser Helm dans l’objectif de déployer rapidement le nécessaire pour Traefik. J’ai cependant réalisé que si celui-ci fonctionne parfaitement, le service créé utilise un nodePort au dessus de 30000, ce qui rend son utilisation moins triviale qu’avec le port http classique.
Si on souhaite définir un nodePort, nous pouvons le faire mais là aussi au dessus des 30000, sinon cela pourrait engendrer des conflits de port qui pourrait affecter par exemple le port 22 et rendre ainsi votre connexion SSH impossible.
Je me suis aussi posé des questions par rapport aux performances de la démarche : passer par un premier service Kubernetes qui va prendre l’ensemble du trafic puis l’envoyer vers de multiples instances Traefik, et pour finir vers les services d’accès au pod, me paraissait un peu lourd. J’ai donc cherché à simplifier le schéma.

Ainsi, j’ai créé un DaemonSet auquel j’ai affecté un hostNetwork pour ouvrir les ports http/https du host directement sans passer par des services.
Mon cluster utilise aussi Role-Based Access Control (RBAC), il faut donc créer un ClusterRole ainsi qu’un ClusterRoleBinding. J’en profite pour créer une règle ingress afin d’accéder au back office de Traefik via une URL, et non par le port 8080 par défaut.

Création des ressources

Création d’un namespace spécifique pour Traefik

apiVersion: v1  
kind: Namespace  
metadata:  
name: traefik  
Création d’un ClusterRole  
kind: ClusterRole  
apiVersion: rbac.authorization.k8s.io/v1beta1  
metadata:  
 name: traefik-ingress-controller
rules:  
 - apiGroups:
     - ""
   resources:
     - pods
     - services
     - endpoints
     - secrets
   verbs:
     - get
     - list
     - watch
 - apiGroups:
     - extensions
   resources:
     - ingresses
   verbs:
     - get
     - list
     - watch

Création d’un ClusterRoleBinding

kind: ClusterRoleBinding  
apiVersion: rbac.authorization.k8s.io/v1beta1  
metadata:  
 name: traefik-ingress-controller
roleRef:  
 apiGroup: rbac.authorization.k8s.io
 kind: ClusterRole
 name: traefik-ingress-controller
subjects:  
- kind: ServiceAccount
 name: traefik-ingress-controller
 namespace: traefik
Création d’un ServiceAccount  
apiVersion: v1  
kind: ServiceAccount  
metadata:  
 name: traefik-ingress-controller
 namespace: traefik

Création d’un ConfigMap

apiVersion: v1  
kind: ConfigMap  
metadata:  
 name: traefik-conf
 namespace: traefik
data:  
 traefik.toml: |-
   defaultEntryPoints = ["http","https"]
   [web]
   address = ":8080"

Création d’un DaemonSet

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 # workaround
     serviceAccountName: traefik-ingress-controller
     terminationGracePeriodSeconds: 60
     tolerations:
     - key: node-role.kubernetes.io/master
       effect: NoSchedule
     containers:
     - image: traefik:v1.3.1
       name: traefik-ingress-lb
       imagePullPolicy: Always
       volumeMounts:
         - mountPath: "/config"
           name: "config"
       resources:
         requests:
           cpu: 100m
           memory: 20Mi
       args:
       - --kubernetes
       - --configfile=/config/traefik.toml
     volumes:
     - name: config
       configMap:
         name: traefik-conf

Création d’un Service

apiVersion: v1  
kind: Service  
metadata:  
 name: traefik-web-ui
 namespace: traefik
spec:  
 selector:
   k8s-app: traefik-ingress-lb
 ports:
 - port: 80
   targetPort: 8080

Création d’un IngressController

Pensez à modifier la ligne host pour qu’elle corresponde à votre nom de domaine.

apiVersion: extensions/v1beta1  
kind: Ingress  
metadata:  
 name: traefik-web-ui
 namespace: traefik
 annotations:
   kubernetes.io/ingress.class: traefik
spec:  
 rules:
 - host: "traefik.exemple.tld"
   http:
     paths:
     - backend:
         serviceName: traefik-web-ui
         servicePort: 80

Si vous allez sur l’adresse que vous avez renseignée dans votre IngressController, vous pouvez maintenant accéder à votre backend Traefik !

Conclusion

Vous avez maintenant un beau Traefik qui répondra à l’ensemble de vos requêtes :) Vous n’avez plus qu’à créer de nouveaux IngressController et ainsi accéder à l’ensemble de vos applications via un point d’entrée unique.