Passage sur MetalLB au lieu de ServiceLB sur un cluster k3s

Cela permet de bien améliorer la résilience de l’accès à l’Ingress

Pourquoi?

Le cluster k3s expose ses services à l’extérieur via un Ingress (Traefik, par défaut), sur les ports 80 et 443.

Mais sur quelle IP sont exposés ces ports 80 et 443? Par défaut, k3s déploie ServiceLB, qui expose ces ports sur tous les nodes du cluster. C’est pratique, mais lequel choisir?

En pratique, il faut bien choisir une IP vers laquelle router les appels. Soit via une résolution DNS, soit via le NAT d’une box Internet, ou d’un routeur.

Donc, si par malchance le node correspondant à cette IP est indisponible, plus rien ne marche. Et oui, ça m’est arrivé, un jour où je n’étais pas chez moi: il a fallu en urgence que je change l’IP sur mon Turris (et depuis mon smartphone, sinon ce ne serait pas rigolo)

Bref, il serait quand même vraiment mieux que le trafic entrant puisse être automatiquement basculé sur un autre node. Chez les cloud providers, c’est géré par des appareils dédiés, nommés Load Balancers. Mais on n’a en général pas ça en on-prem.

MetalLB

C’est justement le rôle de https://metallb.universe.tf/, qu’on peut déployer à la place de ServiceLB.

Je l’ai découvert grâce à cette courte présentation.

Préparation

MatalLB propose deux modes: L2 et BGP. Dans mon cas de figure, L2 suffit amplement, et est plus simple à configurer.

Source: vidéo Youtube ci-dessus (https://www.youtube.com/watch?v=eUhZ-R2IQ9E)

Dans tous les cas, il faut choisir une IP pour metallb, qu’il sera capable de mapper avec l’adresse MAC d’un node portant Traefik.

Il faut s’assurer que cette IP ne risque pas d’être assignée en DHCP. Sur Turris (ou plus généralement OpenWRT), et sur la plupart des serveurs DHCP, il est possible d’ajuster la plage d’IPs pour le DHCP: dans ce cas, il faut choisir une IP en-dehors de cette plage.

Mise en oeuvre

C’est les mêmes étapes pour une première installation de k3s ou pour la bascule d’un cluster k3s existant

Remplacer ServiceLB par MetalLB

Sur le (ou les) control-plane(s), il faut relancer le script d’installation, en y ajoutant un paramètre pour qu’il ne déploie pas ServiceLB:

curl -sfL https://get.k3s.io | sh -s - --disable=servicelb

Ensuite on installe MetalLB. Dans mon cas, j’ai choisi d’utiliser leur chart Helm (mais il y a d’autres options: voir leur doc):

helm repo add metallb https://metallb.github.io/metallb
helm install metallb metallb/metallb

Configuration de MetalLB

Il faut indiquer à MetalLB quelle IP choisir pour Traefik. Voici la conf que j’ai utilisée (en utilisant 192.168.1.10 comme IP):

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: traefik-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.10/32
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: traefik
  namespace: metallb-system
spec:
  ipAddressPools:
    - traefik-pool

… et normalement ça suffit déjà à avoir une configuration qui fonctionne

Petites améliorations

Si vous utilisez d’autres services k8s de type LoadBalancer (pas uniquement Traefik), vous pourriez avoir besoin de définir d’autres IP ou pools d’IP. Il est donc plus prudent d’indiquer à Traefik quel pool il doit utiliser.

C’est possible facilement en ajoutant une annotation sur le Service k8s de Traefik:

metallb.universe.tf/address-pool: traefik-pool

et ça peut se faire facilement dans le values.yaml de Traefik (voir plus bas)

D’autre part, je conseille de configurer Traefik avec externalTrafficPolicy: Local. Ca permet (entre autres) à MetalLB de router directement le trafic vers un node sur lequel un pod Traefik tourne. Dans le cas contraire, il peut l’envoyer vers un node quelconque, qui le re-routera vers un node sur lequel Traefik tourne, ce qui n’est pas optimum.

Pour ces 2 configurations, il est possible de le faire avec un HelmChartConfig:

apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: traefik
  namespace: kube-system
spec:
  valuesContent: |-
    (...there can be more existing content)

    # To have the original IP in X-Forwarded-For headers
    # And to prefer L2 metalLB advertisement where a Traefik pod is running
    service:
      spec:
        externalTrafficPolicy: Local
    # To make MetalLB use the appropriate pool
    service:
      annotations:
        metallb.universe.tf/address-pool: traefik-pool

Résultat

Ca marche nickel!

A présent, le trafic des ports 80 et 443 est routé directement de mon Turris vers le (ou un) node qui fait tourner Traefik. Et si ce node est indisponible, Kubernetes déplace Traefik sur un autre node, et le trafic réseau le suit automatiquement, sans intervention manuelle.

NB: apparemment, kube-vip permet de faire la même chose, avec la même technique (et le fait aussi pour les control-planes, quand il y en a plusieurs en HA, ce qui n’est pas mon cas). Je ne l’ai pas testé

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *