Votre Ingress Controller avec Traefik

Votre Ingress Controller avec Traefik

Traefik, c'est quoi ?

Je vous ai déjà parlé de Traefik comme étant un super proxy inversé (reverse proxy) que j'utilisais pour mes configurations avec docker-compose qui permettait en plus de générer des certificats TLS pour un accès HTTPS sans certificat auto-signé.

Le produit évolue toujours, il est actuellement en version 2.8 à la rédaction de cet article.

Pour une présentation plus complète du fonctionnement interne, je vous redirige vers cet article où j'ai présenté en détail le fonctionnement de ce proxy.

L'objectif de cet article est de voir l'intégration de Traefik au sein de l'écosystème Kubernetes.

Traefik avec Kubernetes

Introduction

Dans Kubernetes, pour rendre vos ressources accessibles de l'extérieur (généralement HTTP ou HTTPS), il est nécessaire d'utiliser (et d'installer) ce que l'on appelle un Ingress Controller afin d'utiliser des objets Ingress.

Si vous souhaitez plus d'informations à ce sujet, la documentation officielle regroupe beaucoup d'informations.

Des Ingress Controller il en existe un paquet ! Que ce soit ceux des services managés du Cloud, nginx, HAProxy, etc.

Alors, pourquoi choisir Traefik ?

  • Très simple à déployer sur un cluster avec un chart Helm configurable ;
  • Très léger et pour cause, K3S, le Kubernetes allégé utilise Traefik en tant qu'Ingress Controller par défaut ;
  • Génération de certificats TLS avec Let's Encrypt et gestion automatique du renouvellement ce qui évite d'utiliser cert-manager en parallèle ;
  • Permet de faire de l'équilibrage de charge HTTP, mais aussi TCP ;
  • Beaucoup de fonctionnalités disponibles (dashboard, plugins, etc.).

Pour ce qui concerne Traefik, il y a deux manières de l'utiliser dans ce type d'orchestrateur :

  • Soit en utilisant un objet Ingress classique qui est nativement inclus sur Kubernetes ;
  • Soit en utilisant un objet IngressRoute qui est un objet personnalisé fourni par Traefik en tant que Custom Resource Definition (CRD).

Pourquoi ces deux possibilités ?

Après avoir utilisé l'objet Ingress, la communauté a voulu créer un objet personnalisé (IngressRoute) au sein de Kubernetes pour éviter d'utiliser les annotations pour l'ensemble des fonctionnalités souhaitées par l'utilisateur afin de ne pas surcharger l'objet Ingress et d'avoir une structure plus claire et lisible.

En résumé, la première solution est simple et rapide, la deuxième est pour des cas d'utilisation avancées.

Installation

Pour ceux qui souhaitent installer le Chart Traefik avec Ansible, voici le bout de code pour y parvenir :

- hosts: client # Machine sur laquelle vous souhaitez jouer le playbook
  gather_facts: true
  become: true
  vars:
    traefik_chart_version: 10.24.0 # Version du Chart Helm de Traefik
  tasks:
  - name: Install traefik chart
    kubernetes.core.helm:
      release_name: traefik
      chart_ref: traefik
      chart_version: "{{ traefik_chart_version }}"
      chart_repo_url: https://helm.traefik.io/traefik
      release_namespace: traefik-ingress
      create_namespace: true
      values:
        globalArguments:
        deployment:
          kind: DaemonSet # Déploiement en mode DaemonSet
        providers:
          kubernetesCRD:
            enabled: true # Active la création des CRD pour les objets personnalisés
        service:
          type: LoadBalancer # Type de service à utiliser pour Traefik
        ingressRoute:
          dashboard:
            enabled: false # Désactive la création d'une IngressRoute pour accéder au dashboard
    become: false

C'est la configuration que j'utilise quand je déploie Traefik sur mon projet kubeadm-automation que vous pouvez retrouver ici. De plus, il ne faut pas oublier de déployer MetalLB pour utiliser le type LoadBalancer sur ce type d'installation.

N'oubliez pas non plus d'exécuter ansible-galaxy collection install kubernetes.core avant de jouer ce playbook afin d'utiliser la collection core de Kubernetes avec Ansible.

Il est possible d'installer l'Ingress Controller Traefik de manière traditionnelle avec Helm avec les commandes suivantes :

# Ajoute le repository à Helm
helm repo add traefik https://helm.traefik.io/traefik
# Permet de mettre à jour l'ensemble des repo
helm repo update
# Crée le namespace traefik-ingress
kubectl create ns traefik-ingress
# Crée le fichier traefik-values.yaml
cat > traefik-values.yaml <<EOF
globalArguments:
deployment:
  kind: DaemonSet
providers:
  kubernetesCRD:
    enabled: true
service:
  type: LoadBalancer
ingressRoute:
  dashboard:
    enabled: false
EOF
# Installe Traefik
helm install --namespace=traefik-ingress traefik traefik/traefik --values=./traefik-values.yaml

Vérifier l'installation

Plusieurs commandes permettent de vérifier le bon déploiement de Traefik.

Tout d'abord, est-ce que le ou les Pod sont Ready ?

$ kubectl -n traefik-ingress get pod
NAME            READY   STATUS    RESTARTS   AGE
traefik-xfz57   1/1     Running   0          3m18s

Oui !

Traefik a été configuré en mode DaemonSet, ce qui veut dire que chaque noeud du cluster doit avoir un Pod associé :

$ k -n traefik-ingress get ds
NAME      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
traefik   1         1         1       1            1           <none>          5m4s

Est-ce que le service de type LoadBalancer dispose d'une EXTERNAL-IP ?

$ kubectl -n traefik-ingress get svc
NAME      TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)                      AGE
traefik   LoadBalancer   10.106.241.64   172.16.254.1   80:30648/TCP,443:31371/TCP   9m29s

Tout est bon ! Traefik est totalement fonctionnel !

Créer son premier Ingress

Maintenant que l'installation est terminée, il est possible de créer un objet de type Ingress avec un exemple très simple.

Tout d'abord, il est impératif de créer un Pod et un service associé à ce Pod :

kubectl run nginx --image=nginx
kubectl expose pod nginx --port=80

et ensuite de créer l'Ingress se basant sur le service préalablement créé :

cat <<EOF | kubectl create -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ing-traefik
  annotations:
    kubernetes.io/ingress.class: traefik
spec:
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx
            port:
              number: 80
EOF

Pour vérifier le fonctionnement de l'Ingress, il suffit de prendre l'EXTERNAL-IP du service Traefik. Ce qui donne :

$ curl 172.16.254.1
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</head>
<body>
...
</body>
</html>

Simple non ?
En même temps, c'est une fonctionnalité très basique que l'ensemble des Ingress Controllers ont.

Utiliser l'objet IngressRoute et obtenir un certificat TLS

On vient de configurer un Ingress classique, le but est maintenant de faire la même chose tout en ayant une génération de certificat automatique pour un nom de domaine. Pour cela, il est nécessaire d'utiliser l'IngressRoute de Traefik.

Tout d'abord, il est impératif d'ajouter un bloc (au niveau du fichier traefik-values.yaml avant de déployer notre Chart) qui est requis pour configurer letsencrypt et permettre à Traefik de générer des certificats TLS.

certResolvers:
  letsencrypt:
    email: # Insérer ici une adresse email qui permettra d'associer vos certificats à celle-ci
    httpChallenge: # Utilisation du challenge HTTP
      entryPoint: "web"
    storage: /data/acme.json # Espace de stockage des certificats sur les masters Kubernetes

L'ensemble des Challenges est disponible à cette adresse afin de vérifier que le nom de domaine vous appartient.

Une chose important à savoir avant de réaliser ces étapes, c'est que vous devez disposer d'une IP publique qui pointe sur votre cluster avec un nom de domaine associé à cette IP publique.

Et voici la configuration à avoir si on reprend l'exemple du dessus :

kubectl run nginx --image=nginx
kubectl expose pod nginx --port=80
cat <<EOF | kubectl create -f -
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: ing-traefik-tls
spec:
  entryPoints:
  - websecure
  routes:
  - match: Host(\`ingress.filador.fr\`)
    kind: Rule
    services:
    - name: nginx
      port: 80
  tls:
    certResolver: letsencrypt
EOF

La configuration est un peu différente d'un Ingress classique, ce qui change surtout c'est la partie tls avec le certResolver à spécifier (qui a été défini au préalable dans le Chart Helm).

La création d'un certificat peut prendre plus ou moins de temps, pas de panique ici, Traefik met à disposition son certificat par défaut (CN=TRAEFIK DEFAULT CERT) le temps de la génération du certificat TLS.

Par ailleurs, les certificats ont une durée de vie de 90 jours et sont renouvelés 30 jours avant leur expiration.

Pour aller plus loin, il peut être intéressant de rediriger le trafic HTTP vers le HTTPS. C'est ce qui est fait ici :

cat <<EOF | kubectl create -f -
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: ing-traefik
spec:
  entryPoints:
    - web
  routes:
  - match: Host(\`ingress.filador.fr\`)
    kind: Rule
    services:
    - name: nginx
      port: 80
    middlewares:
    - name: https-redirect
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: https-redirect
spec:
  redirectScheme:
    scheme: https
    permanent: true
EOF

Un nouvel objet est créé, ici Middleware qui permet de mettre en place une redirection vers le HTTPS.

Beaucoup d'options peuvent être positionnées au niveau du Middleware notamment pour ajuster la sécurité au niveau des Headers HTTPS.

Fractionner le trafic

Dans les utilisations avancées de Traefik, il est possible de fractionner le trafic en créant un TraefikService permettant d'associer des poids à chaque route.

C'est ce qui va être effectué dans cet exemple :

On crée un Pod nginx et un autre httpd avec pour chacun un service de type ClusterIP.

kubectl run nginx --image=nginx
kubectl expose pod nginx --port=80
kubectl run httpd --image=httpd
kubectl expose pod httpd --port=80

On crée par la suite, un TraefikService avec le poids de chaque service : 5 pour nginx et 1 pour httpd.

cat <<EOF | kubectl create -f -
apiVersion: traefik.containo.us/v1alpha1
kind: TraefikService
metadata:
  name: weightedroundrobin
spec:
  weighted:
    services:
      - name: nginx
        port: 80
        weight: 5
      - name: httpd
        port: 80
        weight: 1
EOF

Enfin, on associe le TraefikService avec un objet IngressRoute avec un nom de domaine associé.

cat <<EOF | kubectl create -f -
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: ingress-weightedroundrobin
spec:
  entryPoints:
  - web
  routes:
  - match: Host(\`traefik-wrr.local\`)
    kind: Rule
    services:
    - name: weightedroundrobin
      kind: TraefikService
EOF

Pour tester, il ne faut pas oublier d'ajouter le nom de domaine associé à l'IngressRoute dans votre fichier /etc/hosts avec l'adresse IP externe lié au LoadBalancer de Traefik (dans le cas où vous testez avec le projet kubeadm.

Une fois sur 5 (à peu près), on peut visualiser le <html><body><h1>It works!</h1></body></html> d'httpd au lieu de la page de nginx.

Cette fonctionnalité a le mérite d'être très utile dans le cas où on souhaite tester une nouvelle version d'application avec une petite portion de trafic qui lui est associée.

Le mot de la fin

Traefik se révèle comme l'Ingress Controller facile à mettre en place avec un large panel de fonctionnalités. Son intégration directe avec Let's Encrypt permet la génération de certificats à la volée plutôt que de passer par des outils intermédiaires.

Enfin, les objets personnalisés permettent de réaliser des configurations avancées qui sont intéressantes à mettre en oeuvre en fonction du cas d'utilisation rencontré.