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 😂
- 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 :
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 ! 😘