Comprendre le réseau Kubernetes : comment rendre les applications distribuées accessibles grâce aux services


À l’époque bénie de la virtualisation, les réseaux étaient relativement simples. Chaque machine virtuelle (VM) avait une adresse IP, et chaque application exécutée sur cette VM était associée à un port TCP ou UDP. Lorsqu’une application devait communiquer avec une autre, les mécanismes de base du réseau TCP/IP suffisaient.

Puis les conteneurs sont apparus, et le monde tel que nous le connaissions a changé du tout au tout. À certains égards, la mise en réseau des conteneurs n’est qu’une extension de celle des machines virtuelles ; à d’autres, elle est très différente. Mais dès que l’on ajoute un orchestrateur de conteneurs comme Kubernetes, on peut se retrouver avec des milliers de nœuds et des millions de pods créés et supprimés à toute heure du jour ou de la nuit. Pour le réseau, c’est un changement d’échelle… d’un autre monde.

Dans cet article, nous allons découvrir quelques outils essentiels de mise en réseau Kubernetes afin de faire fonctionner tout cela sans vous faire perdre la tête.

Kubernetes Recap (Ultra Brief Version) 

Avant de nous plonger dans le réseau Kubernetes, nous devons revenir brièvement sur certains concepts et éléments fondamentaux de son architecture. Ceux-ci expliquent en partie pourquoi Kubernetes est si puissant, mais introduisent également des difficultés potentielles importantes pour la mise en réseau. Commençons par deux objets et deux concepts clés.

Besoin d’un récapitulatif complet ? Parcourez notre article : Qu’est-ce que la conteneurisation ? Tutoriel et historique en bref.

Objets clés

Les deux objets clés sont les Pods et les Nodes (nœuds).

Pods

Un Pod est ce qui se rapproche le plus d’un conteneur dans Kubernetes. Il contient un conteneur et lui fournit les ressources dont il a besoin pour fonctionner, notamment la RAM, le stockage, l’accès au CPU, une adresse IP, un port TCP/UDP, etc.

(En réalité, si plusieurs conteneurs peuvent fonctionner avec le même ensemble de ressources, vous pouvez placer tous ces conteneurs dans un seul Pod.)

Le schéma ci-dessous illustre un Pod contenant deux conteneurs, ainsi qu’une adresse IP, de la RAM et du stockage. C’est également un bon moment pour préciser que chaque Pod reçoit dynamiquement une adresse IP lors de sa création, de manière similaire au fonctionnement de DHCP.

Kubernetes Pods

Les nœuds

Un Node est une plateforme de calcul (par exemple, une machine virtuelle). Dans Kubernetes, il existe deux types de Nodes :

Comme illustré ci-dessous, les utilisateurs finaux interagissent généralement avec les Worker Nodes…

Kubernetes Nodes

Concepts clés

Les deux concepts clés sont « l’état souhaité » (desired state) et « l’état actuel » (current state).

L’état souhaité correspond à l’état idéal dans lequel vous voulez que votre Cluster se trouve.

L’état actuel correspond à la situation réelle de votre Cluster à un instant donné.

Kubernetes vous permet de définir l’état souhaité de votre Cluster — par exemple : 100 Pods Nginx exécutés sur des Nodes à Ottawa, 200 Pods Nginx exécutés sur des Nodes à Chicago, 50 Pods MySQL exécutés sur des Nodes à Toronto, etc. Une fois cet état défini, les Master Nodes de Kubernetes surveillent l’ensemble des Workers et des Pods du Cluster afin de s’assurer que son état actuel correspond à l’état souhaité configuré. Si l’un des Pods Nginx à Ottawa cesse de répondre, les Master Nodes prennent automatiquement les mesures nécessaires pour résoudre le problème.

Pour parvenir à cet objectif, qui consiste à faire correspondre l’état actuel à l’état souhaité, Kubernetes utilise une architecture en couches, à la fois ingénieuse et complexe. Chaque objet possède ses propres responsabilités et collabore avec d’autres objets ayant leurs propres fonctions.

Au le plus bas, le rôle d’un Pod est d’exécuter le ou les conteneurs qu’il contient. De plus, les Pods sont conçus pour être éphémères : ne vous attendez donc pas à ce que les Pods que vous créez aujourd’hui soient encore là demain. Si un conteneur à l’intérieur d’un Pod tombe en panne, le Pod ne va pas activement signaler ce problème aux autres composants ; cette responsabilité incombe à un autre élément de l’architecture.

Un niveau au-dessus des Pods se trouve le Deployment, qui gère un ensemble de Pods et surveille leur état de santé. Dans notre scénario précédent, nous pourrions créer un Deployment chargé de maintenir les 100 Pods d’Ottawa en fonctionnement. Si l’un de ces Pods cesse de répondre, plutôt que de perdre du temps à essayer de le réparer, le Pod est supprimé et un nouveau Pod est créé pour le remplacer (au niveau du Deployment).

Cette approche ressemble au support informatique dans une grande organisation : lorsqu’un ordinateur portable rencontre un problème, la solution la plus rapide consiste souvent à réinstaller complètement le système (re-image) ou même à remplacer l’ordinateur.

Cette approche est excellente pour créer des applications hautement disponibles et évolutives, reposant sur un grand nombre de Pods. Cependant, elle introduit également certains défis en matière de réseau.

Plus particulièrement, chaque Pod reçoit dynamiquement son adresse IP au moment de sa création. Un système de mise en réseau basé sur les adresses IP des Pods serait donc impossible à gérer dans un environnement d’entreprise.

Les Services réseau de Kubernetes

Les Services Kubernetes offrent une approche judicieuse pour gérer le réseau avec des Pods éphémères. Un Service est un objet Kubernetes qui vous permet d’accéder à plusieurs Pods identiques en fonction des labels qui leur sont associés, indépendamment de leurs adresses IP. Il est utile de considérer les Services comme utilisant des « noms » (au sens large) plutôt que des adresses IP ou des ports. Dans la suite de cet article, nous examinerons cinq types de Services Kubernetes.

Service ClusterIP

 ClusterIP est le type de Service par défaut dans Kubernetes. Le Service ClusterIP est conçu pour la communication d’application à application au sein d’un cluster. Par exemple, prenons le cas d’une application web et d’une application SMB qui doivent fonctionner ensemble. Dans le diagramme, les Pods de l’application web portent l’étiquette X, et les Pods SMB portent l’étiquette Y.

Si le Pod de l’application web situé à l’adresse 10.0.0.2 a besoin de quelque chose de l’application SMB, il pourrait envoyer une requête au Pod situé à 10.0.0.4:445… jusqu’à ce que ce Pod finisse inévitablement par disparaître et soit remplacé par un Pod identique à une autre adresse IP.

Pour résoudre ce problème, nous pouvons définir deux Services ClusterIP : le « Service X » pour l’application web et le « Service Y » pour l’application SMB. Chaque Service ClusterIP présente une adresse IP virtuelle stable et une valeur de port par lesquelles les Pods peuvent le contacter. L’autre extrémité du Service se connecte aux Pods possédant la valeur d’étiquette appropriée (les « endpoints »), quelle que soit l’adresse IP du Pod. S’il existe plusieurs Pods avec une valeur d’étiquette valide, le Service ClusterIP effectue une répartition de charge entre ces Pods.

Kubernetes Networking - Cluster IP Service

Notez que le Service ClusterIP peut se connecter aux Pods pertinents à travers plusieurs Nodes du cluster. Notez également que l’étiquette est définie dans le fichier manifeste du Pod, de sorte que chaque nouvelle instance de ce Pod portera cette même étiquette, ce qui le rendra accessible au Service quelle que soit son adresse IP.

Un détail important : l’adresse IP présentée par le Service ClusterIP est appelée le « ClusterIP », et le port présenté est appelé le « Port ». (C’est là que je me demande toujours pourquoi ils n’ont pas simplement appelé ça le « ClusterPort » !)

Pour approfondir un peu, voici un exemple de fichier manifeste de Service ClusterIP :

apiVersion: v1
kind: Service
metadata:
name: service-x-cip #ClusterIP for web app
spec:
  type: ClusterIP #optional for a ClusterIP Service
  ports:
    - port: 8080 #Port for the Service (IP is assigned) 
      protocol: TCP 
      targetPort: 80 #port on the Pod
  selector:
    app: web-app #Service will look for pods with this Label 

Le champ « type » est facultatif pour un Service ClusterIP ; s’il n’y a pas de champ « type » dans le fichier manifeste d’un Service, Kubernetes utilise ClusterIP par défaut. De plus, l’adresse ClusterIP est attribuée automatiquement par Kubernetes lors de la création du Service. Pour afficher cette adresse IP, utilisez cette commande :

# kubectl get svc service-x-cip

Pour plus de détails, ajoutez l’option « -o wide ». Pour un affichage complet des propriétés fonctionnelles du Service, utilisez cette commande :

# kubectl describe svc service-x-cip

NodePort Service 

Le type de Service NodePort rend l’application conteneurisée accessible depuis l’extérieur du cluster. Le Service NodePort expose les Pods de l’application sur un port statique (le NodePort) sur chaque Node. Dans la logique de superposition, le Service NodePort crée automatiquement un Service ClusterIP correspondant pour répondre aux besoins de mise en réseau d’application à application ; le Service NodePort ne reproduit pas la fonctionnalité du ClusterIP.

Kubernetes Networking - NodePort Service

Le diagramme illustre un Service NodePort qui expose le Pod Nginx sur le Node 1. Pour accéder à ce Pod, l’utilisateur peut naviguer (ou faire un curl) vers 10.0.0.2:30001 (c’est-à-dire NodeIP:NodePort). Bien que le diagramme ne montre qu’un seul Pod Nginx, plusieurs endpoints sont également possibles. Comme pour un Service ClusterIP, un Service NodePort définit ses endpoints via un sélecteur dans le fichier manifeste du Service et des étiquettes correspondantes dans le fichier manifeste du Pod. Ainsi, s’il y a plusieurs Pods Nginx sur ce Node, portant l’étiquette appropriée, le Service NodePort peut envoyer la requête à n’importe lequel de ces Pods.

Vous devez accéder au Node par adresse IP, ce qui signifie que le Service NodePort est spécifique à un Node. Cela diffère d’un Service ClusterIP, qui peut accéder aux Pods pertinents sur de nombreux autres Nodes du cluster. Cela signifie également que le Service NodePort n’est accessible qu’aux personnes pouvant effectuer un ping vers le Node ; il n’est pas ouvert à l’accès public depuis Internet.

Examinons un fichier manifeste de Service NodePort :

apiVersion: v1
kind: Service
metadata:
  name: service-x-np
spec:
  type: NodePort # this is required for NodePort Service
  ports: 
    - port: 8080 #Port for the ClusterIP Service
      protocol: TCP
      targetPort: 80 #port on the Pod
  selector:
    app: web-app #Service will look for pods with this Label 

Ce fichier manifeste est assez similaire au manifeste ClusterIP. Cependant, vous devez spécifier le type comme « NodePort » ; si vous ne le faites pas, Kubernetes utilisera par défaut un Service ClusterIP. Et la ligne « port » est utilisée pour le Service ClusterIP généré automatiquement.

Alors, qu’en est-il du NodePort, le port de ce Service NodePort ?

Kubernetes vous donne la possibilité de spécifier vous-même la valeur du NodePort (port), ou de la laisser être définie automatiquement. Dans tous les cas, la valeur du NodePort se situe toujours entre 30 000 et 32 767. Dans l’exemple ci-dessus, nous avons laissé Kubernetes choisir la valeur du NodePort pour nous, et nous avons obtenu 30 001.

Comme pour notre exemple ClusterIP, pour afficher les valeurs NodeIP et NodePort de ce service, entrez :

# kubectl get svc service-x-cip

Pour plus de détails, entrez :

# kubectl describe svc service-x-cip

Bien que le Service NodePort soit rattaché à un Node particulier, vous pouvez l’étendre à d’autres Nodes du cluster en réservant la valeur du port NodePort sur les autres Nodes et en les redirigeant vers le Service NodePort. Par exemple, vous pourriez définir une règle selon laquelle tout trafic vers 10.0.0.3:30001 serait redirigé vers 10.0.0.2:30001. Mais cela ne semble guère être une manière appropriée de rendre une application accessible sur le web…

Service LoadBalancer

Un Service LoadBalancer est la manière appropriée d’exposer un Service à Internet. Ce type de Service s’appuie sur la fonction de répartition de charge (load balancer) d’un fournisseur cloud comme AWS ou Azure.

Examinons un exemple de fichier manifeste pour un Service LoadBalancer :

apiVersion: v1
kind: Service
metadata:
  name: service-x-lb
spec:
  type: LoadBalancer # definitely required!
  ports:
    - port: 8080 #Port for the ClusterIP Service
      protocol: TCP
      targetPort: 80 #port on the Pod
  selector:
    app: web-app #Service will look for pods with this Label

La structure devrait maintenant vous être familière. Comme pour le Service NodePort, veillez à bien spécifier le type de Service, sinon vous vous retrouverez avec un Service ClusterIP. Sans surprise, un Service LoadBalancer créera automatiquement un Service NodePort et un Service ClusterIP pour gérer les autres aspects de l’équation. Mais c’est le fournisseur cloud qui décidera de la manière dont ces Services sous-jacents sont provisionnés. Un Service LoadBalancer génère plusieurs machines virtuelles gérées par le fournisseur, qui traitent le trafic réel. Et les décisions fondamentales de répartition de charge sont gérées par le fournisseur, et non par Kubernetes. Cela peut être un avantage ou un inconvénient selon votre point de vue.

Le principal inconvénient est que chaque Service LoadBalancer nécessite sa propre adresse IP publique ; si vous souhaitez plusieurs LoadBalancers, cela peut rapidement devenir coûteux. Certains fournisseurs cloud vous permettent de spécifier l’adresse IP du Service LoadBalancer, ce qui constitue au moins un point positif.

Autres types de Service de mise en réseau Kubernetes

Les trois Services ci-dessus visent tous un objectif commun : rendre les applications distribuées accessibles sur Internet et garantir qu’elles puissent communiquer entre elles. Bien que cela couvre la plupart des cas d’usage, d’autres scénarios entrent parfois en jeu.

ExternalName Service

Le Service ExternalName est conçu pour pointer vers un nom de domaine canonique en vue d’une résolution DNS. Ce type de Service redirige le trafic à travers un alias interne, puis vers une ressource externe. Par exemple, vous pourriez utiliser un Service ExternalName pour diriger le trafic des Pods vers une base de données externe, comme dans le fichier manifeste suivant :

kind: Service
apiVersion: v1
metadata:
  name: external-db
spec:
  type: ExternalName # don’t forget this line!
  externalName: mysql.cengn.ca # note the FQDN structure
  ports:
    - protocol: TCP
      port: 80 

Service Headless

Un Service Headless correspond à un Pod spécifique sans créer d’adresse IP virtuelle (ce qui est totalement différent d’un Service ClusterIP). Les Services Headless conviennent aux applications avec état (stateful), où chaque Pod stocke des données uniques et persistantes. De plus, ils permettent aux utilisateurs d’interagir directement avec le Pod cible.

Voici un exemple de fichier manifeste pour un Service Headless. Remarquez que le champ habituel « type » est remplacé par la ligne « clusterIP: None ».

apiVersion: v1
kind: Service
metadata:
  name: headless-svc
spec:
  clusterIP: None # this is the crucial bit here
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  selector:
    app: myheadlessapp

Récapitulatif

So, there you have it.

Kubernetes enables you to run highly available containerized apps using millions of Pods on thousands of Nodes around the world. Along the way, we ran into a serious problem with networking those Pods together, but Kubernetes also provides a clever set of solutions through its networking Services.

Want to learn more about Kubernetes? Go from intro to expert with CENGN’s Docker + Kubernetes Basics and Advanced courses.

Build your confidence with hands-on experience in online labs and learn on your own schedule.

Voilà, vous savez maintenant tout.

Kubernetes vous permet d’exécuter des applications conteneurisées hautement disponibles en utilisant des millions de Pods sur des milliers de Nodes à travers le monde. En chemin, nous avons rencontré un problème sérieux concernant la mise en réseau de ces Pods entre eux, mais Kubernetes propose également un ensemble ingénieux de solutions grâce à ses Services de mise en réseau.

Vous voulez en apprendre davantage sur Kubernetes ? Passez de débutant à expert avec les cours Docker + Kubernetes Basics et Advanced de CENGN.

Développez votre confiance grâce à une expérience pratique dans des laboratoires en ligne, et apprenez à votre propre rythme.

Revenir au centre d’information

À propos de l'auteur

Peter Heath is CENGN’s Senior Manager for Training Programs, driving CENGN Academy’s strategic focus and leading the training development/delivery team. Throughout his career, Peter has developed expertise in multiple telecom, networking and information technology specializations. Before joining CENGN, Peter had broad experience in teaching, training, and program development across the higher education and commercial education sectors. Peter holds a Ph.D. in Mathematics from Carleton University in Canada and has achieved multiple networking and IT industry certifications.

Plus de Peter Heath

Mises à jour du CENGN, dans votre boîte de réception.

S’inscrire