J'avais déjà abordé cette nouvelle version dans un précédent article que vous pourrez retrouver ici : https://grottedubarbu.fr/traefik-2-2rc/

Si il faut retenir l'essentiel, voici une petite liste non exhaustive des principales nouveautés de cette version :

  • Le Dark mode pour le dashboard
  • Entrypoint Defaults (incl. redirects)
  • UDP
  • Elastic APM Tracer & KV store providers

Mais nous allons aujourd'hui nous attarder sur la technique ! La mise en place de Traefik 2.2 est-elle simple avec K3S ? Comment effectuer une redirection HTTP -> HTTPS par défaut ? Et ajouter un middleware par défaut également ? Le KV store providers ça donne quoi ?

Pré-requis

Il va bien sûr nous falloir installer K3S sans déployer notre reverse-proxy favori car nous allons le faire nous même !

Pour cela rien de plus simple :

curl -sfL https://get.k3s.io | sh -s - --no-deploy=traefik

Vous pouvez ensuite vérifier votre installation :

sudo kubectl get nodes

Et voila on peut commencer ! Comme d'habitude, je mettrai à disposition l'ensemble des configurations sur GitHub, prenez le temps de découvrir la configuration 😇

Traefik

Pour déployer notre service Traefik, il va être nécessaire de réaliser les étapes suivantes :

  • Déclarer les CRD Traefik, et les Roles and ClusterRoles
  • Créer un ConfigMap pour le fichier traefik.yml
  • Exposer le service Traefik et le déployer

Et oui, nous avons du boulot 😂

  1. Déclarer les Custom Ressource Definitions et les Roles/ClusterRoles

Il faut savoir que les ressources sont tout simplement des endpoint dans l'API de K8S qui permettent de stocker des collections d'objets API. Une ressource personnalisée nous permet donc d'étendre l'API Kubernetes avec des collections d'objets créés par nos soins.

Ici nous allons créer des objets que Traefik pourra manipuler. Mais je ne vais pas détailler le fichier yaml que j'utilise pour faire ces déclarations car il s'agit clairement d'un copier/coller de la documentation officielle qui est très bien faite :

https://docs.traefik.io/user-guides/crd-acme/#ingressroute-definition

2. ConfigMap : Traefik.yml

Afin de créer un fichier de configuration, je vais passer par une ressource ConfigMap. Il serait envisageable d'utiliser un fichier plat comme j'ai l'habitude de le faire avec Docker. Mais l'utilisation d'un ConfigMap est pour moi un avantage, car il va rendre notre configuration portable. Si nous décidions par exemple de créer un cluster par la suite.

Cela nous permettra ainsi de nous affranchir d'un éventuel stockage réseau partagé dédié à la configuration.

Voici mon fichier ConfigMap.yml :

apiVersion: v1
kind: ConfigMap
metadata:
  name: traefik-config-static
  namespace: default
  labels:
    app: traefik
data:
  traefik.yml: |
    entryPoints:
      web:
        address: :80
        http:
          redirections:
            entryPoint:
              to: websecure
              scheme: https

      websecure:
        address: :443
        http:
          tls:
            certResolver: myresolver

    api:
      insecure: true

    providers:
      kubernetesCRD: {}

    certificatesResolvers:
      myresolver:
        acme:
          caServer: https://acme-staging-v02.api.letsencrypt.org/directory
          email: "XXXXXXX"
          storage: "/letsencrypt/acme.json"
          tlsChallenge: {}

Nouveauté de cette version 2.2, je vais rediriger le trafic du flux HTTP directement depuis vers le HTTPS et ça depuis la déclaration de l'entrypoint. Plus besoin de passer par un middleware avec une règle spécifique :

    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

Autre nouveauté, je peux identifier le certResolver dans la déclaration de mon entrypoint HTTPS. Plus besoin de le rappeler systématiquement :

  websecure:
    address: :443
    http:
      tls:
        certResolver: myresolver

Je n'ai pas ajouté de Middleware immédiatement même si cette nouvelle version me le permet. Je vais revenir sur ce point après l'ajout de ma base redis afin de stocker mon middleware dans cette base.

3. Service et deployment de Traefik

Rien d'extraordinaire ici, je vais déclarer un service Traefik et un service whoami  dans le cadre de la démonstration :

apiVersion: v1
kind: Service
metadata:
  name: traefik

spec:
  ports:
    - protocol: TCP
      name: web
      port: 80
    - protocol: TCP
      name: admin
      port: 8080
    - protocol: TCP
      name: websecure
      port: 443
  type: LoadBalancer
  selector:
    app: traefik

---
apiVersion: v1
kind: Service
metadata:
  name: whoami

spec:
  ports:
    - protocol: TCP
      name: web
      port: 80
  selector:
    app: whoami

De même la déclaration du déploiement :

spec:
  replicas: 1
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik
    spec:
      serviceAccountName: traefik-ingress-controller
      containers:
        - name: traefik
          image: traefik:v2.2
          volumeMounts:
            - name: traefik-config-static
              mountPath: /etc/traefik/
            - name: letsencrypt
              mountPath: "/letsencrypt/"
      volumes:
        - name: traefik-config-static
          configMap:
            name: traefik-config-static
        - name: letsencrypt
          hostPath:
            path: /home/debian/stackfiles/traefik/letsencrypt
            type: Directory

J'utilise donc une version de Traefik en 2.2 ( actuellement en RC4 lors de l'écriture de cet article ).

Pensez notamment à changer le chemin au dossier letsencrypt afin de le faire correspondre à votre système. Sinon rien d'extraordinaire, j'utilise la ressource ConfigMap pour ma configuration statique que nous avons déclarée précédemment.

Un dernier fichier va contenir la déclaration de la route Ingress :

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: ingressroutetls
  namespace: default
spec:
  entryPoints:
    - websecure
  routes:
  - match: Host(`my.domain.com`)
    kind: Rule
    services:
    - name: whoami
      port: 80

Nous allons pouvoir aborder une autre nouveauté de cette version, le stockage d'éléments dans une base clé/valeur. Dans le cadre de cette démonstration, j'ai choisi Redis mais d'autres solutions sont bien sûr envisageables :

  • ETCD
  • ZooKeeper
  • Redis

Redis

Tout d'abord nous allons lancer une instance de Redis afin de pouvoir stocker nos clés/valeurs, voici mon fichier redis.yml :

apiVersion: v1
kind: Namespace
metadata:
  name: database
---
apiVersion: v1
kind: Service
metadata:
  name: redis-master
  labels:
    app: redis
  namespace: database
spec:
  ports:
    - port: 6379
      targetPort: 6379
  selector:
    app: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  labels:
    app: redis
  namespace: database
spec:
  selector:
    matchLabels:
      app: redis
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis:5-alpine
          resources:
            requests:
              cpu: 100m
              memory: 100Mi
          ports:
            - containerPort: 6379

🚩Bien évidement si vous souhaitez réaliser cette installation en production, il faudra sécuriser les accès. Je n'ai pas pris le temps de le faire ici. 🚩

J'ai choisi de créer un Namespace dédié pour la création de ma base, mais ce n'est pas une obligation. Nous allons pouvoir lancer notre instance :

sudo kubectl apply -f redis.yml

Je vais ensuite enregistrer quelques valeurs dans ma base Redis. Pour cela, je vais devoir me connecter sur l'instance. Je vais d'abord récupérer son nom avec la commande  :

sudo kubectl get po -n database

Ce qui me donne :

Je me connecte sur l'instance en exécutant la commande redis-cli afin de pouvoir saisir mes valeurs :

sudo kubectl exec -n database -it redis-6d54f486f8-65xkj -- redis-cli

Enfin je vais set des valeurs, ici un middleware avec quelques headers de sécurité :

set traefik/http/middlewares/SecHeaders/headers/frameDeny true
set traefik/http/middlewares/SecHeaders/headers/contentTypeNosniff true
set traefik/http/middlewares/SecHeaders/headers/browserXssFilter true
set traefik/http/middlewares/SecHeaders/headers/stsIncludeSubdomains true
set traefik/http/middlewares/SecHeaders/headers/stsSeconds 31536000

Il ne me reste maintenant plus qu'à déclarer ce provider dans Traefik ! Retournons donc à notre ConfigMap préalablement créé :

    providers:
      kubernetesCRD: {}

      redis:
        endpoints:
          - "redis-master.database.svc.cluster.local:6379"

Et finalement, je vais pouvoir utiliser mon middleware directement dans mon entrypoint HTTPS afin de mettre en place ces headers de façon systématique :

      websecure:
        address: :443
        http:
          tls:
            certResolver: myresolver
          middlewares:
          - [email protected]

Vous pouvez consulter votre dashboard afin de valider le bon fonctionnement :

Et enfin en consultant les headers de l'instance whoami tout simplement :

curl -k -I https://my.domain.com/

Le résultat devrait être le suivant :

HTTP/2 200
content-type: text/plain; charset=utf-8
date: Fri, 20 Mar 2020 08:49:27 GMT
strict-transport-security: max-age=31536000; includeSubDomains
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 1; mode=block
content-length: 388

Voila, notre installation d'un nouveau provider est fonctionnel !


Nous venons donc d'installer K3S et Traefik en version 2.2. Cette installation nous a permis de tester quelques nouveautés de cette version. J'avais déjà pu aborder au cours d'un précédent article certains ajouts de cette version mais dans le cadre d'une utilisation purement Docker. Nous avons pu mettre en place ces nouvelles fonctionnalités avec Kubernetes.

Ceci nous a également permis de tester l'ajout d'un nouveau provider : Les bases clés/valeurs. J'ai fait le choix dans cet article d'utiliser Redis, mais la configuration est similaire avec une autre base.

J'ai longtemps écrit mes fichiers de configuration au format yaml mais cela pose de nombreux soucis lors de la mise en place d'un cluster. L'utilisation d'un tel stockage va clairement faciliter cette utilisation !

Cette version apporte vraiment des ajouts pertinents et intéressants. Un grand bravo d'ailleurs à l'équipe derrière Traefik ! Les développeurs bien-sûr, mais pas uniquement !  De plus, je tiens à souligner la réactivité de l'équipe sur la correction des bugs, l'aide à la communauté, les webinar, etc !

Enfin voici les fichiers de configuration utilisés lors de cet article :

lfache/K3S-stackfiles
Contribute to lfache/K3S-stackfiles development by creating an account on GitHub.
En tout cas  n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter, c'est toujours avec plaisir de voir vos retours ! 😘