Dans ce deuxième article sur le réseau dans Kubernetes, nous allons voir le fonctionnement des services. Nous reviendrons sur les concepts essentiels du premier article et je vous parlerai particulièrement du composant Kube Proxy.

Rappel du besoin

Les containers sont des instances d'images dites immuables. Normalement, une image est lancée et ne doit pas être modifiée. Chaque modification de l'image passe par un nouveau container, cela permet de réaliser un véritable versionning des workloads. Lorsque l'on souhaite modifier la configuration d'un container, on en crée un nouveau avec la même image et de nouveaux paramètres qui peuvent être insérés sous plusieurs formes  (variables d'environnement, ConfigMap, etc...)

Sous Kubernetes, les containers sont inclus dans un Pod.
Nous avons vu qu'une adresse IP est assignée à un Pod lors de son démarrage par l'intermédiaire de l'IPAM de la CNI (Container Network Interface) qui est un plugin du composant Kubelet (je reviendrai sur ces deux composants dans des articles dédiés).
Donc un Pod change d'adresse IP chaque fois qu'il est mis à jour (en fait c'est un nouveau Pod).
Il est nécessaire de trouver un moyen d'assigner une adresse IP durable pour un composant intermédiaire sur lequel la résolution DNS pourra être faite.
Les services Kubernetes permettent de répondre à ce besoin. De plus ils assurent le Load Balancing entre plusieurs Pods répliqués.

Dans l'exemple ci-dessus, un Pod "Devices Discovery" doit faire des requêtes API vers Netbox. Netbox possède 3 réplicas et le Service Netbox assure le rôle de proxy afin de répartir la charge et de maintenir une VIP durable pour atteindre les Pods utiles.

Rentrons dans les détails.

Services Kubernetes

Lors de la création d'un service, un paramètre "selector" est configuré pour lui permettre de connaitre les Pods qu'il doit servir. Ce selector est représenté par un label.

Pour chaque Pod possédant ce selector comme attribut,  un autre composant de Kubernetes est créé : un Endpoints.

Endpoints

Le Endpoints controller gère la création et la maintenance des endpoints pour savoir quand un Pod configuré avec le selector concerné est ajouté, supprimé, disponible ou non. Ainsi le service se base sur les Endpoints pour connaitre les Pods à servir.

Il existe deux listes d'adresses/ports dans un endpoint:

  • addresses : seules les adresses qui ont passé les tests "pod readiness checks" apparaissent dans cette liste
  • not ready addresses :  les adresses qui n'ont pas réussi à passer les tests

Kube Proxy, que nous détaillerons plus loin dans cet article, surveille les services et les endpoints pour mettre à jour, en conséquence, les règles de routage sur l'host Linux (Node) qui permettent la communication vers les Services.

Kube Proxy

Kube Proxy est le composant responsable des Services Kubernetes. Il tourne sur chacun des Nodes du Cluster et permet de configurer les règles de load balancing au niveau de l'host linux pour les protocoles UDP, TCP et SCTP. Il existe plusieurs modes d'exécution dont les 3 suivants :

  • userspace
  • iptables
  • ipvs

User Space mode

Dans ce mode, pour chaque connexion d'un client local vers un service, le flux passe par le Kernel via iptables, puis remonte dans le User Space via Kube Proxy pour être Load Balancé vers les Pods concernés.
Ce mode a été déclaré obsolète, car il était considéré comme mécanisme trop lent lié à la création d'un goulet d'étranglement et aux allers retours entre le User Space et le Kernel.  

iptables mode

Iptables est un utilitaire de Firewall Linux très connu qui permet de faire du filtrage et du NAT sur les paquets entrant dans le Kernel. Cela est rendu possible grâce à son interfaçage avec le module netfilter du Kernel Linux qui permet d'intercepter et de modifier les paquets. Si vous n'êtes pas familier avec netfilter, voici un très bon article de Justin Ellingwood dans un tutoriel de Digital Ocean. Avec ce mode, les flux passent uniquement par iptables qui effectue, lui-même, le Load Balancing vers les pods concernés.
Cependant, la façon dont kube-proxy programme les règles iptables, implique un algorithme nominale de style O(n), où n croît à peu près proportionnellement avec la taille du cluster (plus précisément avec le nombre de services et le nombre de pods backend derrière chaque service)

IPVS (IP Virtual Server) mode

IPVS est une fonctionnalité du noyau Linux spécialement conçue pour l'équilibrage de charge. En mode IPVS, kube-proxy programme le load balancer IPVS au lieu d'utiliser iptables.
IPVS possède une API et une routine de recherche optimisée basée sur du Hashing plutôt que sur une liste de règles séquentielles (Chains). Cela résulte sur un traitement de connexion de kube-proxy avec une complexité de calcul nominale de O(1). En d'autres termes, dans la plupart des scénarios, ses performances de traitement de connexion resteront constantes indépendamment de la taille du cluster.
De plus, en tant que Load Balancer dédié, IPVS dispose de plusieurs algorithmes de planification différents tels que le round-robin, le délai le plus court attendu, le moins de connexions et diverses approches de hachage. Alors qu'avec iptables, kube-proxy utilise un algorithme de sélection aléatoire à coût égal.
Un inconvénient potentiel d'IPVS est que les paquets empruntent un chemin, à travers les filter hooks d'iptables, très différent des paquets traités dans des circonstances normales.
Cela dit, vous l'aurez compris, IPVS sera plus rapide et efficace dans le Load Balancing qu'iptables.

Pour plus de détails sur les modes de fonctionnement de Kube-Proxy, voici un très bon article de Arthur Chiao Cracking Kubernetes Node Proxy (aka kube-proxy).

Dans le prochain article, nous aborderons les différent type de services que l'on trouve dans Kubernetes.