Services, équilibrage de charge et réseau

Concepts et ressources qui sous-tendent le réseau dans Kubernetes.

Le modèle réseau de Kubernetes

Le modèle réseau de Kubernetes repose sur plusieurs éléments :

  • Chaque pod d'un cluster reçoit sa propre adresse IP, unique dans tout le cluster.

    • Un pod a son propre namespace réseau privé, partagé par tous les conteneurs du pod. Les processus qui s'exécutent dans différents conteneurs d'un même pod peuvent communiquer entre eux via localhost.
  • Le réseau des pods (aussi appelé réseau du cluster) gère la communication entre les pods. Il garantit que (sauf segmentation réseau volontaire) :

    • Tous les pods peuvent communiquer avec tous les autres pods, qu'ils soient sur le même nœud ou sur des nœuds différents. Les pods peuvent communiquer entre eux directement, sans proxy ni traduction d'adresses (NAT).

      Sous Windows, cette règle ne s'applique pas aux pods qui utilisent le réseau de l'hôte.

    • Les agents d'un nœud (comme les démons système ou le kubelet) peuvent communiquer avec tous les pods de ce nœud.

  • L'API Service vous permet de fournir une adresse IP ou un nom d'hôte stable (durable) pour un service mis en œuvre par un ou plusieurs pods backend, alors que les pods qui composent ce service peuvent changer au fil du temps.

    • Kubernetes gère automatiquement des objets EndpointSlice qui donnent des informations sur les pods qui servent actuellement de backends à un Service.

    • Une implémentation de proxy de service surveille l'ensemble des objets Service et EndpointSlice, et programme le plan de données pour acheminer le trafic des services vers leurs backends, en utilisant les API du système d'exploitation ou du fournisseur cloud pour intercepter ou réécrire les paquets.

  • L'API Gateway (ou son prédécesseur, Ingress) vous permet de rendre des Services accessibles à des clients extérieurs au cluster.

    • Un mécanisme plus simple, mais moins configurable, pour faire entrer le trafic dans le cluster est proposé par le type: LoadBalancer de l'API Service, si vous utilisez un fournisseur de cloud compatible.
  • NetworkPolicy est une API intégrée à Kubernetes qui vous permet de contrôler le trafic entre les pods, ou entre les pods et le monde extérieur.

Dans les anciens systèmes de conteneurs, il n'existait pas de connectivité automatique entre les conteneurs de différents hôtes. Il fallait donc souvent créer explicitement des liens entre les conteneurs, ou faire correspondre les ports des conteneurs à des ports de l'hôte pour que les conteneurs d'autres hôtes puissent les joindre. Ce n'est pas nécessaire dans Kubernetes : dans son modèle, les pods peuvent être traités presque comme des VM ou des hôtes physiques du point de vue de l'allocation des ports, du nommage, de la découverte de services, de l'équilibrage de charge, de la configuration des applications et de la migration.

Seules quelques parties de ce modèle sont mises en œuvre par Kubernetes lui-même. Pour les autres, Kubernetes définit les API, mais la fonctionnalité correspondante est fournie par des composants externes, dont certains sont facultatifs :

  • La mise en place du namespace réseau des pods est assurée par un logiciel système qui implémente la Container Runtime Interface.

  • Le réseau des pods lui-même est géré par une implémentation du réseau des pods. Sous Linux, la plupart des environnements d'exécution de conteneurs utilisent la Container Networking Interface (CNI) pour dialoguer avec l'implémentation du réseau des pods. C'est pourquoi ces implémentations sont souvent appelées plugins CNI.

  • Kubernetes fournit une implémentation par défaut du proxy de service, appelée kube-proxy, mais certaines implémentations du réseau des pods utilisent plutôt leur propre proxy de service, plus étroitement intégré au reste de leur implémentation.

  • Les NetworkPolicies sont en général aussi mises en œuvre par l'implémentation du réseau des pods. (Certaines implémentations plus simples du réseau des pods ne prennent pas en charge les NetworkPolicies, ou un administrateur peut choisir de configurer le réseau des pods sans elles. Dans ces cas, l'API reste présente, mais elle n'a aucun effet.)

  • Il existe de nombreuses implémentations de Gateway API, certaines propres à des environnements cloud particuliers, d'autres plutôt orientées vers les environnements « bare metal », et d'autres plus génériques.

A suivre

Le tutoriel Connecter des applications avec des Services vous permet de découvrir les Services et le réseau de Kubernetes avec un exemple pratique.

Réseau du cluster explique comment mettre en place le réseau de votre cluster, et donne aussi une vue d'ensemble des technologies utilisées.

Pour découvrir des concepts réseau précis, consultez :

  • Service : exposer une application derrière un point d'accès externe unique
  • Ingress : routage HTTP/HTTPS sensible au protocole, à partir des URI, des noms d'hôte et des chemins
  • Gateway API : provisionnement dynamique de l'infrastructure et routage avancé du trafic
  • Politiques réseau : contrôler les flux de trafic au niveau des adresses IP ou des ports (couches 3 ou 4 du modèle OSI)
  • DNS pour les Services et les Pods : découvrir les services de votre cluster grâce au DNS

1 - Service

Exposez une application qui s'exécute dans votre cluster derrière un point d'accès externe unique, même lorsque la charge de travail est répartie sur plusieurs backends.

Dans Kubernetes, un Service est une manière abstraite d'exposer une application s'exécutant sur un ensemble de Pods en tant que service réseau.

L'un des buts des Services est de vous éviter de modifier votre application pour l'adapter à un mécanisme de découverte de services qu'elle ne connaît pas. Le code qui tourne dans vos Pods peut être une application conçue pour le cloud (cloud native) ou une ancienne application que vous avez simplement mise en conteneur. Dans les deux cas, un Service rend cet ensemble de Pods accessible sur le réseau, pour que des clients puissent l'utiliser.

Si votre application tourne dans un Déploiement, celui-ci crée et détruit des Pods au fil de l'eau. À un instant donné, vous ne savez pas combien de Pods fonctionnent correctement, ni même forcément comment ils s'appellent. Kubernetes crée et détruit les Pods pour atteindre l'état souhaité de votre cluster : ce sont des ressources éphémères. Ne comptez pas sur la fiabilité ni sur la durée de vie d'un Pod en particulier.

Chaque Pod a sa propre adresse IP (c'est au plugin réseau de le garantir). Or, pour un même Deployment, les Pods qui font tourner l'application à un instant donné ne sont pas forcément les mêmes un instant plus tard.

D'où un problème. Imaginons que certains Pods (les « backends ») rendent un service à d'autres Pods (les « frontends ») de votre cluster. Comment les frontends savent-ils à quelle adresse IP joindre un backend, alors que ces adresses peuvent changer d'un instant à l'autre ?

C'est précisément le rôle des Services.

Les Services dans Kubernetes

L'API Service de Kubernetes est une abstraction qui vous aide à exposer des groupes de Pods sur le réseau. Chaque objet Service définit un ensemble logique de points de terminaison (le plus souvent des Pods) et une politique qui indique comment rendre ces Pods accessibles.

Prenons un backend de traitement d'images sans état, qui tourne avec 3 réplicas. Ces 3 réplicas font exactement la même chose : peu importe à un frontend lequel lui répond. Et si les Pods qui composent ce backend changent, les frontends n'ont pas à le savoir ni à tenir à jour eux-mêmes la liste des backends.

C'est ce découplage que permet l'abstraction Service.

Le déploiement canary est un cas d'usage courant des Services. Pour voir ce que cela implique, suivez le tutoriel Déployer une release avec un déploiement canary.

L'ensemble des Pods ciblés par un Service est en général déterminé par un sélecteur que vous définissez. Pour découvrir d'autres façons de définir les points de terminaison d'un Service, consultez Services sans sélecteurs.

Si votre charge de travail communique en HTTP, vous pouvez utiliser un Ingress pour contrôler la façon dont le trafic web lui parvient. Un Ingress n'est pas un type de Service, mais il sert de point d'entrée à votre cluster. Il vous permet de regrouper vos règles de routage dans une seule ressource, et donc d'exposer derrière un seul point d'écoute plusieurs composants de votre charge de travail qui s'exécutent séparément dans le cluster.

L'API Gateway de Kubernetes va plus loin qu'Ingress et Service. Gateway est une famille d'API d'extension, mises en œuvre avec des CustomResourceDefinitions : vous l'ajoutez à votre cluster, puis vous l'utilisez pour configurer l'accès aux services réseau qui s'y exécutent.

Découverte de services cloud native

Si votre application peut utiliser les API Kubernetes pour la découverte de services, vous pouvez interroger le serveur d'API pour obtenir les EndpointSlices correspondants. Kubernetes met à jour les EndpointSlices d'un Service chaque fois que l'ensemble des Pods de ce Service change.

Pour les applications qui ne s'appuient pas sur ces API, Kubernetes permet de placer un port réseau ou un équilibreur de charge entre votre application et les Pods backend.

Dans les deux cas, votre charge de travail peut utiliser ces mécanismes de découverte de services pour trouver la cible à laquelle elle veut se connecter.

Définir un Service

Un Service est un objet (au même titre qu'un Pod ou une ConfigMap). Vous pouvez créer, consulter ou modifier des définitions de Service avec l'API Kubernetes. En général, c'est un outil comme kubectl qui fait ces appels d'API à votre place.

Par exemple, supposons que vous ayez un ensemble de Pods qui écoutent chacun sur le port TCP 9376 et qui portent le label app.kubernetes.io/name=MyApp. Vous pouvez définir un Service pour exposer ce port TCP :

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

Appliquer ce manifeste crée un nouveau Service nommé « my-service », avec le type de Service ClusterIP par défaut. Le Service cible le port TCP 9376 de tout Pod qui porte le label app.kubernetes.io/name: MyApp.

Kubernetes attribue à ce Service une adresse IP (l'adresse IP de cluster), utilisée par le mécanisme d'IP virtuelles. Pour plus de détails sur ce mécanisme, lisez IP virtuelles et proxys de service.

Le contrôleur de ce Service recherche en continu les Pods qui correspondent à son sélecteur, et met à jour au besoin les EndpointSlices du Service.

Le nom d'un objet Service doit être un nom de label RFC 1123 valide.

Note:

Un Service peut associer n'importe quel port entrant à un targetPort. Par défaut, et pour plus de simplicité, le targetPort prend la même valeur que le champ port.

Définitions de ports

Les ports définis dans les Pods ont des noms, auxquels vous pouvez faire référence dans l'attribut targetPort d'un Service. Par exemple, on peut associer le targetPort du Service au port du Pod de la façon suivante :

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app.kubernetes.io/name: proxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: proxy
spec:
  containers:
  - name: nginx
    image: nginx:stable
    ports:
      - containerPort: 80
        name: http-web-svc

Cela fonctionne même si le Service regroupe des Pods différents qui utilisent le même nom de port, avec le même protocole réseau mais des numéros de port différents. Vous gagnez ainsi beaucoup de souplesse pour déployer et faire évoluer vos Services. Par exemple, vous pouvez changer le numéro de port qu'exposent les Pods dans la version suivante de votre logiciel backend, sans casser les clients.

Le protocole par défaut des Services est TCP ; vous pouvez aussi utiliser n'importe quel autre protocole pris en charge.

Comme de nombreux Services doivent exposer plusieurs ports, Kubernetes prend en charge plusieurs définitions de ports pour un même Service. Chaque définition de port peut utiliser le même protocol que les autres ou un protocole différent.

Services sans sélecteurs

La plupart du temps, un Service donne accès à des Pods Kubernetes grâce à son sélecteur. Mais un Service sans sélecteur, associé aux objets EndpointSlices correspondants, peut aussi servir d'abstraction pour d'autres types de backends, y compris des backends qui s'exécutent en dehors du cluster.

Par exemple :

  • Vous voulez utiliser un cluster de bases de données externe en production, mais vos propres bases de données dans votre environnement de test.
  • Vous voulez faire pointer votre Service vers un Service d'un autre Namespace ou d'un autre cluster.
  • Vous migrez une charge de travail vers Kubernetes et, pendant la phase d'évaluation, vous n'exécutez qu'une partie de vos backends dans Kubernetes.

Dans tous ces cas, vous pouvez définir un Service sans sélecteur de Pods. Par exemple :

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376

Comme ce Service n'a pas de sélecteur, les objets EndpointSlice correspondants ne sont pas créés automatiquement. Pour associer le Service à l'adresse réseau et au port où s'exécute le backend, ajoutez vous-même un objet EndpointSlice. Par exemple :

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: my-service-1 # by convention, use the name of the Service
                     # as a prefix for the name of the EndpointSlice
  labels:
    # You should set the "kubernetes.io/service-name" label.
    # Set its value to match the name of the Service
    kubernetes.io/service-name: my-service
addressType: IPv4
ports:
  - name: http # should match with the name of the service port defined above
    appProtocol: http
    protocol: TCP
    port: 9376
endpoints:
  - addresses:
      - "10.4.5.6"
  - addresses:
      - "10.1.2.3"

EndpointSlices personnalisés

Lorsque vous créez un objet EndpointSlice pour un Service, vous pouvez lui donner n'importe quel nom. Chaque EndpointSlice d'un namespace doit avoir un nom unique. Pour relier un EndpointSlice à un Service, posez sur cet EndpointSlice le label kubernetes.io/service-name.

Note:

Les adresses IP des points de terminaison ne doivent pas être des adresses de loopback (127.0.0.0/8 pour IPv4, ::1/128 pour IPv6), ni des adresses link-local (169.254.0.0/16 et 224.0.0.0/24 pour IPv4, fe80::/64 pour IPv6).

Les adresses IP des points de terminaison ne peuvent pas être les adresses IP de cluster d'autres Services Kubernetes, car kube-proxy ne prend pas en charge les adresses IP virtuelles comme destination.

Pour un EndpointSlice que vous créez vous-même, ou par votre propre code, vous devriez aussi choisir une valeur pour le label endpointslice.kubernetes.io/managed-by. Si vous écrivez votre propre contrôleur pour gérer les EndpointSlices, envisagez une valeur du type "my-domain.example/name-of-controller". Si vous utilisez un outil tiers, utilisez le nom de l'outil tout en minuscules, en remplaçant les espaces et les autres signes de ponctuation par des tirets (-). Si des personnes gèrent les EndpointSlices directement avec un outil comme kubectl, utilisez un nom qui décrit cette gestion manuelle, comme "staff" ou "cluster-admins". Évitez la valeur réservée "controller", qui identifie les EndpointSlices gérés par le plan de contrôle de Kubernetes lui-même.

Accéder à un Service sans sélecteur

Un Service sans sélecteur s'utilise exactement comme un Service avec sélecteur. Dans l'exemple de Service sans sélecteur, le trafic est acheminé vers l'un des deux points de terminaison définis dans le manifeste de l'EndpointSlice : une connexion TCP vers 10.1.2.3 ou 10.4.5.6, sur le port 9376.

Note:

Le serveur d'API Kubernetes refuse de servir de proxy vers des points de terminaison qui ne correspondent pas à des pods. Les actions comme kubectl port-forward service/<service-name> forwardedPort:servicePort sur un Service sans sélecteur échouent donc. Cette contrainte empêche d'utiliser le serveur d'API Kubernetes comme proxy vers des points de terminaison auxquels l'appelant n'a peut-être pas le droit d'accéder.

Un Service ExternalName est un cas particulier : il n'a pas de sélecteur et utilise des noms DNS à la place. Pour plus d'informations, consultez la section ExternalName.

EndpointSlices

Feature state: Stable since Kubernetes v1.21

Les EndpointSlices sont des objets qui représentent une partie (une tranche) des points de terminaison réseau d'un Service.

Votre cluster Kubernetes suit le nombre de points de terminaison de chaque EndpointSlice. Quand un Service a tant de points de terminaison qu'un seuil est atteint, Kubernetes ajoute un EndpointSlice vide et y enregistre les nouveaux points de terminaison. Par défaut, Kubernetes crée un nouvel EndpointSlice quand tous les EndpointSlices existants contiennent au moins 100 points de terminaison, et seulement au moment où un point de terminaison supplémentaire doit être ajouté.

Consultez EndpointSlices pour plus d'informations sur cette API.

Endpoints (obsolète)

Feature state: Deprecated since Kubernetes v1.33

L'API EndpointSlice est l'évolution de l'ancienne API Endpoints. Par rapport à EndpointSlice, l'API Endpoints, obsolète, pose plusieurs problèmes :

  • Elle ne prend pas en charge les clusters en double pile.
  • Elle ne contient pas les informations nécessaires aux fonctionnalités plus récentes, comme trafficDistribution.
  • Elle tronque la liste des points de terminaison si celle-ci est trop longue pour tenir dans un seul objet.

Il est donc recommandé à tous les clients d'utiliser l'API EndpointSlice plutôt qu'Endpoints.

Points de terminaison en surcapacité

Kubernetes limite le nombre de points de terminaison que peut contenir un objet Endpoints. Au-delà de 1000 points de terminaison pour un Service, Kubernetes tronque les données de l'objet Endpoints. Comme un Service peut être relié à plusieurs EndpointSlices, cette limite de 1000 ne concerne que l'ancienne API Endpoints.

Dans ce cas, Kubernetes enregistre au maximum 1000 points de terminaison backend dans l'objet Endpoints, et y pose une annotation : endpoints.kubernetes.io/over-capacity: truncated. Le plan de contrôle retire aussi cette annotation lorsque le nombre de Pods backend repasse sous 1000.

Le trafic est toujours envoyé aux backends, mais tout mécanisme d'équilibrage de charge qui s'appuie sur l'ancienne API Endpoints n'utilise au maximum que 1000 des points de terminaison disponibles.

Pour la même raison, vous ne pouvez pas mettre à jour manuellement un objet Endpoints pour lui donner plus de 1000 points de terminaison.

Protocole applicatif

Feature state: Stable since Kubernetes v1.20

Le champ appProtocol permet d'indiquer le protocole applicatif de chaque port d'un Service. Les implémentations s'en servent comme d'une indication pour offrir un comportement plus riche avec les protocoles qu'elles connaissent. La valeur de ce champ est recopiée dans les objets Endpoints et EndpointSlice correspondants.

Ce champ suit la syntaxe standard des labels Kubernetes. Les valeurs valides sont l'une des suivantes :

  • Les noms de services standard de l'IANA.

  • Des noms préfixés définis par l'implémentation, comme mycompany.com/my-custom-protocol.

  • Des noms préfixés définis par Kubernetes :

ProtocoleDescription
kubernetes.io/h2cHTTP/2 en clair, comme décrit dans la RFC 9113
kubernetes.io/wsWebSocket en clair, comme décrit dans la RFC 6455
kubernetes.io/wssWebSocket sur TLS, comme décrit dans la RFC 6455

Services multi-ports

Certains Services doivent exposer plusieurs ports. Kubernetes vous permet de configurer plusieurs définitions de ports sur un même objet Service. Dans ce cas, vous devez donner un nom à chacun des ports pour éviter toute ambiguïté. Par exemple :

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
    - name: https
      protocol: TCP
      port: 443
      targetPort: 9377

Note:

Comme pour les noms Kubernetes en général, les noms de ports ne doivent contenir que des caractères alphanumériques en minuscules et des -. Les noms de ports doivent aussi commencer et se terminer par un caractère alphanumérique.

Par exemple, les noms 123-abc et web sont valides, mais 123_abc et -web ne le sont pas.

Type de Service

Pour certaines parties de votre application (par exemple les frontends), vous voudrez peut-être exposer un Service sur une adresse IP externe, accessible depuis l'extérieur de votre cluster.

Le type d'un Service Kubernetes vous permet d'indiquer quel genre de Service vous voulez.

Voici les valeurs possibles de type et leur comportement :

ClusterIP
Expose le Service sur une adresse IP interne au cluster : le Service n'est alors joignable que depuis le cluster. C'est la valeur par défaut si vous n'indiquez pas explicitement de type pour un Service. Vous pouvez exposer le Service sur Internet avec un Ingress ou une Gateway.
NodePort
Expose le Service sur un port statique (le NodePort) de l'adresse IP de chaque nœud. Pour rendre ce port de nœud disponible, Kubernetes configure aussi une adresse IP de cluster, comme pour un Service de type: ClusterIP.
LoadBalancer
Expose le Service à l'extérieur grâce à un équilibreur de charge externe. Kubernetes ne fournit pas directement de composant d'équilibrage de charge : vous devez en fournir un, ou intégrer votre cluster Kubernetes à un fournisseur de cloud.
ExternalName
Associe le Service au contenu du champ externalName (par exemple au nom d'hôte api.foo.bar.example). Le serveur DNS de votre cluster est alors configuré pour renvoyer un enregistrement CNAME avec ce nom d'hôte externe. Aucun proxy, de quelque sorte que ce soit, n'est mis en place.

Le champ type de l'API Service est conçu comme une suite de fonctionnalités imbriquées : chaque niveau s'ajoute au précédent. Il y a toutefois une exception à ce principe : vous pouvez définir un Service LoadBalancer en désactivant l'allocation de NodePort pour l'équilibreur de charge.

type: ClusterIP

Ce type de Service, utilisé par défaut, attribue une adresse IP prise dans un pool d'adresses que votre cluster réserve à cet usage.

Plusieurs autres types de Service reposent sur le type ClusterIP.

Si vous définissez un Service dont .spec.clusterIP vaut "None", Kubernetes n'attribue pas d'adresse IP. Consultez Services headless pour plus d'informations.

Choisir votre propre adresse IP

Vous pouvez choisir vous-même l'adresse IP de cluster dans la requête de création d'un Service, en renseignant le champ .spec.clusterIP. C'est utile par exemple si vous avez une entrée DNS existante à réutiliser, ou d'anciens systèmes configurés pour une adresse IP précise et difficiles à reconfigurer.

L'adresse IP que vous choisissez doit être une adresse IPv4 ou IPv6 valide, comprise dans la plage CIDR service-cluster-ip-range configurée pour le serveur d'API. Si vous essayez de créer un Service avec une valeur clusterIP invalide, le serveur d'API renvoie un code d'état HTTP 422 pour signaler le problème.

Lisez Éviter les collisions pour savoir comment Kubernetes aide à réduire le risque, et l'impact, d'une situation où deux Services différents essaient d'utiliser la même adresse IP.

type: NodePort

Si vous donnez au champ type la valeur NodePort, le plan de contrôle de Kubernetes alloue un port dans une plage définie par l'option --service-node-port-range (par défaut : 30000-32767). Sur chaque nœud, ce port (le même numéro partout) est relayé vers votre Service. Votre Service indique le port alloué dans son champ .spec.ports[*].nodePort.

Utiliser un NodePort vous laisse libre de mettre en place votre propre solution d'équilibrage de charge, de configurer des environnements que Kubernetes ne prend pas entièrement en charge, ou même d'exposer directement les adresses IP d'un ou plusieurs nœuds.

Pour un Service NodePort, Kubernetes alloue en plus un port (TCP, UDP ou SCTP, selon le protocole du Service). Chaque nœud du cluster se configure pour écouter sur ce port et transférer le trafic vers l'un des points de terminaison prêts du Service. Depuis l'extérieur du cluster, vous pouvez joindre le Service de type: NodePort en vous connectant à n'importe quel nœud avec le bon protocole (par exemple TCP) et le bon port (celui attribué au Service).

Choisir votre propre port

Si vous voulez un numéro de port précis, indiquez-le dans le champ nodePort. Le plan de contrôle vous attribuera ce port, ou signalera l'échec de la transaction de l'API. C'est donc à vous de gérer les éventuelles collisions de ports. Le numéro de port doit aussi être valide, c'est-à-dire compris dans la plage configurée pour les NodePorts.

Voici un exemple de manifeste pour un Service de type: NodePort qui indique une valeur de NodePort (30007 dans cet exemple) :

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      # By default and for convenience, the `targetPort` is set to
      # the same value as the `port` field.
      targetPort: 80
      # Optional field
      # By default and for convenience, the Kubernetes control plane
      # will allocate a port from a range (default: 30000-32767)
      nodePort: 30007

Réserver des plages de NodePort pour éviter les collisions

La politique d'attribution des ports aux Services NodePort vaut aussi bien pour l'attribution automatique que pour l'attribution manuelle. Quand un utilisateur veut créer un Service NodePort sur un port précis, ce port peut entrer en conflit avec un port déjà attribué.

Pour éviter ce problème, la plage de ports des Services NodePort est divisée en deux bandes. L'attribution dynamique utilise par défaut la bande haute, et peut passer à la bande basse une fois la bande haute épuisée. Les utilisateurs peuvent ainsi choisir leurs ports dans la bande basse, avec moins de risque de collision.

Avec la plage NodePort par défaut 30000-32767, les bandes sont réparties ainsi :

  • Bande statique : 30000-30085
  • Bande dynamique : 30086-32767

Consultez Avoid Collisions Assigning Ports to NodePort Services pour savoir comment les bandes statique et dynamique sont calculées.

Configuration des adresses IP des Services de type: NodePort

Lorsque kube-proxy est utilisé en mode iptables, les Services NodePort sont disponibles par défaut sur toutes les adresses IP du nœud. En mode nftables, ils ne sont disponibles par défaut que sur l'adresse IP principale du nœud (ou les adresses IP principales en double pile).

Vous pouvez changer l'ensemble des adresses IP de nœud sur lesquelles les Services NodePort sont disponibles avec l'option --nodeport-addresses de kube-proxy, ou le champ équivalent nodePortAddresses du fichier de configuration de kube-proxy. Elle accepte une liste de blocs d'adresses IP séparés par des virgules (par exemple 10.0.0.0/8, 192.0.2.0/25), ou un ou plusieurs des mots-clés suivants :

  • primary : l'adresse IPv4 et/ou IPv6 principale du nœud, d'après l'objet Node. (C'est la valeur par défaut en mode nftables.)
  • localhost : les adresses de loopback du nœud (127.0.0.0/8, ::1/128).
  • all : toutes les adresses. (C'est la valeur par défaut en modes iptables et ipvs.)

Par exemple, si vous démarrez kube-proxy avec l'option --nodeport-addresses=192.168.0.0/24, kube-proxy essaie de trouver sur chaque nœud une adresse IP locale dans ce sous-réseau, et ne sert les Services NodePort que sur cette adresse IP.

Services de type: NodePort via localhost

Les mécanismes qu'utilisent les proxys de service pour mettre en œuvre les Services NodePort ne permettent pas toujours de les rendre disponibles sur localhost. Pour kube-proxy :

  • En mode iptables, avec une valeur de --nodeport-addresses qui inclut 127.0.0.1, les Services NodePort sont disponibles sur 127.0.0.1. Cela nécessite toutefois d'activer un sysctl du noyau (route_localnet) qui peut avoir des effets secondaires néfastes pour la sécurité dans certains clusters. Pour désactiver les NodePorts sur localhost en mode iptables, passez --iptables-localhost-nodeports false à kube-proxy, ou donnez à --nodeport-addresses une plage qui n'inclut pas 127.0.0.1.

  • En mode ipvs, ou en mode iptables dans un cluster IPv6 en pile simple, les Services NodePort ne sont pas disponibles sur localhost.

Feature state: Alpha since Kubernetes v1.37; disabled by default
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the KubeProxyNFTablesLocalhostNodePorts feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

  • En mode nftables, les Services NodePort sont disponibles sur localhost lorsque la feature gate KubeProxyNFTablesLocalhostNodePorts est activée et que --nodeport-addresses a une valeur qui inclut explicitement localhost. (La valeur all seule ne suffit pas.) Cette fonctionnalité redirige les connexions NodePort sur localhost vers un proxy en espace utilisateur : elle est donc moins efficace que le proxy de service habituel.

Les plugins réseau tiers qui ont leur propre implémentation de proxy de service peuvent prendre en charge ou non les NodePorts localhost : consultez la documentation de ces plugins.

type: LoadBalancer

Chez les fournisseurs de cloud qui proposent des équilibreurs de charge externes, la valeur LoadBalancer du champ type provisionne un équilibreur de charge pour votre Service. L'équilibreur de charge est créé de façon asynchrone, et les informations le concernant sont publiées dans le champ .status.loadBalancer du Service. Par exemple :

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.0.2.127

Le trafic de l'équilibreur de charge externe est dirigé vers les Pods backend. C'est le fournisseur de cloud qui décide comment répartir la charge.

Pour mettre en œuvre un Service de type: LoadBalancer, Kubernetes commence en général par faire les mêmes changements que si vous aviez demandé un Service de type: NodePort. Le composant cloud-controller-manager configure ensuite l'équilibreur de charge externe pour qu'il transfère le trafic vers le port de nœud attribué.

Vous pouvez configurer un Service avec équilibreur de charge pour ne pas lui attribuer de port de nœud, à condition que l'implémentation du fournisseur de cloud le permette.

Certains fournisseurs de cloud vous permettent d'indiquer le loadBalancerIP. Dans ce cas, l'équilibreur de charge est créé avec le loadBalancerIP indiqué par l'utilisateur. Si le champ loadBalancerIP n'est pas renseigné, l'équilibreur de charge est configuré avec une adresse IP éphémère. Si vous indiquez un loadBalancerIP mais que votre fournisseur de cloud ne prend pas en charge cette fonctionnalité, le champ loadbalancerIP que vous avez défini est ignoré.

Note:

Le champ .spec.loadBalancerIP d'un Service est obsolète depuis Kubernetes v1.24.

Ce champ était insuffisamment spécifié et son sens varie selon les implémentations. Il ne peut pas non plus prendre en charge le réseau en double pile. Ce champ pourrait être supprimé dans une future version de l'API.

Si votre fournisseur permet d'indiquer la ou les adresses IP de l'équilibreur de charge d'un Service par une annotation (qui lui est propre), vous devriez passer à cette méthode.

Si vous écrivez du code pour intégrer un équilibreur de charge à Kubernetes, évitez d'utiliser ce champ. Vous pouvez vous appuyer sur Gateway plutôt que sur Service, ou définir sur le Service vos propres annotations (propres au fournisseur) qui portent l'information équivalente.

Impact de la disponibilité des nœuds sur le trafic de l'équilibreur de charge

Les vérifications de santé des équilibreurs de charge sont essentielles aux applications modernes. Elles permettent de déterminer vers quel serveur (machine virtuelle ou adresse IP) l'équilibreur de charge doit envoyer le trafic. Les API Kubernetes ne définissent pas comment mettre en œuvre ces vérifications pour les équilibreurs de charge gérés par Kubernetes : ce sont les fournisseurs de cloud (et les personnes qui écrivent le code d'intégration) qui décident du comportement. Ces vérifications de santé sont très utilisées pour prendre en charge le champ externalTrafficPolicy des Services.

Équilibreurs de charge avec plusieurs protocoles

This is a stable feature in Kubernetes, and has been since the 1.26 release. You can no longer toggle this feature (the associated feature gate has been removed).

Par défaut, pour les Services de type LoadBalancer qui définissent plusieurs ports, tous les ports doivent avoir le même protocole, et ce protocole doit être pris en charge par le fournisseur de cloud.

La feature gate MixedProtocolLBService (activée par défaut pour kube-apiserver depuis la v1.24) permet d'utiliser des protocoles différents pour les Services de type LoadBalancer qui définissent plusieurs ports.

Note:

C'est votre fournisseur de cloud qui définit les protocoles utilisables pour les Services avec équilibreur de charge, et il peut imposer des restrictions plus strictes que l'API Kubernetes.

Désactiver l'allocation de NodePort pour l'équilibreur de charge

Feature state: Stable since Kubernetes v1.24

Vous pouvez, si vous le souhaitez, désactiver l'allocation de ports de nœud pour un Service de type: LoadBalancer en donnant au champ spec.allocateLoadBalancerNodePorts la valeur false. Ne l'utilisez que pour les implémentations d'équilibreur de charge qui acheminent le trafic directement vers les pods, sans passer par des ports de nœud. Par défaut, spec.allocateLoadBalancerNodePorts vaut true, et les Services de type LoadBalancer continuent d'allouer des ports de nœud. Si vous passez spec.allocateLoadBalancerNodePorts à false sur un Service existant qui a déjà des ports de nœud alloués, ces ports ne sont pas libérés automatiquement. Pour les libérer, vous devez supprimer explicitement l'entrée nodePorts de chaque port du Service.

Indiquer la classe d'implémentation de l'équilibreur de charge

Feature state: Stable since Kubernetes v1.24

Pour un Service dont le type vaut LoadBalancer, le champ .spec.loadBalancerClass vous permet d'utiliser une autre implémentation d'équilibreur de charge que celle fournie par défaut par le fournisseur de cloud.

Par défaut, .spec.loadBalancerClass n'est pas défini, et un Service de type LoadBalancer utilise l'implémentation d'équilibreur de charge par défaut du fournisseur de cloud si le cluster est configuré avec un fournisseur de cloud via l'option --cloud-provider de ses composants.

Si vous indiquez .spec.loadBalancerClass, Kubernetes considère qu'une implémentation d'équilibreur de charge correspondant à cette classe surveille les Services. Toute implémentation d'équilibreur de charge par défaut (par exemple celle du fournisseur de cloud) ignore les Services pour lesquels ce champ est défini. spec.loadBalancerClass ne peut être défini que sur un Service de type LoadBalancer. Une fois défini, il ne peut plus être modifié. La valeur de spec.loadBalancerClass doit être un identifiant au format d'un label, avec un préfixe facultatif, comme « internal-vip » ou « example.com/internal-vip ». Les noms sans préfixe sont réservés aux utilisateurs finaux.

Mode d'adresse IP de l'équilibreur de charge

Pour un Service de type: LoadBalancer, un contrôleur peut définir .status.loadBalancer.ingress.ipMode. Le champ .status.loadBalancer.ingress.ipMode indique le comportement de l'adresse IP de l'équilibreur de charge. Il ne peut être indiqué que si le champ .status.loadBalancer.ingress.ip l'est aussi.

.status.loadBalancer.ingress.ipMode peut prendre deux valeurs : « VIP » et « Proxy ». La valeur par défaut, « VIP », signifie que le trafic arrive au nœud avec pour destination l'adresse IP et le port de l'équilibreur de charge. Avec « Proxy », deux cas sont possibles, selon la façon dont l'équilibreur de charge du fournisseur de cloud achemine le trafic :

  • Si le trafic arrive au nœud puis est traduit (DNAT) vers le pod, la destination est l'adresse IP et le port de nœud ;
  • Si le trafic arrive directement au pod, la destination est l'adresse IP et le port du pod.

Les implémentations de Service peuvent utiliser cette information pour ajuster le routage du trafic.

Équilibreur de charge interne

Dans un environnement mixte, il est parfois nécessaire d'acheminer le trafic de Services situés dans le même bloc d'adresses réseau (virtuel).

Dans un environnement DNS split-horizon, il vous faudrait deux Services pour acheminer à la fois le trafic externe et le trafic interne vers vos points de terminaison.

Pour configurer un équilibreur de charge interne, ajoutez à votre Service l'une des annotations suivantes, selon le fournisseur de cloud que vous utilisez :

Sélectionnez l'un des onglets.

metadata:
  name: my-service
  annotations:
    networking.gke.io/load-balancer-type: "Internal"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internal"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/azure-load-balancer-internal: "true"
metadata:
  name: my-service
  annotations:
    service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: "private"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"
metadata:
  annotations:
    service.kubernetes.io/qcloud-loadbalancer-internal-subnetid: subnet-xxxxx
metadata:
  annotations:
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/oci-load-balancer-internal: true

type: ExternalName

Un Service de type ExternalName est associé à un nom DNS, et non à un sélecteur classique comme my-service ou cassandra. Vous indiquez ce nom avec le paramètre spec.externalName.

Par exemple, cette définition associe le Service my-service du namespace prod à my.database.example.com :

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

Note:

Un Service de type: ExternalName accepte une adresse IPv4 sous forme de chaîne, mais il la traite comme un nom DNS composé de chiffres, et non comme une adresse IP (Internet n'autorise d'ailleurs pas ce genre de noms dans le DNS). Les serveurs DNS ne résolvent pas les Services dont le nom externe ressemble à une adresse IPv4.

Pour associer un Service directement à une adresse IP précise, envisagez plutôt des Services headless.

Quand on recherche l'hôte my-service.prod.svc.cluster.local, le Service DNS du cluster renvoie un enregistrement CNAME de valeur my.database.example.com. On accède à my-service comme aux autres Services, à une différence près, essentielle : la redirection se fait au niveau du DNS, et non par un proxy ou un transfert. Si vous décidez plus tard d'intégrer votre base de données au cluster, vous pourrez démarrer ses Pods, ajouter les sélecteurs ou les points de terminaison nécessaires, et changer le type du Service.

Avertissement:

ExternalName peut poser problème avec certains protocoles courants, dont HTTP et HTTPS. En effet, le nom d'hôte qu'utilisent les clients de votre cluster n'est pas le même que le nom auquel l'ExternalName fait référence.

Pour les protocoles qui utilisent des noms d'hôte, cette différence peut entraîner des erreurs ou des réponses inattendues. Les requêtes HTTP auront un en-tête Host: que le serveur d'origine ne reconnaît pas ; les serveurs TLS ne pourront pas fournir de certificat correspondant au nom d'hôte auquel le client s'est connecté.

Services headless

Parfois, vous n'avez besoin ni d'équilibrage de charge ni d'une adresse IP unique pour le Service. Dans ce cas, vous pouvez créer ce qu'on appelle des Services headless, en indiquant explicitement "None" comme adresse IP de cluster (.spec.clusterIP).

Un Service headless vous permet de vous interfacer avec d'autres mécanismes de découverte de services, sans dépendre de l'implémentation de Kubernetes.

Pour les Services headless, aucune adresse IP de cluster n'est allouée, kube-proxy ne gère pas ces Services, et la plateforme ne fait pour eux ni équilibrage de charge ni proxy.

Un Service headless permet à un client de se connecter directement au Pod de son choix. Ces Services ne configurent ni routes ni transfert de paquets à l'aide d'adresses IP virtuelles et de proxys : ils publient à la place les adresses IP de chaque pod via des enregistrements DNS internes, servis par le service DNS du cluster. Pour définir un Service headless, créez un Service dont .spec.type vaut ClusterIP (la valeur par défaut de type), et donnez en plus à .spec.clusterIP la valeur None.

La chaîne None est un cas particulier : ce n'est pas la même chose que de laisser le champ .spec.clusterIP vide.

La configuration automatique du DNS dépend de la présence ou non de sélecteurs sur le Service :

Avec sélecteurs

Pour les Services headless qui définissent des sélecteurs, le contrôleur des points de terminaison crée des EndpointSlices dans l'API Kubernetes et modifie la configuration DNS pour qu'elle renvoie des enregistrements A ou AAAA (adresses IPv4 ou IPv6) qui pointent directement vers les Pods du Service.

Sans sélecteurs

Pour les Services headless qui ne définissent pas de sélecteurs, le plan de contrôle ne crée pas d'objets EndpointSlice. Le système DNS recherche et configure toutefois, selon le cas :

  • des enregistrements DNS CNAME pour les Services de type: ExternalName ;
  • des enregistrements DNS A / AAAA pour toutes les adresses IP des points de terminaison prêts du Service, pour tous les types de Service autres que ExternalName.
    • Pour les points de terminaison IPv4, le système DNS crée des enregistrements A.
    • Pour les points de terminaison IPv6, le système DNS crée des enregistrements AAAA.

Lorsque vous définissez un Service headless sans sélecteur, le port doit être identique au targetPort.

Découvrir les services

Pour les clients qui s'exécutent dans votre cluster, Kubernetes propose deux méthodes principales pour trouver un Service : les variables d'environnement et le DNS.

Variables d'environnement

Lorsqu'un Pod s'exécute sur un nœud, le kubelet ajoute un ensemble de variables d'environnement pour chaque Service actif. Il ajoute les variables {SVCNAME}_SERVICE_HOST et {SVCNAME}_SERVICE_PORT, où le nom du Service est mis en majuscules et les tirets sont remplacés par des tirets bas.

Par exemple, le Service redis-primary, qui expose le port TCP 6379 et a reçu l'adresse IP de cluster 10.0.0.11, produit les variables d'environnement suivantes :

REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11

Note:

Si un Pod doit accéder à un Service et que vous transmettez le port et l'adresse IP de cluster aux Pods clients par des variables d'environnement, vous devez créer le Service avant les Pods clients. Sinon, les variables d'environnement de ces Pods ne seront pas renseignées.

Si vous passez uniquement par le DNS pour trouver l'adresse IP de cluster d'un Service, cette question d'ordre ne se pose pas.

Kubernetes fournit aussi des variables compatibles avec la fonctionnalité « legacy container links » de Docker Engine. Pour voir comment c'est mis en œuvre dans Kubernetes, lisez makeLinkVariables.

DNS

Vous pouvez (et devriez presque toujours) installer un service DNS dans votre cluster Kubernetes avec un module complémentaire.

Un serveur DNS qui connaît le cluster, comme CoreDNS, surveille l'API Kubernetes pour repérer les nouveaux Services et crée un ensemble d'enregistrements DNS pour chacun. Si le DNS est activé dans tout le cluster, tous les Pods devraient pouvoir résoudre automatiquement les Services par leur nom DNS.

Par exemple, si vous avez un Service appelé my-service dans un namespace Kubernetes my-ns, le plan de contrôle et le Service DNS créent ensemble un enregistrement DNS pour my-service.my-ns. Les Pods du namespace my-ns devraient trouver le Service en résolvant simplement le nom my-service (my-service.my-ns fonctionne aussi).

Les Pods des autres namespaces doivent utiliser le nom qualifié my-service.my-ns. Ces noms sont résolus en l'adresse IP de cluster du Service.

Kubernetes prend aussi en charge les enregistrements DNS SRV (Service) pour les ports nommés. Si le Service my-service.my-ns a un port nommé http dont le protocole est TCP, vous pouvez faire une requête DNS SRV sur _http._tcp.my-service.my-ns pour obtenir le numéro de port de http, ainsi que l'adresse IP.

Les Services ExternalName ne sont accessibles que par le serveur DNS de Kubernetes. Pour en savoir plus sur la résolution des ExternalName, consultez DNS pour les Services et les Pods.

Mécanisme d'adressage IP virtuel

La page IP virtuelles et proxys de service explique le mécanisme que Kubernetes fournit pour exposer un Service avec une adresse IP virtuelle.

Politiques de trafic

Vous pouvez définir les champs .spec.internalTrafficPolicy et .spec.externalTrafficPolicy pour contrôler la façon dont Kubernetes achemine le trafic vers les backends en bonne santé (« ready »).

Consultez Politiques de trafic pour plus de détails.

Contrôle de la distribution du trafic

Le champ .spec.trafficDistribution est un autre moyen d'influencer le routage du trafic dans un Service Kubernetes. Les politiques de trafic apportent des garanties sémantiques strictes ; la distribution du trafic, elle, vous permet d'exprimer des préférences (par exemple acheminer le trafic vers les points de terminaison les plus proches dans la topologie). Cela peut aider à optimiser les performances, les coûts ou la fiabilité. Dans Kubernetes 1.37, les valeurs suivantes sont prises en charge :

PreferSameZone
Indique une préférence pour acheminer le trafic vers des points de terminaison situés dans la même zone que le client.
PreferSameNode
Indique une préférence pour acheminer le trafic vers des points de terminaison situés sur le même nœud que le client.
PreferClose (obsolète)
Ancien alias de PreferSameZone, au sens moins explicite.

Si le champ n'est pas défini, l'implémentation applique sa stratégie de routage par défaut.

Consultez Distribution du trafic pour plus de détails.

Affinité de session

Si vous voulez vous assurer que les connexions d'un client donné arrivent toujours au même Pod, vous pouvez configurer une affinité de session basée sur l'adresse IP du client. Lisez affinité de session pour en savoir plus.

Adresses IP externes

Feature state: Deprecated since Kubernetes v1.36

Tous les utilisateurs devraient commencer à migrer pour ne plus utiliser externalIPs. Envisagez plutôt un contrôleur d'équilibreur de charge externe ou une implémentation de Gateway API.

Si des adresses IP externes sont routées vers un ou plusieurs nœuds du cluster, les Services Kubernetes peuvent être exposés sur ces externalIPs. Quand du trafic réseau arrive dans le cluster avec l'adresse IP externe pour destination et le port de ce Service, les règles et les routes configurées par Kubernetes garantissent qu'il est acheminé vers l'un des points de terminaison de ce Service.

Vous pouvez indiquer des externalIPs pour n'importe quel type de Service. Dans l'exemple ci-dessous, les clients peuvent joindre le Service "my-service" en TCP sur "198.51.100.32:80" (calculé à partir de .spec.externalIPs[] et .spec.ports[].port).

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 49152
  externalIPs:
    - 198.51.100.32

Note:

Kubernetes ne gère pas l'allocation des externalIPs : c'est la responsabilité de l'administrateur du cluster.

Objet de l'API

Service est une ressource de premier niveau de l'API REST de Kubernetes. Vous trouverez plus de détails sur l'objet Service de l'API.

A suivre

Pour en savoir plus sur les Services et leur place dans Kubernetes :

Pour aller plus loin, lisez les pages suivantes :

2 - Ingress

Rendez votre service réseau HTTP (ou HTTPS) accessible grâce à un mécanisme de configuration sensible au protocole et comprend les concepts du web comme les URI, les noms d'hôte, les chemins, etc. Le concept d'Ingress vous permet d'acheminer le trafic vers différents backends selon des règles que vous définissez via l'API Kubernetes.

Feature state: Stable since Kubernetes v1.19

Un objet API qui gère l'accès externe aux services d'un cluster, typiquement HTTP.

L'Ingress peut fournir un équilibrage de charge, une terminaison SSL ainsi qu'un hébergement virtuel basé sur un nom.

Note:

Le projet Kubernetes recommande d'utiliser Gateway plutôt qu'Ingress. L'API Ingress est figée.

Cela signifie que :

  • L'API Ingress est en disponibilité générale (GA) et soumise aux garanties de stabilité prévues pour les API GA. Le projet Kubernetes n'a pas l'intention de retirer Ingress de Kubernetes.
  • L'API Ingress n'est plus développée et ne recevra plus aucune modification ni mise à jour.

Terminologie

Par souci de clarté, ce guide définit les termes suivants :

  • Nœud (Node) : une machine de travail de Kubernetes, qui fait partie d'un cluster.
  • Cluster : un ensemble de nœuds qui exécutent des applications conteneurisées gérées par Kubernetes. Dans cet exemple, comme dans la plupart des déploiements Kubernetes courants, les nœuds du cluster ne sont pas exposés sur l'Internet public.
  • Routeur de bordure (edge router) : un routeur qui applique la politique de pare-feu de votre cluster. Il peut s'agir d'une passerelle gérée par un fournisseur de cloud ou d'un équipement physique.
  • Réseau du cluster : un ensemble de liens, logiques ou physiques, qui permettent la communication au sein d'un cluster selon le modèle réseau de Kubernetes.
  • Service : un Service Kubernetes qui identifie un ensemble de Pods à l'aide de sélecteurs de labels. Sauf mention contraire, on suppose que les Services ont des adresses IP virtuelles routables uniquement dans le réseau du cluster.

Qu'est-ce qu'un Ingress ?

Un Ingress expose des routes HTTP et HTTPS depuis l'extérieur du cluster vers des services du cluster. Le routage du trafic est contrôlé par des règles définies sur la ressource Ingress.

Voici un exemple simple dans lequel un Ingress envoie tout son trafic vers un seul Service :

ingress-diagram

Figure. Ingress

Un Ingress peut être configuré pour donner aux Services des URL accessibles de l'extérieur, équilibrer la charge du trafic, assurer la terminaison SSL / TLS et proposer un hébergement virtuel basé sur le nom. Un contrôleur d'Ingress est chargé de mettre en œuvre l'Ingress, généralement avec un équilibreur de charge (load balancer), mais il peut aussi configurer votre routeur de bordure ou des frontaux supplémentaires pour aider à gérer le trafic.

Un Ingress n'expose pas de ports ni de protocoles arbitraires. Pour exposer à Internet des services autres que HTTP et HTTPS, on utilise généralement un Service de type Service.Type=NodePort ou Service.Type=LoadBalancer.

Prérequis

Vous devez disposer d'un contrôleur d'Ingress pour qu'un Ingress soit pris en compte. La simple création d'une ressource Ingress n'a aucun effet.

Vous avez le choix entre de nombreux contrôleurs d'Ingress.

Idéalement, tous les contrôleurs d'Ingress devraient respecter la spécification de référence. En pratique, les différents contrôleurs d'Ingress fonctionnent de manière légèrement différente.

Note:

Consultez bien la documentation de votre contrôleur d'Ingress pour connaître les limites à prendre en compte avant de le choisir.

La ressource Ingress

Exemple de ressource Ingress minimale :

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

Un Ingress a besoin des champs apiVersion, kind, metadata et spec. Le nom d'un objet Ingress doit être un nom de sous-domaine DNS valide. Pour des informations générales sur l'utilisation des fichiers de configuration, consultez déployer des applications, configurer des conteneurs et gérer des ressources. Les contrôleurs d'Ingress utilisent souvent des annotations pour configurer leur comportement. Consultez la documentation du contrôleur d'Ingress que vous avez choisi pour savoir quelles annotations sont attendues ou prises en charge.

La spec de l'Ingress contient toutes les informations nécessaires pour configurer un équilibreur de charge ou un serveur proxy. Elle contient surtout une liste de règles comparées à toutes les requêtes entrantes. La ressource Ingress ne prend en charge que des règles pour diriger du trafic HTTP(S).

Si ingressClassName est omis, une classe d'Ingress par défaut devrait être définie.

Certains contrôleurs d'Ingress fonctionnent même sans définition d'une IngressClass par défaut. Même si vous utilisez un contrôleur d'Ingress capable de fonctionner sans IngressClass, le projet Kubernetes recommande tout de même de définir une IngressClass par défaut.

Règles d'Ingress

Chaque règle HTTP contient les informations suivantes :

  • Un hôte facultatif. Dans cet exemple, aucun hôte n'est indiqué : la règle s'applique donc à tout le trafic HTTP entrant par l'adresse IP indiquée. Si un hôte est fourni (par exemple foo.bar.com), les règles s'appliquent à cet hôte.
  • Une liste de chemins (par exemple /testpath), chacun associé à un backend défini par un service.name et un service.port.name ou un service.port.number. L'hôte et le chemin doivent tous deux correspondre au contenu d'une requête entrante pour que l'équilibreur de charge dirige le trafic vers le Service référencé.
  • Un backend est une combinaison d'un nom de Service et d'un port, comme décrit dans la documentation des Services, ou un backend de ressource personnalisée défini au moyen d'une CRD. Les requêtes HTTP (et HTTPS) adressées à l'Ingress qui correspondent à l'hôte et au chemin de la règle sont envoyées au backend indiqué.

Un defaultBackend est souvent configuré dans un contrôleur d'Ingress pour traiter toutes les requêtes qui ne correspondent à aucun chemin de la spec.

DefaultBackend

Un Ingress sans règles envoie tout le trafic vers un unique backend par défaut, et .spec.defaultBackend est le backend qui doit traiter les requêtes dans ce cas. Le defaultBackend est habituellement une option de configuration du contrôleur d'Ingress et n'est pas indiqué dans vos ressources Ingress. Si .spec.rules n'est pas défini, .spec.defaultBackend doit l'être. Si defaultBackend n'est pas défini, c'est le contrôleur d'Ingress qui décide du traitement des requêtes qui ne correspondent à aucune règle (consultez la documentation de votre contrôleur d'Ingress pour savoir comment il gère ce cas).

Si aucun des hôtes ou chemins des objets Ingress ne correspond à la requête HTTP, le trafic est acheminé vers votre backend par défaut.

Backends de ressource

Un backend Resource est une référence (ObjectRef) vers une autre ressource Kubernetes du même namespace que l'objet Ingress. Resource et Service s'excluent mutuellement : la validation échoue si les deux sont indiqués. Un backend Resource sert couramment à faire entrer des données dans un backend de stockage objet contenant des ressources statiques.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-resource-backend
spec:
  defaultBackend:
    resource:
      apiGroup: k8s.example.com
      kind: StorageBucket
      name: static-assets
  rules:
    - http:
        paths:
          - path: /icons
            pathType: ImplementationSpecific
            backend:
              resource:
                apiGroup: k8s.example.com
                kind: StorageBucket
                name: icon-assets

Après avoir créé l'Ingress ci-dessus, vous pouvez l'afficher avec la commande suivante :

kubectl describe ingress ingress-resource-backend
Name:             ingress-resource-backend
Namespace:        default
Address:
Default backend:  APIGroup: k8s.example.com, Kind: StorageBucket, Name: static-assets
Rules:
  Host        Path  Backends
  ----        ----  --------
  *
              /icons   APIGroup: k8s.example.com, Kind: StorageBucket, Name: icon-assets
Annotations:  <none>
Events:       <none>

Types de chemins

Chaque chemin d'un Ingress doit avoir un type de chemin correspondant. Les chemins sans pathType explicite échouent à la validation. Trois types de chemins sont pris en charge :

  • ImplementationSpecific : avec ce type de chemin, la correspondance dépend de l'IngressClass. Les implémentations peuvent le traiter comme un pathType distinct ou de la même manière que les types de chemins Prefix ou Exact.

  • Exact : correspond exactement au chemin de l'URL, en tenant compte de la casse.

  • Prefix : correspond selon un préfixe du chemin de l'URL découpé par /. La correspondance tient compte de la casse et se fait élément par élément. Un élément de chemin désigne la liste des libellés du chemin découpé par le séparateur /. Une requête correspond au chemin p si chaque p est un préfixe, élément par élément, de p dans le chemin de la requête.

    Note:

    Si le dernier élément du chemin est une sous-chaîne du dernier élément du chemin de la requête, il n'y a pas de correspondance (par exemple, /foo/bar correspond à /foo/bar/baz, mais pas à /foo/barbaz).

Exemples

TypeChemin(s)Chemin(s) de la requêteCorrespondance ?
Prefix/(tous les chemins)Oui
Exact/foo/fooOui
Exact/foo/barNon
Exact/foo/foo/Non
Exact/foo//fooNon
Prefix/foo/foo, /foo/Oui
Prefix/foo//foo, /foo/Oui
Prefix/aaa/bb/aaa/bbbNon
Prefix/aaa/bbb/aaa/bbbOui
Prefix/aaa/bbb//aaa/bbbOui, ignore la barre oblique finale
Prefix/aaa/bbb/aaa/bbb/Oui, correspond avec la barre oblique finale
Prefix/aaa/bbb/aaa/bbb/cccOui, correspond au sous-chemin
Prefix/aaa/bbb/aaa/bbbxyzNon, ne correspond pas au préfixe de chaîne
Prefix/, /aaa/aaa/cccOui, correspond au préfixe /aaa
Prefix/, /aaa, /aaa/bbb/aaa/bbbOui, correspond au préfixe /aaa/bbb
Prefix/, /aaa, /aaa/bbb/cccOui, correspond au préfixe /
Prefix/aaa/cccNon, utilise le backend par défaut
Mixte/foo (Prefix), /foo (Exact)/fooOui, préfère Exact

Correspondances multiples

Dans certains cas, plusieurs chemins d'un Ingress correspondent à une requête. La priorité est alors donnée au chemin correspondant le plus long. Si deux chemins correspondent toujours à égalité, la priorité est donnée aux chemins de type exact plutôt qu'aux chemins de type préfixe.

Jokers dans les noms d'hôte

Les hôtes peuvent être des correspondances précises (par exemple « foo.bar.com ») ou un joker (par exemple « *.foo.com »). Une correspondance précise exige que l'en-tête HTTP host corresponde au champ host. Une correspondance avec joker exige que l'en-tête HTTP host soit égal au suffixe de la règle avec joker.

HôteEn-tête HostCorrespondance ?
*.foo.combar.foo.comCorrespond grâce au suffixe commun
*.foo.combaz.bar.foo.comPas de correspondance, le joker ne couvre qu'un seul libellé DNS
*.foo.comfoo.comPas de correspondance, le joker ne couvre qu'un seul libellé DNS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wildcard-host
spec:
  rules:
  - host: "foo.bar.com"
    http:
      paths:
      - pathType: Prefix
        path: "/bar"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: "*.foo.com"
    http:
      paths:
      - pathType: Prefix
        path: "/foo"
        backend:
          service:
            name: service2
            port:
              number: 80

Classe d'Ingress

Les Ingress peuvent être mis en œuvre par différents contrôleurs, souvent avec des configurations différentes. Chaque Ingress devrait indiquer une classe, c'est-à-dire une référence à une ressource IngressClass qui contient une configuration supplémentaire, dont le nom du contrôleur chargé de mettre en œuvre la classe.

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb
spec:
  controller: example.com/ingress-controller
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb

Le champ .spec.parameters d'une IngressClass vous permet de référencer une autre ressource qui fournit la configuration associée à cette IngressClass.

Le type précis de paramètres à utiliser dépend du contrôleur d'Ingress que vous indiquez dans le champ .spec.controller de l'IngressClass.

Portée d'une IngressClass

Selon votre contrôleur d'Ingress, vous pourrez peut-être utiliser des paramètres définis pour tout le cluster, ou pour un seul namespace.

Par défaut, les paramètres d'une IngressClass ont une portée à l'échelle du cluster.

Si vous définissez le champ .spec.parameters sans définir .spec.parameters.scope, ou si vous définissez .spec.parameters.scope sur Cluster, l'IngressClass fait référence à une ressource à portée cluster. Le kind (combiné à l'apiGroup) des paramètres fait référence à une API à portée cluster (éventuellement une ressource personnalisée), et le name des paramètres identifie une ressource précise à portée cluster de cette API.

Par exemple :

---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb-1
spec:
  controller: example.com/ingress-controller
  parameters:
    # The parameters for this IngressClass are specified in a
    # ClusterIngressParameter (API group k8s.example.net) named
    # "external-config-1". This definition tells Kubernetes to
    # look for a cluster-scoped parameter resource.
    scope: Cluster
    apiGroup: k8s.example.net
    kind: ClusterIngressParameter
    name: external-config-1
Feature state: Stable since Kubernetes v1.23

Si vous définissez le champ .spec.parameters et définissez .spec.parameters.scope sur Namespace, l'IngressClass fait référence à une ressource à portée namespace. Vous devez aussi définir le champ namespace de .spec.parameters avec le namespace qui contient les paramètres que vous voulez utiliser.

Le kind (combiné à l'apiGroup) des paramètres fait référence à une API à portée namespace (par exemple : ConfigMap), et le name des paramètres identifie une ressource précise dans le namespace indiqué dans namespace.

Les paramètres à portée namespace aident l'opérateur du cluster à déléguer le contrôle de la configuration (par exemple : réglages de l'équilibreur de charge, définition de la passerelle d'API) utilisée pour une charge de travail. Avec un paramètre à portée cluster, soit :

  • l'équipe qui exploite le cluster doit approuver les modifications d'une autre équipe chaque fois qu'un nouveau changement de configuration est appliqué ;
  • l'équipe qui exploite le cluster doit définir des contrôles d'accès spécifiques, comme des rôles et des liaisons RBAC, qui permettent à l'équipe applicative de modifier la ressource de paramètres à portée cluster.

L'API IngressClass elle-même a toujours une portée cluster.

Voici un exemple d'IngressClass qui fait référence à des paramètres à portée namespace :

---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb-2
spec:
  controller: example.com/ingress-controller
  parameters:
    # The parameters for this IngressClass are specified in an
    # IngressParameter (API group k8s.example.com) named "external-config",
    # that's in the "external-configuration" namespace.
    scope: Namespace
    apiGroup: k8s.example.com
    kind: IngressParameter
    namespace: external-configuration
    name: external-config

Annotation obsolète

Avant l'ajout de la ressource IngressClass et du champ ingressClassName dans Kubernetes 1.18, les classes d'Ingress étaient indiquées par une annotation kubernetes.io/ingress.class sur l'Ingress. Cette annotation n'a jamais été définie formellement, mais elle était largement prise en charge par les contrôleurs d'Ingress.

Le champ ingressClassName, plus récent, remplace cette annotation, sans en être un équivalent direct. Alors que l'annotation servait généralement à référencer le nom du contrôleur d'Ingress chargé de mettre en œuvre l'Ingress, le champ est une référence à une ressource IngressClass qui contient une configuration d'Ingress supplémentaire, dont le nom du contrôleur d'Ingress.

IngressClass par défaut

Vous pouvez marquer une IngressClass donnée comme classe par défaut de votre cluster. Définir l'annotation ingressclass.kubernetes.io/is-default-class sur true sur une ressource IngressClass garantit que cette IngressClass par défaut sera attribuée aux nouveaux Ingress qui n'indiquent pas de champ ingressClassName.

Avertissement:

Si plusieurs IngressClass sont marquées comme classe par défaut de votre cluster, le contrôleur d'admission empêche la création de nouveaux objets Ingress qui n'indiquent pas d'ingressClassName. Pour résoudre ce problème, assurez-vous qu'au plus une IngressClass est marquée comme classe par défaut dans votre cluster.

Commencez par définir une IngressClass par défaut. Il est toutefois recommandé d'indiquer l'IngressClass par défaut :

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
  name: example-class
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/example-class

Types d'Ingress

Ingress reposant sur un seul Service

Il existe déjà des concepts Kubernetes qui permettent d'exposer un seul Service (voir les alternatives). Vous pouvez aussi le faire avec un Ingress en indiquant un backend par défaut sans règles.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
spec:
  defaultBackend:
    service:
      name: test
      port:
        number: 80

Si vous le créez avec kubectl apply -f, vous devriez pouvoir afficher l'état de l'Ingress que vous avez ajouté :

kubectl get ingress test-ingress
NAME           CLASS         HOSTS   ADDRESS         PORTS   AGE
test-ingress   external-lb   *       203.0.113.123   80      59s

203.0.113.123 est l'adresse IP allouée par le contrôleur d'Ingress pour satisfaire cet Ingress.

Note:

Les contrôleurs d'Ingress et les équilibreurs de charge peuvent mettre une minute ou deux à allouer une adresse IP. En attendant, l'adresse s'affiche souvent sous la forme <pending>.

Fanout simple

Une configuration en fanout achemine le trafic d'une seule adresse IP vers plusieurs Services, en fonction de l'URI HTTP demandée. Un Ingress vous permet de réduire au minimum le nombre d'équilibreurs de charge. Prenons par exemple la configuration suivante :

ingress-fanout-diagram

Figure. Fanout d'Ingress

Elle nécessiterait un Ingress comme celui-ci :

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-fanout-example
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /foo
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 4200
      - path: /bar
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 8080

Une fois l'Ingress créé avec kubectl apply -f :

kubectl describe ingress simple-fanout-example
Name:             simple-fanout-example
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:4200 (10.8.0.90:4200)
               /bar   service2:8080 (10.8.0.91:8080)
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     22s                loadbalancer-controller  default/test

Le contrôleur d'Ingress provisionne un équilibreur de charge propre à son implémentation qui satisfait l'Ingress, à condition que les Services (service1, service2) existent. Une fois que c'est fait, vous pouvez voir l'adresse de l'équilibreur de charge dans le champ Address.

Note:

Selon le contrôleur d'Ingress que vous utilisez, vous devrez peut-être créer un Service default-http-backend.

Hébergement virtuel basé sur le nom

Les hôtes virtuels basés sur le nom permettent d'acheminer le trafic HTTP vers plusieurs noms d'hôte partageant la même adresse IP.

ingress-namebase-diagram

Figure. Hébergement virtuel basé sur le nom

L'Ingress suivant indique à l'équilibreur de charge sous-jacent d'acheminer les requêtes en fonction de l'en-tête Host.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: bar.foo.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80

Si vous créez une ressource Ingress sans définir d'hôte dans les règles, tout trafic web adressé à l'adresse IP de votre contrôleur d'Ingress peut correspondre sans qu'un hôte virtuel basé sur le nom soit nécessaire.

Par exemple, l'Ingress suivant achemine le trafic demandé pour first.bar.com vers service1, celui pour second.bar.com vers service2, et tout trafic dont l'en-tête Host de la requête ne correspond ni à first.bar.com ni à second.bar.com vers service3.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress-no-third-host
spec:
  rules:
  - host: first.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: second.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80
  - http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service3
            port:
              number: 80

TLS

Vous pouvez sécuriser un Ingress en indiquant un Secret qui contient une clé privée et un certificat TLS. La ressource Ingress ne prend en charge qu'un seul port TLS, le 443, et suppose que la terminaison TLS se fait au point d'entrée (le trafic vers le Service et ses Pods circule en clair). Si la section de configuration TLS d'un Ingress indique plusieurs hôtes, ils sont multiplexés sur le même port selon le nom d'hôte indiqué via l'extension TLS SNI (à condition que le contrôleur d'Ingress prenne en charge SNI). Le Secret TLS doit contenir des clés nommées tls.crt et tls.key, qui contiennent le certificat et la clé privée à utiliser pour TLS. Par exemple :

apiVersion: v1
kind: Secret
metadata:
  name: testsecret-tls
  namespace: default
data:
  tls.crt: base64 encoded cert
  tls.key: base64 encoded key
type: kubernetes.io/tls

Référencer ce Secret dans un Ingress indique au contrôleur d'Ingress de sécuriser avec TLS le canal entre le client et l'équilibreur de charge. Vous devez vous assurer que le Secret TLS que vous avez créé provient d'un certificat contenant un Common Name (CN), aussi appelé nom de domaine complet (FQDN), pour https-example.foo.com.

Note:

Gardez à l'esprit que TLS ne fonctionnera pas sur la règle par défaut, car les certificats devraient être émis pour tous les sous-domaines possibles. C'est pourquoi les hosts de la section tls doivent correspondre explicitement au host de la section rules.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-example-ingress
spec:
  tls:
  - hosts:
      - https-example.foo.com
    secretName: testsecret-tls
  rules:
  - host: https-example.foo.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80

Note:

Les fonctionnalités TLS prises en charge varient d'un contrôleur d'Ingress à l'autre. Consultez la documentation du ou des contrôleurs d'Ingress que vous avez choisis pour comprendre le fonctionnement de TLS dans votre environnement.

Équilibrage de charge

Un contrôleur d'Ingress démarre avec des réglages de politique d'équilibrage de charge qu'il applique à tous les Ingress, comme l'algorithme d'équilibrage de charge, le schéma de pondération des backends, etc. Les concepts d'équilibrage de charge plus avancés (par exemple les sessions persistantes ou les pondérations dynamiques) ne sont pas encore exposés via l'Ingress. Vous pouvez en revanche obtenir ces fonctionnalités grâce à l'équilibreur de charge utilisé pour un Service.

Notez aussi que, même si les contrôles de santé (health checks) ne sont pas exposés directement via l'Ingress, il existe dans Kubernetes des concepts parallèles, comme les sondes de disponibilité (readiness probes), qui permettent d'obtenir le même résultat. Consultez la documentation propre à votre contrôleur pour savoir comment il gère les contrôles de santé.

Mettre à jour un Ingress

Pour mettre à jour un Ingress existant afin d'ajouter un nouvel hôte, vous pouvez modifier la ressource :

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:80 (10.8.0.90:80)
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     35s                loadbalancer-controller  default/test
kubectl edit ingress test

Un éditeur s'ouvre avec la configuration existante au format YAML. Modifiez-la pour ajouter le nouvel hôte :

spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - backend:
          service:
            name: service1
            port:
              number: 80
        path: /foo
        pathType: Prefix
  - host: bar.baz.com
    http:
      paths:
      - backend:
          service:
            name: service2
            port:
              number: 80
        path: /foo
        pathType: Prefix
..

Une fois vos modifications enregistrées, kubectl met à jour la ressource dans le serveur d'API, ce qui indique au contrôleur d'Ingress de reconfigurer l'équilibreur de charge.

Vérifiez-le :

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:80 (10.8.0.90:80)
  bar.baz.com
               /foo   service2:80 (10.8.0.91:80)
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     45s                loadbalancer-controller  default/test

Vous pouvez obtenir le même résultat en exécutant kubectl replace -f sur un fichier YAML d'Ingress modifié.

Basculement entre zones de disponibilité

Les techniques de répartition du trafic entre domaines de défaillance diffèrent d'un fournisseur de cloud à l'autre. Consultez la documentation du contrôleur d'Ingress concerné pour plus de détails.

Alternatives

Vous pouvez exposer un Service de plusieurs manières qui n'impliquent pas directement la ressource Ingress :

A suivre

3 - Contrôleurs d'Ingress

Pour qu'un Ingress fonctionne dans votre cluster, un contrôleur d'Ingress doit être en cours d'exécution. Vous devez choisir au moins un contrôleur d'Ingress et vous assurer qu'il est mis en place dans votre cluster. Cette page liste des contrôleurs d'Ingress courants que vous pouvez déployer.

Note:

Le projet Kubernetes recommande d'utiliser Gateway plutôt qu'Ingress. L'API Ingress est figée.

Cela signifie que :

  • L'API Ingress est en disponibilité générale (GA) et soumise aux garanties de stabilité prévues pour les API GA. Le projet Kubernetes n'a pas l'intention de retirer Ingress de Kubernetes.
  • L'API Ingress n'est plus développée et ne recevra plus aucune modification ni mise à jour.

Contrôleurs d'Ingress

Le projet Kubernetes prend en charge et maintient les contrôleurs d'Ingress AWS et GCE.

Contrôleurs d'Ingress tiers

Note: Cette section renvoie à des projets tiers qui fournissent des fonctionnalités requises par Kubernetes. Les auteurs du projet Kubernetes ne sont pas responsables de ces projets, classés par ordre alphabétique. Pour ajouter un projet à cette liste, lisez le guide avant de soumettre une modification. Plus d'informations.

Utiliser plusieurs contrôleurs d'Ingress

Vous pouvez déployer autant de contrôleurs d'Ingress que vous le souhaitez dans un cluster, grâce aux classes d'Ingress. Notez le .metadata.name de votre ressource de classe d'Ingress. Lorsque vous créez un Ingress, vous avez besoin de ce nom pour renseigner le champ ingressClassName de votre objet Ingress (voir la référence IngressSpec v1). ingressClassName remplace l'ancienne méthode par annotation.

Si vous ne précisez pas d'IngressClass pour un Ingress et que votre cluster a exactement une IngressClass marquée comme classe par défaut, Kubernetes applique cette IngressClass par défaut du cluster à l'Ingress. Pour marquer une IngressClass comme classe par défaut, définissez l'annotation ingressclass.kubernetes.io/is-default-class sur cette IngressClass, avec la chaîne "true".

Idéalement, tous les contrôleurs d'Ingress devraient respecter cette spécification, mais les différents contrôleurs d'Ingress fonctionnent de manière légèrement différente.

Note:

Consultez bien la documentation de votre contrôleur d'Ingress pour connaître les points d'attention liés à ce choix.

A suivre

4 - Gateway API

Gateway API est une famille de types d'API qui fournissent un provisionnement dynamique de l'infrastructure et un routage avancé du trafic.

Rendez des services réseau accessibles grâce à un mécanisme de configuration extensible, orienté rôles et conscient des protocoles. Gateway API est un add-on qui contient des types d'API fournissant un provisionnement dynamique de l'infrastructure et un routage avancé du trafic.

Principes de conception

Les principes suivants ont guidé la conception et l'architecture de Gateway API :

  • Orientée rôles : les types de Gateway API sont modélisés d'après les rôles organisationnels chargés de gérer le réseau des services Kubernetes :
    • Fournisseur d'infrastructure : gère une infrastructure qui permet à plusieurs clusters isolés de servir plusieurs locataires, par exemple un fournisseur de cloud.
    • Opérateur de cluster : gère des clusters et s'occupe généralement des politiques, de l'accès réseau, des permissions des applications, etc.
    • Développeur d'applications : gère une application qui s'exécute dans un cluster et s'occupe généralement de la configuration au niveau de l'application et de la composition des Services.
  • Portable : les spécifications de Gateway API sont définies sous forme de ressources personnalisées et sont prises en charge par de nombreuses implémentations.
  • Expressive : les types de Gateway API prennent en charge des fonctionnalités couvrant les cas d'usage courants de routage du trafic, comme la correspondance sur les en-têtes ou la pondération du trafic, entre autres, qui n'étaient possibles avec Ingress qu'au moyen d'annotations personnalisées.
  • Extensible : Gateway permet de lier des ressources personnalisées à différents niveaux de l'API. Cela rend possible une personnalisation fine aux endroits appropriés de la structure de l'API.

Modèle de ressources

Gateway API comporte quatre types d'API stables :

  • GatewayClass : définit un ensemble de gateways partageant une configuration commune et gérées par un contrôleur qui implémente la classe.

  • Gateway : définit une instance d'infrastructure de traitement du trafic, comme un équilibreur de charge (load balancer) cloud.

  • HTTPRoute : définit des règles propres à HTTP pour acheminer le trafic d'un listener de Gateway vers une représentation de points de terminaison réseau de backend. Ces points de terminaison sont souvent représentés par un Service.

  • GRPCRoute : définit des règles propres à gRPC pour acheminer le trafic d'un listener de Gateway vers une représentation de points de terminaison réseau de backend. Ces points de terminaison sont souvent représentés par un Service.

Gateway API est organisée en différents types d'API liés par des relations d'interdépendance, afin de refléter l'organisation par rôles des entreprises. Un objet Gateway est associé à exactement une GatewayClass ; la GatewayClass décrit le contrôleur de gateway chargé de gérer les Gateways de cette classe. Un ou plusieurs types de routes, comme HTTPRoute, sont ensuite associés aux Gateways. Une Gateway peut filtrer les routes autorisées à se rattacher à ses listeners, ce qui forme un modèle de confiance bidirectionnel avec les routes.

La figure suivante illustre les relations entre les trois types stables de Gateway API :

Figure illustrant les relations entre les trois types stables de Gateway API

GatewayClass

Les Gateways peuvent être implémentées par différents contrôleurs, souvent avec des configurations différentes. Une Gateway doit référencer une GatewayClass qui contient le nom du contrôleur qui implémente la classe.

Exemple minimal de GatewayClass :

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: example-class
spec:
  controllerName: example.com/gateway-controller

Dans cet exemple, un contrôleur qui implémente Gateway API est configuré pour gérer les GatewayClasses dont le nom de contrôleur est example.com/gateway-controller. Les Gateways de cette classe seront gérées par le contrôleur de l'implémentation.

Consultez la référence de GatewayClass pour la définition complète de ce type d'API.

Gateway

Une Gateway décrit une instance d'infrastructure de traitement du trafic. Elle définit un point de terminaison réseau qui peut servir à traiter le trafic, c'est-à-dire à le filtrer, à en équilibrer la charge, à le fractionner, etc., vers des backends comme un Service. Par exemple, une Gateway peut représenter un équilibreur de charge cloud ou un serveur proxy interne au cluster, configuré pour accepter du trafic HTTP.

Exemple typique de ressource Gateway :

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
  namespace: example-namespace
spec:
  gatewayClassName: example-class
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    hostname: "www.example.com"
    allowedRoutes:
      namespaces:
        from: Same

Dans cet exemple, une instance d'infrastructure de traitement du trafic est programmée pour écouter le trafic HTTP sur le port 80. Comme le champ addresses n'est pas renseigné, une adresse ou un nom d'hôte est attribué à la Gateway par le contrôleur de l'implémentation. Cette adresse sert de point de terminaison réseau pour traiter le trafic destiné aux points de terminaison réseau de backend définis dans les routes.

Consultez la référence de Gateway pour la définition complète de ce type d'API. Pour configurer des listeners HTTPS/TLS, consultez le guide TLS de Gateway API.

Note:

Par défaut, une Gateway n'accepte que les routes du même namespace. Les routes situées dans d'autres namespaces nécessitent de configurer allowedRoutes.

HTTPRoute

Le type HTTPRoute définit le comportement de routage des requêtes HTTP, d'un listener de Gateway vers des points de terminaison réseau de backend. Pour un backend de type Service, une implémentation peut représenter le point de terminaison réseau du backend par l'IP du Service ou par les EndpointSlices associées au Service. Une HTTPRoute représente une configuration appliquée à l'implémentation de Gateway sous-jacente. Par exemple, définir une nouvelle HTTPRoute peut conduire à configurer des routes de trafic supplémentaires dans un équilibreur de charge cloud ou dans un serveur proxy du cluster.

Exemple typique d'HTTPRoute :

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example-httproute
spec:
  parentRefs:
  - name: example-gateway
  hostnames:
  - "www.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /login
    backendRefs:
    - name: example-svc
      port: 8080

Dans cet exemple, le trafic HTTP provenant de la Gateway example-gateway, dont l'en-tête Host: vaut www.example.com et dont le chemin de la requête est /login, sera acheminé vers le Service example-svc sur le port 8080.

Consultez la référence de HTTPRoute pour la définition complète de ce type d'API.

GRPCRoute

Le type GRPCRoute définit le comportement de routage des requêtes gRPC, d'un listener de Gateway vers des points de terminaison réseau de backend. Pour un backend de type Service, une implémentation peut représenter le point de terminaison réseau du backend par l'IP du Service ou par les EndpointSlices associées au Service. Une GRPCRoute représente une configuration appliquée à l'implémentation de Gateway sous-jacente. Par exemple, définir une nouvelle GRPCRoute peut conduire à configurer des routes de trafic supplémentaires dans un équilibreur de charge cloud ou dans un serveur proxy du cluster.

Les Gateways qui prennent en charge GRPCRoute doivent prendre en charge HTTP/2 sans mise à niveau initiale depuis HTTP/1, afin de garantir que le trafic gRPC circule correctement.

Exemple typique de GRPCRoute :

apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
  name: example-grpcroute
spec:
  parentRefs:
  - name: example-gateway
  hostnames:
  - "svc.example.com"
  rules:
  - backendRefs:
    - name: example-svc
      port: 50051

Dans cet exemple, le trafic gRPC provenant de la Gateway example-gateway, dont l'hôte est svc.example.com, sera dirigé vers le Service example-svc sur le port 50051, dans le même namespace.

GRPCRoute permet de cibler des services gRPC précis, comme dans l'exemple suivant :

apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
  name: example-grpcroute
spec:
  parentRefs:
  - name: example-gateway
  hostnames:
  - "svc.example.com"
  rules:
  - matches:
    - method:
        service: com.example
        method: Login
    backendRefs:
    - name: foo-svc
      port: 50051

Dans ce cas, la GRPCRoute correspond à tout le trafic destiné à svc.example.com et applique ses règles de routage pour transmettre le trafic au bon backend. Comme une seule correspondance est définie, seules les requêtes vers la méthode com.example.User.Login sur svc.example.com seront transmises. Les RPC vers toute autre méthode ne correspondront pas à cette route.

Consultez la référence de GRPCRoute pour la définition complète de ce type d'API.

Flux des requêtes

Voici un exemple simple de trafic HTTP acheminé vers un Service à l'aide d'une Gateway et d'une HTTPRoute :

Schéma montrant un exemple de trafic HTTP acheminé vers un Service à l'aide d'une Gateway et d'une HTTPRoute

Dans cet exemple, le flux d'une requête pour une Gateway implémentée sous forme de proxy inverse est le suivant :

  1. Le client commence à préparer une requête HTTP pour l'URL http://www.example.com.
  2. Le résolveur DNS du client résout le nom de destination en une ou plusieurs adresses IP associées à la Gateway.
  3. Le client envoie une requête à l'adresse IP de la Gateway ; le proxy inverse reçoit la requête HTTP et utilise l'en-tête Host: pour trouver une configuration issue de la Gateway et de l'HTTPRoute rattachée.
  4. Le proxy inverse peut éventuellement vérifier la correspondance des en-têtes et/ou du chemin de la requête, selon les règles de correspondance de l'HTTPRoute.
  5. Le proxy inverse peut éventuellement modifier la requête, par exemple pour ajouter ou supprimer des en-têtes, selon les règles de filtrage de l'HTTPRoute.
  6. Enfin, le proxy inverse transmet la requête à un ou plusieurs backends.

Conformité

Gateway API couvre un large ensemble de fonctionnalités et elle est largement implémentée. Cette combinaison exige des définitions et des tests de conformité clairs pour garantir que l'API offre une expérience cohérente partout où elle est utilisée.

Consultez la documentation sur la conformité pour comprendre des notions comme les canaux de publication, les niveaux de prise en charge et l'exécution des tests de conformité.

Migrer depuis Ingress

Gateway API succède à l'API Ingress. Elle n'inclut cependant pas le type Ingress. Une conversion ponctuelle de vos ressources Ingress existantes en ressources Gateway API est donc nécessaire.

Consultez le guide de migration depuis Ingress pour savoir comment migrer des ressources Ingress vers des ressources Gateway API.

A suivre

Les ressources de Gateway API ne sont pas implémentées nativement par Kubernetes : leurs spécifications sont définies sous forme de ressources personnalisées prises en charge par un large éventail d'implémentations. Installez les CRD de Gateway API ou suivez les instructions d'installation de l'implémentation choisie. Une fois l'implémentation installée, utilisez le guide Getting Started pour prendre en main rapidement Gateway API.

Note:

Consultez la documentation de l'implémentation choisie pour bien en comprendre les éventuelles limites.

Consultez la spécification de l'API pour plus de détails sur tous les types de Gateway API.

5 - EndpointSlices

L'API EndpointSlice est le mécanisme que Kubernetes utilise pour permettre à votre Service de passer à l'échelle et de gérer un grand nombre de backends, et elle permet au cluster de mettre à jour efficacement sa liste de backends sains.
Feature state: Stable since Kubernetes v1.21
Les EndpointSlices suivent les adresses IP des points de terminaison backend.
Les EndpointSlices sont généralement associés à un
Service et les points de terminaison backend représentent typiquement des
Pods.

API EndpointSlice

Dans Kubernetes, un EndpointSlice contient des références à un ensemble de points de terminaison réseau. Le plan de contrôle crée automatiquement des EndpointSlices pour tout Service Kubernetes pour lequel un sélecteur est spécifié. Ces EndpointSlices contiennent des références à tous les Pods qui correspondent au sélecteur du Service. Les EndpointSlices regroupent les points de terminaison réseau par combinaison unique de famille d'adresses IP, de protocole, de numéro de port et de nom de Service. Le nom d'un objet EndpointSlice doit être un nom de sous-domaine DNS valide.

Voici par exemple un objet EndpointSlice dont le propriétaire est le Service Kubernetes example.

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: example-abc
  labels:
    kubernetes.io/service-name: example
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
    hostname: pod-1
    nodeName: node-1
    zone: us-west2-a

Par défaut, le plan de contrôle crée et gère des EndpointSlices qui ne contiennent pas plus de 100 points de terminaison chacun. Vous pouvez modifier cette valeur avec l'option --max-endpoints-per-slice de kube-controller-manager, jusqu'à un maximum de 1000.

Les EndpointSlices constituent la source de vérité de kube-proxy pour déterminer comment acheminer le trafic interne.

Types d'adresses

Les EndpointSlices prennent en charge deux types d'adresses :

  • IPv4
  • IPv6

Chaque objet EndpointSlice correspond à un type d'adresse IP précis. Si vous avez un Service accessible en IPv4 et en IPv6, il existera au moins deux objets EndpointSlice (un pour IPv4 et un pour IPv6).

Conditions

L'API EndpointSlice enregistre, pour les points de terminaison, des conditions qui peuvent être utiles à ses consommateurs. Les trois conditions sont serving, terminating et ready.

Serving

Feature state: Stable since Kubernetes v1.26

La condition serving indique que le point de terminaison répond actuellement aux requêtes, et qu'il devrait donc être utilisé comme cible pour le trafic du Service. Pour les points de terminaison adossés à un Pod, elle correspond à la condition Ready du Pod.

Terminating

Feature state: Stable since Kubernetes v1.26

La condition terminating indique que le point de terminaison est en cours d'arrêt. Pour les points de terminaison adossés à un Pod, cette condition est définie dès que la suppression du Pod est demandée (c'est-à-dire lorsqu'il reçoit un horodatage de suppression, mais très probablement avant que les conteneurs du Pod ne s'arrêtent).

Les proxys de Service ignorent normalement les points de terminaison marqués terminating, mais ils peuvent acheminer le trafic vers des points de terminaison à la fois serving et terminating si tous les points de terminaison disponibles sont marqués terminating. (Cela contribue à garantir qu'aucun trafic du Service n'est perdu pendant les mises à jour progressives des Pods sous-jacents.)

Ready

La condition ready est essentiellement un raccourci pour vérifier « serving et non terminating » (elle vaut toutefois toujours true pour les Services dont spec.publishNotReadyAddresses est défini sur true).

Informations de topologie

Chaque point de terminaison d'un EndpointSlice peut contenir des informations de topologie pertinentes. Ces informations comprennent l'emplacement du point de terminaison ainsi que des informations sur le nœud et la zone correspondants. Elles sont disponibles dans les champs suivants, propres à chaque point de terminaison d'un EndpointSlice :

  • nodeName : le nom du nœud sur lequel se trouve ce point de terminaison.
  • zone : la zone dans laquelle se trouve ce point de terminaison.

Gestion

Le plus souvent, c'est le plan de contrôle (plus précisément, le contrôleur d'EndpointSlices) qui crée et gère les objets EndpointSlice. Les EndpointSlices ont de nombreux autres cas d'usage, comme les implémentations de service mesh, qui peuvent amener d'autres entités ou contrôleurs à gérer des ensembles supplémentaires d'EndpointSlices.

Pour que plusieurs entités puissent gérer des EndpointSlices sans se gêner mutuellement, Kubernetes définit le label endpointslice.kubernetes.io/managed-by, qui indique l'entité qui gère un EndpointSlice. Le contrôleur d'EndpointSlices attribue la valeur endpointslice-controller.k8s.io à ce label sur tous les EndpointSlices qu'il gère. Les autres entités qui gèrent des EndpointSlices doivent elles aussi attribuer une valeur unique à ce label.

Propriété

Dans la plupart des cas d'usage, le propriétaire d'un EndpointSlice est le Service dont l'objet EndpointSlice suit les points de terminaison. Cette propriété est indiquée par une référence de propriétaire (owner reference) sur chaque EndpointSlice, ainsi que par un label kubernetes.io/service-name qui permet de retrouver simplement tous les EndpointSlices d'un Service.

Répartition des EndpointSlices

Chaque EndpointSlice possède un ensemble de ports qui s'applique à tous les points de terminaison de la ressource. Lorsqu'un Service utilise des ports nommés, les Pods peuvent se retrouver avec des numéros de port cible différents pour un même port nommé, ce qui nécessite des EndpointSlices distincts.

Le plan de contrôle essaie de remplir les EndpointSlices autant que possible, mais ne les rééquilibre pas activement. La logique est assez simple :

  1. Parcourir les EndpointSlices existants, retirer les points de terminaison qui ne sont plus souhaités et mettre à jour les points de terminaison correspondants qui ont changé.
  2. Parcourir les EndpointSlices modifiés lors de la première étape et les compléter avec les nouveaux points de terminaison nécessaires.
  3. S'il reste encore de nouveaux points de terminaison à ajouter, essayer de les placer dans un EndpointSlice resté inchangé et/ou en créer de nouveaux.

Point important : la troisième étape privilégie la limitation des mises à jour d'EndpointSlices plutôt qu'un remplissage optimal des EndpointSlices. Par exemple, s'il y a 10 nouveaux points de terminaison à ajouter et 2 EndpointSlices pouvant chacun en accueillir 5 de plus, cette approche créera un nouvel EndpointSlice au lieu de remplir les 2 EndpointSlices existants. Autrement dit, une seule création d'EndpointSlice est préférable à plusieurs mises à jour d'EndpointSlices.

Comme kube-proxy s'exécute sur chaque nœud et surveille les EndpointSlices, chaque modification d'un EndpointSlice devient relativement coûteuse, puisqu'elle est transmise à chaque nœud du cluster. Cette approche vise à limiter le nombre de modifications à envoyer à chaque nœud, même si elle peut aboutir à plusieurs EndpointSlices partiellement remplis.

En pratique, cette répartition moins qu'idéale devrait être rare. La plupart des modifications traitées par le contrôleur d'EndpointSlices sont suffisamment petites pour tenir dans un EndpointSlice existant ; dans le cas contraire, un nouvel EndpointSlice sera de toute façon probablement nécessaire bientôt. Les mises à jour progressives des Deployments entraînent aussi un réagencement naturel des EndpointSlices, puisque tous les Pods et leurs points de terminaison correspondants sont remplacés.

Points de terminaison en double

En raison de la nature des modifications d'EndpointSlices, un point de terminaison peut figurer dans plusieurs EndpointSlices en même temps. Cela se produit naturellement, car les modifications de différents objets EndpointSlice peuvent parvenir au watch / cache du client Kubernetes à des moments différents.

Note:

Les clients de l'API EndpointSlice doivent parcourir tous les EndpointSlices existants associés à un Service et construire une liste complète de points de terminaison réseau uniques. Il est important de noter que des points de terminaison peuvent être dupliqués dans différents EndpointSlices.

Vous trouverez une implémentation de référence de cette agrégation et de cette déduplication des points de terminaison dans le code EndpointSliceCache de kube-proxy.

Mise en miroir des EndpointSlices

Feature state: Deprecated since Kubernetes v1.33

L'API EndpointSlice remplace l'ancienne API Endpoints. Pour préserver la compatibilité avec les anciens contrôleurs et les charges de travail des utilisateurs qui s'attendent à ce que kube-proxy achemine le trafic à partir des ressources Endpoints, le plan de contrôle du cluster reproduit (en miroir) la plupart des ressources Endpoints créées par les utilisateurs dans des EndpointSlices correspondants.

(Cette fonctionnalité est toutefois dépréciée, comme le reste de l'API Endpoints. Les utilisateurs qui définissent manuellement des points de terminaison pour des Services sans sélecteur doivent le faire en créant directement des ressources EndpointSlice, plutôt qu'en créant des ressources Endpoints et en laissant le plan de contrôle les reproduire en miroir.)

Le plan de contrôle reproduit les ressources Endpoints en miroir, sauf si :

  • la ressource Endpoints a un label endpointslice.kubernetes.io/skip-mirror défini sur true ;
  • la ressource Endpoints a une annotation control-plane.alpha.kubernetes.io/leader ;
  • la ressource Service correspondante n'existe pas ;
  • la ressource Service correspondante a un sélecteur non nul.

Une même ressource Endpoints peut donner lieu à plusieurs EndpointSlices. C'est le cas si une ressource Endpoints comporte plusieurs sous-ensembles (subsets) ou contient des points de terminaison de plusieurs familles d'adresses IP (IPv4 et IPv6). Au maximum 1000 adresses par sous-ensemble sont reproduites dans les EndpointSlices.

A suivre

6 - Politiques réseau

Si vous voulez contrôler les flux de trafic au niveau des adresses IP ou des ports (couches 3 ou 4 du modèle OSI), les NetworkPolicies vous permettent de définir des règles pour les flux de trafic au sein de votre cluster, ainsi qu'entre les Pods et le monde extérieur. Votre cluster doit utiliser un plugin réseau qui sait faire respecter les NetworkPolicies.

Si vous voulez contrôler les flux de trafic au niveau des adresses IP ou des ports pour les protocoles TCP, UDP et SCTP, vous pouvez envisager d'utiliser les NetworkPolicies de Kubernetes pour certaines applications de votre cluster. Les NetworkPolicies sont un mécanisme centré sur l'application, qui vous permet de définir comment un pod est autorisé à communiquer sur le réseau avec différentes « entités » (nous employons ici le mot « entité » pour ne pas donner un sens de plus à des termes courants comme « endpoints » et « services », qui ont un sens précis dans Kubernetes). Les NetworkPolicies s'appliquent à une connexion dont l'une des extrémités au moins est un pod, et ne concernent pas les autres connexions.

Les entités avec lesquelles un Pod peut communiquer sont identifiées par une combinaison des trois identifiants suivants :

  1. Les autres pods autorisés (exception : un pod ne peut pas bloquer l'accès à lui-même)
  2. Les namespaces autorisés
  3. Les blocs d'adresses IP (exception : le trafic depuis et vers le nœud sur lequel un Pod s'exécute est toujours autorisé, quelle que soit l'adresse IP du Pod ou du nœud)

Pour définir une NetworkPolicy basée sur les pods ou les namespaces, vous utilisez un sélecteur pour indiquer quel trafic est autorisé depuis et vers le ou les Pods qui correspondent à ce sélecteur.

Pour les NetworkPolicies basées sur les adresses IP, en revanche, on définit des politiques à partir de blocs d'adresses IP (plages CIDR).

Prérequis

Les politiques réseau sont mises en œuvre par le plugin réseau. Pour utiliser les politiques réseau, il vous faut une solution réseau qui prend en charge les NetworkPolicies. Créer une ressource NetworkPolicy sans contrôleur pour la mettre en œuvre n'aura aucun effet.

Les deux types d'isolation d'un pod

Un pod peut être isolé de deux façons : en sortie (egress) et en entrée (ingress). Ces isolations portent sur les connexions qui peuvent être établies. Ici, « isolé » n'a rien d'absolu : cela signifie que « certaines restrictions s'appliquent ». À l'inverse, « non isolé dans une direction » signifie qu'aucune restriction ne s'applique dans cette direction. Les deux types d'isolation (ou leur absence) sont déclarés indépendamment l'un de l'autre, et entrent tous deux en jeu pour une connexion d'un pod vers un autre.

Par défaut, un pod n'est pas isolé en sortie : toutes les connexions sortantes sont autorisées. Un pod est isolé en sortie s'il existe au moins une NetworkPolicy qui sélectionne ce pod et dont les policyTypes contiennent « Egress » ; on dit alors que cette politique s'applique au pod en sortie. Quand un pod est isolé en sortie, les seules connexions autorisées depuis ce pod sont celles qu'autorise la liste egress d'au moins une NetworkPolicy qui s'applique au pod en sortie. Le trafic de réponse de ces connexions autorisées est lui aussi autorisé implicitement. Les effets de ces listes egress s'additionnent.

Par défaut, un pod n'est pas isolé en entrée : toutes les connexions entrantes sont autorisées. Un pod est isolé en entrée s'il existe au moins une NetworkPolicy qui sélectionne ce pod et dont les policyTypes contiennent « Ingress » ; on dit alors que cette politique s'applique au pod en entrée. Quand un pod est isolé en entrée, les seules connexions autorisées vers ce pod sont celles qui viennent du nœud du pod et celles qu'autorise la liste ingress d'au moins une NetworkPolicy qui s'applique au pod en entrée. Le trafic de réponse de ces connexions autorisées est lui aussi autorisé implicitement. Les effets de ces listes ingress s'additionnent.

Les politiques réseau n'entrent pas en conflit : elles s'additionnent. Si une ou plusieurs politiques s'appliquent à un pod donné dans une direction donnée, les connexions autorisées dans cette direction pour ce pod sont l'union de ce qu'autorisent ces politiques. L'ordre d'évaluation n'a donc aucune influence sur le résultat.

Pour qu'une connexion d'un pod source vers un pod de destination soit autorisée, il faut que la politique en sortie du pod source et la politique en entrée du pod de destination l'autorisent toutes les deux. Si l'un des deux côtés ne l'autorise pas, la connexion n'a pas lieu.

La ressource NetworkPolicy

Consultez la référence NetworkPolicy pour la définition complète de la ressource.

Voici un exemple de NetworkPolicy :

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

Note:

Envoyer cette ressource (POST) au serveur d'API de votre cluster n'aura aucun effet si la solution réseau que vous avez choisie ne prend pas en charge les politiques réseau.

Champs obligatoires : comme toute autre configuration Kubernetes, une NetworkPolicy a besoin des champs apiVersion, kind et metadata. Pour des informations générales sur l'utilisation des fichiers de configuration, consultez Configurer un Pod pour utiliser une ConfigMap et Gestion des objets.

spec : la spec d'une NetworkPolicy contient toutes les informations nécessaires pour définir une politique réseau donnée dans le namespace concerné.

podSelector : chaque NetworkPolicy contient un podSelector qui sélectionne le groupe de pods auquel la politique s'applique. La politique de l'exemple sélectionne les pods qui ont le label « role=db ». Un podSelector vide sélectionne tous les pods du namespace.

policyTypes : chaque NetworkPolicy contient une liste policyTypes qui peut contenir Ingress, Egress ou les deux. Le champ policyTypes indique si la politique s'applique au trafic entrant vers les pods sélectionnés, au trafic sortant des pods sélectionnés, ou aux deux. Si aucun policyTypes n'est indiqué dans une NetworkPolicy, Ingress est toujours défini par défaut, et Egress l'est aussi si la NetworkPolicy contient des règles egress.

ingress : chaque NetworkPolicy peut contenir une liste de règles ingress autorisées. Chaque règle autorise le trafic qui correspond à la fois aux sections from et ports. La politique de l'exemple contient une seule règle, qui correspond au trafic sur un seul port, depuis l'une de trois sources : la première définie par un ipBlock, la deuxième par un namespaceSelector et la troisième par un podSelector.

egress : chaque NetworkPolicy peut contenir une liste de règles egress autorisées. Chaque règle autorise le trafic qui correspond à la fois aux sections to et ports. La politique de l'exemple contient une seule règle, qui correspond au trafic sur un seul port vers n'importe quelle destination dans 10.0.0.0/24.

La NetworkPolicy de l'exemple a donc les effets suivants :

  1. elle isole les pods role=db du namespace default pour le trafic entrant et sortant (s'ils n'étaient pas déjà isolés)

  2. (règles Ingress) elle autorise les connexions vers tous les pods du namespace default qui ont le label role=db, sur le port TCP 6379, depuis :

    • n'importe quel pod du namespace default qui a le label role=frontend
    • n'importe quel pod d'un namespace qui a le label project=myproject
    • les adresses IP des plages 172.17.0.0–172.17.0.255 et 172.17.2.0–172.17.255.255 (c'est-à-dire tout 172.17.0.0/16 sauf 172.17.1.0/24)
  3. (règles Egress) elle autorise les connexions depuis n'importe quel pod du namespace default qui a le label role=db vers le CIDR 10.0.0.0/24, sur le port TCP 5978

Consultez le tutoriel Déclarer une politique réseau pour d'autres exemples.

Comportement des sélecteurs to et from

Quatre types de sélecteurs peuvent être indiqués dans une section from d'ingress ou dans une section to d'egress :

podSelector : sélectionne certains Pods du même namespace que la NetworkPolicy, qui doivent être autorisés comme sources en entrée ou comme destinations en sortie.

namespaceSelector : sélectionne certains namespaces dont tous les Pods doivent être autorisés comme sources en entrée ou comme destinations en sortie.

namespaceSelector et podSelector : une même entrée to/from qui indique à la fois namespaceSelector et podSelector sélectionne certains Pods dans certains namespaces. Veillez à utiliser une syntaxe YAML correcte. Par exemple :

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
      podSelector:
        matchLabels:
          role: client
  ...

Cette politique contient un seul élément from, qui autorise les connexions depuis les Pods qui ont le label role=client dans les namespaces qui ont le label user=alice. La politique suivante, elle, est différente :

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
    - podSelector:
        matchLabels:
          role: client
  ...

Elle contient deux éléments dans le tableau from, et autorise les connexions depuis les Pods du namespace local qui ont le label role=client, ou depuis n'importe quel Pod de n'importe quel namespace qui a le label user=alice.

En cas de doute, utilisez kubectl describe pour voir comment Kubernetes a interprété la politique.

ipBlock : sélectionne certaines plages CIDR d'adresses IP à autoriser comme sources en entrée ou comme destinations en sortie. Il devrait s'agir d'adresses IP externes au cluster, car les adresses IP des Pods sont éphémères et imprévisibles.

Les mécanismes d'entrée et de sortie du cluster doivent souvent réécrire l'adresse IP source ou de destination des paquets. Lorsque c'est le cas, rien ne définit si cette réécriture a lieu avant ou après le traitement des NetworkPolicies, et le comportement peut varier selon la combinaison de plugin réseau, de fournisseur cloud, d'implémentation des Service, etc.

En entrée, cela veut dire que, dans certains cas, vous pourrez filtrer les paquets entrants selon leur véritable adresse IP source d'origine, alors que dans d'autres cas, l'« adresse IP source » prise en compte par la NetworkPolicy peut être celle d'un LoadBalancer, du nœud du Pod, etc.

En sortie, cela veut dire que les connexions des pods vers des adresses IP de Service réécrites en adresses IP externes au cluster peuvent être soumises ou non aux politiques basées sur ipBlock.

Politiques par défaut

Par défaut, s'il n'existe aucune politique dans un namespace, tout le trafic entrant et sortant est autorisé vers et depuis les pods de ce namespace. Les exemples suivants vous permettent de modifier ce comportement par défaut dans ce namespace.

Refuser par défaut tout le trafic entrant

Pour créer une politique d'isolation en entrée « par défaut » dans un namespace, créez une NetworkPolicy qui sélectionne tous les pods mais n'autorise aucun trafic entrant vers ces pods.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

Ainsi, même les pods qui ne sont sélectionnés par aucune autre NetworkPolicy restent isolés en entrée. Cette politique n'a pas d'effet sur l'isolation en sortie des pods.

Autoriser tout le trafic entrant

Si vous voulez autoriser toutes les connexions entrantes vers tous les pods d'un namespace, vous pouvez créer une politique qui les autorise explicitement.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

Une fois cette politique en place, aucune autre politique ne peut entraîner le refus d'une connexion entrante vers ces pods. Cette politique n'a pas d'effet sur l'isolation en sortie des pods.

Refuser par défaut tout le trafic sortant

Pour créer une politique d'isolation en sortie « par défaut » dans un namespace, créez une NetworkPolicy qui sélectionne tous les pods mais n'autorise aucun trafic sortant depuis ces pods.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

Ainsi, même les pods qui ne sont sélectionnés par aucune autre NetworkPolicy n'ont droit à aucun trafic sortant. Cette politique ne change pas le comportement d'isolation en entrée des pods.

Avertissement:

Une politique qui refuse par défaut tout le trafic sortant bloque aussi le trafic DNS. Si vos charges de travail ont besoin de la résolution DNS, vous devez ajouter une NetworkPolicy distincte qui autorise le trafic sortant vers le service DNS de votre cluster.

Autoriser tout le trafic sortant

Si vous voulez autoriser toutes les connexions depuis tous les pods d'un namespace, vous pouvez créer une politique qui autorise explicitement toutes les connexions sortantes des pods de ce namespace.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

Une fois cette politique en place, aucune autre politique ne peut entraîner le refus d'une connexion sortante depuis ces pods. Cette politique n'a pas d'effet sur l'isolation en entrée des pods.

Refuser par défaut tout le trafic entrant et sortant

Vous pouvez créer une politique « par défaut » pour un namespace qui empêche tout le trafic entrant ET sortant en créant la NetworkPolicy suivante dans ce namespace.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

Ainsi, même les pods qui ne sont sélectionnés par aucune autre NetworkPolicy n'ont droit à aucun trafic entrant ni sortant.

Filtrage du trafic réseau

Les NetworkPolicies sont définies pour les connexions de couche 4 (TCP, UDP et, en option, SCTP). Pour tous les autres protocoles, le comportement peut varier d'un plugin réseau à l'autre.

Note:

Vous devez utiliser un plugin CNI qui prend en charge les NetworkPolicies pour le protocole SCTP.

Lorsqu'une politique réseau deny all est définie, elle ne garantit que le refus des connexions TCP, UDP et SCTP. Pour les autres protocoles, comme ARP ou ICMP, le comportement n'est pas défini. Il en va de même pour les règles d'autorisation : quand un pod précis est autorisé comme source en entrée ou comme destination en sortie, ce qu'il advient des paquets ICMP, par exemple, n'est pas défini. Des protocoles comme ICMP peuvent être autorisés par certains plugins réseau et refusés par d'autres.

Cibler une plage de ports

Feature state: Stable since Kubernetes v1.25

Quand vous écrivez une NetworkPolicy, vous pouvez cibler une plage de ports au lieu d'un seul port.

Pour cela, utilisez le champ endPort, comme dans l'exemple suivant :

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: multi-port-egress
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
    - Egress
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24
      ports:
        - protocol: TCP
          port: 32000
          endPort: 32768

La règle ci-dessus autorise n'importe quel Pod qui a le label role=db dans le namespace default à communiquer avec n'importe quelle adresse IP de la plage 10.0.0.0/24 en TCP, à condition que le port de destination soit compris entre 32000 et 32768.

Les restrictions suivantes s'appliquent à l'utilisation de ce champ :

  • Le champ endPort doit être supérieur ou égal au champ port.
  • endPort ne peut être défini que si port est aussi défini.
  • Les deux ports doivent être numériques.

Note:

Votre cluster doit utiliser un plugin CNI qui prend en charge le champ endPort dans les spécifications des NetworkPolicies. Si votre plugin réseau ne prend pas en charge le champ endPort et que vous définissez une NetworkPolicy qui l'utilise, seul le champ port sera pris en compte.

Cibler plusieurs namespaces par label

Dans ce scénario, votre NetworkPolicy Egress cible plusieurs namespaces grâce à leurs labels. Pour que cela fonctionne, vous devez poser un label sur les namespaces cibles. Par exemple :

kubectl label namespace frontend namespace=frontend
kubectl label namespace backend namespace=backend

Ajoutez les labels sous namespaceSelector dans votre NetworkPolicy. Par exemple :

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-namespaces
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchExpressions:
        - key: namespace
          operator: In
          values: ["frontend", "backend"]

Note:

Il n'est pas possible d'indiquer directement le nom des namespaces dans une NetworkPolicy. Vous devez utiliser un namespaceSelector avec matchLabels ou matchExpressions pour sélectionner les namespaces selon leurs labels.

Cibler un namespace par son nom

Le plan de contrôle de Kubernetes pose sur tous les namespaces un label immuable kubernetes.io/metadata.name, dont la valeur est le nom du namespace.

Une NetworkPolicy ne peut pas cibler un namespace par son nom via un champ de l'objet, mais vous pouvez utiliser ce label standardisé pour cibler un namespace précis.

Cycle de vie d'un pod

Note:

Ce qui suit s'applique aux clusters qui disposent d'un plugin réseau conforme et d'une implémentation conforme des NetworkPolicies.

Quand un nouvel objet NetworkPolicy est créé, le plugin réseau peut mettre un certain temps à le prendre en compte. Si un pod concerné par une NetworkPolicy est créé avant que le plugin réseau ait fini de traiter cette NetworkPolicy, ce pod peut démarrer sans protection, et les règles d'isolation seront appliquées une fois le traitement de la NetworkPolicy terminé.

Une fois la NetworkPolicy traitée par le plugin réseau :

  1. Tous les nouveaux pods concernés par une NetworkPolicy donnée seront isolés avant de démarrer. Les implémentations des NetworkPolicies doivent garantir que le filtrage est effectif pendant tout le cycle de vie du Pod, dès le tout premier instant où l'un des conteneurs de ce Pod démarre. Comme elles s'appliquent au niveau du Pod, les NetworkPolicies s'appliquent de la même façon aux conteneurs d'initialisation, aux conteneurs sidecar et aux conteneurs ordinaires.

  2. Les règles d'autorisation finiront par être appliquées après les règles d'isolation (ou peut-être en même temps). Dans le pire des cas, un pod nouvellement créé peut n'avoir aucune connectivité réseau au démarrage, si les règles d'isolation étaient déjà appliquées mais pas encore les règles d'autorisation.

Chaque NetworkPolicy créée finira par être traitée par un plugin réseau, mais l'API Kubernetes ne permet pas de savoir à quel moment précis.

Les pods doivent donc pouvoir démarrer avec une connectivité réseau différente de celle attendue. Si vous devez vous assurer que le pod peut joindre certaines destinations avant de démarrer, vous pouvez utiliser un conteneur d'initialisation qui attend que ces destinations soient joignables avant que le kubelet ne démarre les conteneurs de l'application.

Chaque NetworkPolicy finira par être appliquée à tous les pods sélectionnés. Comme le plugin réseau peut mettre en œuvre les NetworkPolicies de manière distribuée, il est possible que les pods aient une vue légèrement incohérente des politiques réseau au moment de la création d'un pod, ou quand des pods ou des politiques changent. Par exemple, un pod nouvellement créé qui doit pouvoir joindre à la fois le Pod A sur le Nœud 1 et le Pod B sur le Nœud 2 peut constater qu'il joint le Pod A immédiatement, mais qu'il ne peut joindre le Pod B que quelques secondes plus tard.

NetworkPolicy et pods hostNetwork

Le comportement des NetworkPolicies pour les pods hostNetwork n'est pas défini, mais il devrait se limiter à deux possibilités :

  • Le plugin réseau sait distinguer le trafic des pods hostNetwork de tout le reste du trafic (y compris distinguer le trafic de différents pods hostNetwork sur le même nœud), et applique les NetworkPolicies aux pods hostNetwork comme aux pods qui utilisent le réseau des pods.
  • Le plugin réseau ne sait pas distinguer correctement le trafic des pods hostNetwork, et ignore donc les pods hostNetwork lorsqu'il évalue podSelector et namespaceSelector. Le trafic depuis et vers les pods hostNetwork est traité comme tout autre trafic depuis et vers l'adresse IP du nœud. (C'est l'implémentation la plus courante.)

Cela s'applique quand

  1. un pod hostNetwork est sélectionné par spec.podSelector.

      ...
      spec:
        podSelector:
          matchLabels:
            role: client
      ...
    
  2. un pod hostNetwork est sélectionné par un podSelector ou un namespaceSelector dans une règle ingress ou egress.

      ...
      ingress:
        - from:
          - podSelector:
              matchLabels:
                role: client
      ...
    

Par ailleurs, comme les pods hostNetwork ont la même adresse IP que le nœud sur lequel ils s'exécutent, leurs connexions sont traitées comme des connexions du nœud. Par exemple, vous pouvez autoriser le trafic venant d'un Pod hostNetwork avec une règle ipBlock.

Ce que les politiques réseau ne permettent pas de faire (du moins, pas encore)

Dans Kubernetes 1.37, les fonctionnalités suivantes n'existent pas dans l'API NetworkPolicy, mais vous pouvez peut-être les contourner avec des composants du système d'exploitation (comme SELinux, OpenVSwitch, IPTables, etc.), des technologies de couche 7 (contrôleurs d'Ingress, implémentations de service mesh) ou des contrôleurs d'admission. Si vous découvrez la sécurité réseau dans Kubernetes, sachez que les cas d'usage suivants ne peuvent pas (encore) être mis en œuvre avec l'API NetworkPolicy.

  • Forcer le trafic interne du cluster à passer par une passerelle commune (un service mesh ou un autre proxy est sans doute plus adapté).
  • Tout ce qui touche à TLS (utilisez un service mesh ou un contrôleur d'Ingress pour cela).
  • Des politiques propres à un nœud (vous pouvez utiliser la notation CIDR, mais vous ne pouvez pas cibler les nœuds par leur identité Kubernetes).
  • Cibler des services par leur nom (vous pouvez en revanche cibler des pods ou des namespaces par leurs labels, ce qui est souvent un bon moyen de contourner le problème).
  • Créer ou gérer des « demandes de politique » traitées par un tiers.
  • Des politiques par défaut appliquées à tous les namespaces ou à tous les pods (certains projets et distributions Kubernetes tiers savent le faire).
  • Des outils avancés d'interrogation des politiques et d'analyse d'accessibilité.
  • Journaliser les événements de sécurité réseau (par exemple les connexions bloquées ou acceptées).
  • Refuser explicitement par une politique (actuellement, le modèle des NetworkPolicies refuse par défaut et permet seulement d'ajouter des règles d'autorisation).
  • Empêcher le trafic de loopback ou le trafic entrant venant de l'hôte (les Pods ne peuvent pas, à l'heure actuelle, bloquer l'accès à localhost, ni bloquer l'accès depuis le nœud sur lequel ils s'exécutent).

Impact des NetworkPolicies sur les connexions existantes

Quand l'ensemble des NetworkPolicies qui s'appliquent à une connexion existante change (parce que les NetworkPolicies sont modifiées, ou parce que les labels concernés des namespaces ou des pods sélectionnés par la politique, côté sujet comme côté pairs, changent pendant une connexion existante), c'est l'implémentation qui détermine si le changement s'applique ou non à cette connexion existante. Exemple : une politique est créée et refuse une connexion jusque-là autorisée. C'est l'implémentation du plugin réseau sous-jacent qui détermine si cette nouvelle politique ferme ou non les connexions existantes. Il est recommandé de ne pas modifier les politiques, les pods ou les namespaces d'une façon qui pourrait affecter les connexions existantes.

A suivre

7 - DNS pour les Services et les Pods

Vos charges de travail peuvent découvrir les Services de votre cluster grâce au DNS ; cette page explique comment cela fonctionne.

Kubernetes crée des enregistrements DNS pour les Services et les Pods. Vous pouvez joindre les Services avec des noms DNS stables plutôt qu'avec des adresses IP.

Kubernetes publie des informations sur les Pods et les Services, qui servent à configurer le DNS. Le kubelet configure le DNS des Pods pour que les conteneurs en cours d'exécution puissent trouver les Services par leur nom plutôt que par leur adresse IP.

Les Services définis dans le cluster reçoivent des noms DNS. Par défaut, la liste de recherche DNS d'un Pod client contient le namespace du Pod lui-même et le domaine par défaut du cluster.

Namespaces des Services

Une requête DNS peut renvoyer des résultats différents selon le namespace du Pod qui l'envoie. Les requêtes DNS qui n'indiquent pas de namespace sont limitées au namespace du Pod. Pour accéder aux Services d'autres namespaces, indiquez le namespace dans la requête DNS.

Prenons par exemple un Pod dans un namespace test, et un Service data dans le namespace prod.

Une requête pour data ne renvoie aucun résultat, car elle utilise le namespace test du Pod.

Une requête pour data.prod renvoie le résultat attendu, car elle indique le namespace.

Les requêtes DNS peuvent être complétées à l'aide du fichier /etc/resolv.conf du Pod. Le kubelet configure ce fichier pour chaque Pod. Par exemple, une requête pour data seul peut être complétée en data.test.svc.cluster.local. Ce sont les valeurs de l'option search qui servent à compléter les requêtes. Pour en savoir plus sur les requêtes DNS, consultez la page de manuel de resolv.conf.

nameserver 10.32.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

En résumé, un Pod du namespace test peut résoudre aussi bien data.prod que data.prod.svc.cluster.local.

Enregistrements DNS

Quels objets reçoivent des enregistrements DNS ?

  1. Les Services
  2. Les Pods

Les sections suivantes décrivent en détail les types d'enregistrements DNS et la structure pris en charge. Toute autre structure, tout autre nom ou toute autre requête qui se trouverait fonctionner relève de détails d'implémentation susceptibles de changer sans préavis. Pour une spécification plus à jour, consultez Kubernetes DNS-Based Service Discovery.

Services

Enregistrements A/AAAA

Les Services « normaux » (non headless) reçoivent des enregistrements DNS A et/ou AAAA, selon la ou les familles d'adresses IP du Service, avec un nom de la forme my-svc.my-namespace.svc.cluster-domain.example. Ce nom est résolu en l'adresse IP de cluster du Service.

Les Services headless (sans IP de cluster) reçoivent eux aussi des enregistrements DNS A et/ou AAAA, avec un nom de la forme my-svc.my-namespace.svc.cluster-domain.example. Contrairement aux Services normaux, ce nom est résolu en l'ensemble des adresses IP de tous les Pods sélectionnés par le Service. Les clients sont censés utiliser cet ensemble, ou bien y choisir une adresse en round-robin standard.

Enregistrements SRV

Des enregistrements SRV sont créés pour les ports nommés des Services normaux ou headless.

  • Pour chaque port nommé, l'enregistrement SRV a la forme _port-name._port-protocol.my-svc.my-namespace.svc.cluster-domain.example.
  • Pour un Service normal, il est résolu en le numéro de port et le nom de domaine my-svc.my-namespace.svc.cluster-domain.example.
  • Pour un Service headless, il est résolu en plusieurs réponses, une pour chaque Pod sous-jacent au Service. Chaque réponse contient le numéro de port et le nom de domaine du Pod, de la forme hostname.my-svc.my-namespace.svc.cluster-domain.example.

Pods

Enregistrements A/AAAA

Avant la mise en œuvre de la spécification DNS, les versions de Kube-DNS utilisaient la résolution DNS suivante :

<pod-IPv4-address>.<namespace>.pod.<cluster-domain>

Par exemple, si un Pod du namespace default a l'adresse IP 172.17.0.3, et que le nom de domaine de votre cluster est cluster.local, le Pod a le nom DNS suivant :

172-17-0-3.default.pod.cluster.local

Certains mécanismes DNS de cluster, comme CoreDNS, fournissent aussi des enregistrements A de la forme :

<pod-ipv4-address>.<service-name>.<my-namespace>.svc.<cluster-domain.example>

Par exemple, si un Pod du namespace cafe a l'adresse IP 172.17.0.3, qu'il est un point de terminaison d'un Service nommé barista et que le nom de domaine de votre cluster est cluster.local, le Pod aura l'enregistrement DNS A suivant, propre au Service.

172-17-0-3.barista.cafe.svc.cluster.local

Champs hostname et subdomain d'un Pod

Actuellement, lorsqu'un Pod est créé, son nom d'hôte (vu depuis l'intérieur du Pod) est la valeur metadata.name du Pod.

La spec du Pod a un champ facultatif hostname, qui permet d'indiquer un autre nom d'hôte. Lorsqu'il est renseigné, il prime sur le nom du Pod pour définir le nom d'hôte du Pod (toujours vu depuis l'intérieur du Pod). Par exemple, un Pod dont spec.hostname vaut "my-host" aura pour nom d'hôte "my-host".

La spec du Pod a aussi un champ facultatif subdomain, qui permet d'indiquer que le Pod fait partie d'un sous-groupe du namespace. Par exemple, un Pod dont spec.hostname vaut "foo" et spec.subdomain vaut "bar", dans le namespace "my-namespace", aura pour nom d'hôte "foo" et pour nom de domaine pleinement qualifié (FQDN) "foo.bar.my-namespace.svc.cluster.local" (là encore, vu depuis l'intérieur du Pod).

S'il existe un Service headless dans le même namespace que le Pod, avec le même nom que le sous-domaine, le serveur DNS du cluster renvoie aussi des enregistrements A et/ou AAAA pour le nom d'hôte pleinement qualifié du Pod.

Exemple :

apiVersion: v1
kind: Service
metadata:
  name: busybox-subdomain
spec:
  selector:
    name: busybox
  clusterIP: None
  ports:
  - name: foo # name is not required for single-port Services
    port: 1234
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox1
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: busybox-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox2
  labels:
    name: busybox
spec:
  hostname: busybox-2
  subdomain: busybox-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

Avec le Service "busybox-subdomain" ci-dessus et les Pods dont spec.subdomain vaut "busybox-subdomain", le premier Pod verra "busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example" comme son propre FQDN. Le DNS fournit des enregistrements A et/ou AAAA pour ce nom, qui pointent vers l'adresse IP du Pod. Les deux Pods « busybox1 » et « busybox2 » auront chacun leurs propres enregistrements d'adresse.

Un EndpointSlice peut indiquer le nom d'hôte DNS de n'importe quelle adresse de point de terminaison, avec son adresse IP.

Note:

Aucun enregistrement A ou AAAA n'est créé pour les noms de Pods si le Pod n'a pas de hostname. Pour un Pod sans hostname mais avec un subdomain, seul l'enregistrement A ou AAAA du Service headless (busybox-subdomain.my-namespace.svc.cluster-domain.example) est créé ; il pointe vers les adresses IP des Pods. Par ailleurs, le Pod doit être prêt (ready) pour avoir un enregistrement, sauf si publishNotReadyAddresses=True est défini sur le Service.

Champ setHostnameAsFQDN d'un Pod

Feature state: Stable since Kubernetes v1.22

Lorsqu'un Pod est configuré pour avoir un nom de domaine pleinement qualifié (FQDN), son nom d'hôte est le nom d'hôte court. Par exemple, pour un Pod dont le nom de domaine pleinement qualifié est busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example, la commande hostname exécutée dans ce Pod renvoie par défaut busybox-1, et la commande hostname --fqdn renvoie le FQDN.

Lorsque vous définissez setHostnameAsFQDN: true dans la spec du Pod, le kubelet écrit le FQDN du Pod comme nom d'hôte dans le namespace de ce Pod. Dans ce cas, hostname et hostname --fqdn renvoient tous les deux le FQDN du Pod.

Note:

Sous Linux, le champ hostname du noyau (le champ nodename de struct utsname) est limité à 64 caractères.

Si un Pod active cette fonctionnalité et que son FQDN dépasse 64 caractères, il ne pourra pas démarrer. Le Pod restera à l'état Pending (ContainerCreating dans kubectl) et générera des événements d'erreur, comme Failed to construct FQDN from Pod hostname and cluster domain, FQDN long-FQDN is too long (64 characters is the max, 70 characters requested). Pour améliorer l'expérience utilisateur dans ce cas, vous pouvez créer un contrôleur de webhook d'admission qui vérifie la taille du FQDN lorsque les utilisateurs créent des objets de premier niveau, par exemple un Deployment.

Politique DNS d'un Pod

Les politiques DNS se définissent Pod par Pod. Actuellement, Kubernetes prend en charge les politiques DNS suivantes pour les Pods. Elles sont indiquées dans le champ dnsPolicy de la spec du Pod.

  • « Default » : le Pod hérite de la configuration de résolution de noms du nœud sur lequel il s'exécute. Consultez la discussion à ce sujet pour plus de détails.

  • « ClusterFirst » : toute requête DNS qui ne correspond pas au suffixe de domaine configuré pour le cluster, comme « www.kubernetes.io », est transmise par le serveur DNS à un serveur de noms en amont. Les administrateurs du cluster peuvent avoir configuré des serveurs DNS supplémentaires, pour des domaines de délégation (stub domains) ou en amont. Consultez la discussion à ce sujet pour savoir comment les requêtes DNS sont traitées dans ces cas.

  • « ClusterFirstWithHostNet » : pour les Pods qui s'exécutent avec hostNetwork, il faut régler explicitement leur politique DNS sur « ClusterFirstWithHostNet ». Sinon, les Pods qui s'exécutent avec hostNetwork et "ClusterFirst" se comporteront comme avec la politique "Default".

    Note:

    Ce n'est pas pris en charge sous Windows. Pour plus de détails, voir ci-dessous.
  • « None » : permet à un Pod d'ignorer les paramètres DNS de l'environnement Kubernetes. Tous les paramètres DNS sont alors censés être fournis par le champ dnsConfig de la spec du Pod. Consultez la sous-section Configuration DNS d'un Pod ci-dessous.

Note:

« Default » n'est pas la politique DNS par défaut. Si dnsPolicy n'est pas indiqué explicitement, c'est « ClusterFirst » qui est utilisé.

L'exemple ci-dessous montre un Pod dont la politique DNS est « ClusterFirstWithHostNet », car son champ hostNetwork vaut true.

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet

Configuration DNS d'un Pod

Feature state: Stable since Kubernetes v1.14

La configuration DNS d'un Pod donne aux utilisateurs plus de contrôle sur les paramètres DNS de ce Pod.

Le champ dnsConfig est facultatif et fonctionne avec n'importe quelle valeur de dnsPolicy. En revanche, lorsque le dnsPolicy d'un Pod vaut « None », le champ dnsConfig doit être renseigné.

Voici les propriétés qu'un utilisateur peut indiquer dans le champ dnsConfig :

  • nameservers : une liste d'adresses IP qui serviront de serveurs DNS au Pod. On peut indiquer au maximum 3 adresses IP. Lorsque le dnsPolicy du Pod vaut « None », la liste doit contenir au moins une adresse IP ; dans les autres cas, cette propriété est facultative. Les serveurs indiqués sont ajoutés aux serveurs de noms de base générés à partir de la politique DNS choisie, les adresses en double étant supprimées.
  • searches : une liste de domaines de recherche DNS pour la résolution des noms d'hôte dans le Pod. Cette propriété est facultative. Si elle est renseignée, la liste fournie est fusionnée avec les domaines de recherche de base générés à partir de la politique DNS choisie. Les noms de domaine en double sont supprimés. Kubernetes accepte jusqu'à 32 domaines de recherche.
  • options : une liste facultative d'objets, dont chacun peut avoir une propriété name (obligatoire) et une propriété value (facultative). Le contenu de cette propriété est fusionné avec les options générées à partir de la politique DNS choisie. Les entrées en double sont supprimées.

Voici un exemple de Pod avec des paramètres DNS personnalisés :

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - 192.0.2.1 # this is an example
    searches:
      - ns1.svc.cluster-domain.example
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

Une fois le Pod ci-dessus créé, le fichier /etc/resolv.conf du conteneur test a le contenu suivant :

nameserver 192.0.2.1
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0

Pour une configuration IPv6, le chemin de recherche et le serveur de noms devraient se présenter ainsi :

kubectl exec -it dns-example -- cat /etc/resolv.conf

Le résultat ressemble à ceci :

nameserver 2001:db8:30::a
search default.svc.cluster-domain.example svc.cluster-domain.example cluster-domain.example
options ndots:5

Limites de la liste des domaines de recherche DNS

Feature state: Stable since Kubernetes v1.28

Kubernetes lui-même ne limite la configuration DNS que lorsque la liste des domaines de recherche dépasse 32 éléments, ou que la longueur totale de tous les domaines de recherche dépasse 2048. Cette limite s'applique respectivement au fichier de configuration du résolveur du nœud, à la configuration DNS du Pod et à la configuration DNS fusionnée.

Note:

Les anciennes versions de certains environnements d'exécution de conteneurs peuvent imposer leurs propres restrictions sur le nombre de domaines de recherche DNS. Selon l'environnement d'exécution de conteneurs, les Pods qui ont beaucoup de domaines de recherche DNS peuvent rester bloqués à l'état Pending.

Ce problème est connu pour containerd v1.5.5 et versions antérieures, ainsi que pour CRI-O v1.21 et versions antérieures.

Résolution DNS sur les nœuds Windows

  • ClusterFirstWithHostNet n'est pas pris en charge pour les Pods qui s'exécutent sur des nœuds Windows. Windows considère tout nom qui contient un . comme un FQDN et ne fait pas de résolution FQDN.
  • Sous Windows, plusieurs résolveurs DNS peuvent être utilisés. Comme leurs comportements diffèrent légèrement, il est recommandé d'utiliser la cmdlet PowerShell Resolve-DNSName pour résoudre les noms.
  • Sous Linux, vous disposez d'une liste de suffixes DNS, utilisée lorsque la résolution d'un nom en tant que nom pleinement qualifié a échoué. Sous Windows, vous ne pouvez avoir qu'un seul suffixe DNS : celui associé au namespace du Pod (par exemple mydns.svc.cluster.local). Windows peut résoudre les FQDN, les Services ou les noms réseau résolubles avec ce seul suffixe. Par exemple, un Pod lancé dans le namespace default aura le suffixe DNS default.svc.cluster.local. Dans un Pod Windows, vous pouvez résoudre à la fois kubernetes.default.svc.cluster.local et kubernetes, mais pas les noms partiellement qualifiés (kubernetes.default ou kubernetes.default.svc).

A suivre

Pour savoir comment administrer les configurations DNS, consultez Configurer le service DNS.

8 - Double pile IPv4/IPv6

Kubernetes vous permet de configurer un réseau en pile simple IPv4, un réseau en pile simple IPv6, ou un réseau en double pile avec les deux familles d'adresses actives. Cette page explique comment.
Feature state: Stable since Kubernetes v1.23

Le réseau en double pile IPv4/IPv6 permet d'attribuer à la fois des adresses IPv4 et IPv6 aux Pods et aux Services.

Le réseau en double pile IPv4/IPv6 est activé par défaut dans votre cluster Kubernetes depuis la version 1.21, ce qui permet d'attribuer simultanément des adresses IPv4 et IPv6.

Fonctionnalités prises en charge

La double pile IPv4/IPv6 de votre cluster Kubernetes apporte les fonctionnalités suivantes :

  • Réseau des Pods en double pile (une adresse IPv4 et une adresse IPv6 attribuées à chaque Pod)
  • Services compatibles IPv4 et IPv6
  • Routage du trafic sortant des Pods vers l'extérieur du cluster (par exemple Internet) par des interfaces IPv4 et IPv6

Prérequis

Pour utiliser des clusters Kubernetes en double pile IPv4/IPv6, il vous faut :

  • Kubernetes 1.20 ou une version ultérieure

    Pour savoir comment utiliser les Services en double pile avec des versions antérieures de Kubernetes, consultez la documentation de la version de Kubernetes concernée.

  • La prise en charge du réseau en double pile par le fournisseur (le fournisseur de cloud ou un autre fournisseur doit pouvoir doter les nœuds Kubernetes d'interfaces réseau IPv4/IPv6 routables)

  • Un plugin réseau qui prend en charge le réseau en double pile.

Configurer la double pile IPv4/IPv6

Pour configurer la double pile IPv4/IPv6, définissez des plages réseau en double pile pour le cluster :

  • kube-apiserver :
    • --service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
  • kube-controller-manager :
    • --cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
    • --service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
    • --node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6 vaut par défaut /24 pour IPv4 et /64 pour IPv6
  • kube-proxy :
    • --cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
  • kubelet :
    • --node-ip=<IPv4 IP>,<IPv6 IP>
      • Cette option est obligatoire pour les nœuds bare metal en double pile (les nœuds qui ne définissent pas de fournisseur de cloud avec l'option --cloud-provider). Si vous utilisez un fournisseur de cloud et que vous choisissez de remplacer les adresses IP de nœud choisies par ce fournisseur, renseignez l'option --node-ip.
      • (Les fournisseurs de cloud intégrés historiques ne prennent pas en charge --node-ip en double pile.)

Note:

Exemple de CIDR IPv4 : 10.244.0.0/16 (à remplacer par votre propre plage d'adresses)

Exemple de CIDR IPv6 : fdXY:IJKL:MNOP:15::/64 (cet exemple montre le format, mais ce n'est pas une adresse valide ; voir la RFC 4193)

Services

Vous pouvez créer des Services qui utilisent IPv4, IPv6 ou les deux.

Par défaut, la famille d'adresses d'un Service est celle de la première plage d'adresses IP des Services (configurée par l'option --service-cluster-ip-range de kube-apiserver).

Lorsque vous définissez un Service, vous pouvez le configurer en double pile si vous le souhaitez. Pour indiquer le comportement voulu, donnez au champ .spec.ipFamilyPolicy l'une des valeurs suivantes :

  • SingleStack : Service en pile simple. Le plan de contrôle attribue une adresse IP de cluster au Service, en utilisant la première plage d'adresses IP des Services configurée.
  • PreferDualStack : attribue au Service des adresses IP de cluster IPv4 et IPv6 si la double pile est activée. Si la double pile n'est pas activée ou pas prise en charge, le Service se comporte comme en pile simple.
  • RequireDualStack : attribue les .spec.clusterIPs du Service à partir des plages d'adresses IPv4 et IPv6 si la double pile est activée. Si la double pile n'est pas activée ou pas prise en charge, la création de l'objet Service dans l'API échoue.
    • Choisit .spec.clusterIP dans la liste .spec.clusterIPs en fonction de la famille d'adresses du premier élément du tableau .spec.ipFamilies.

Si vous voulez choisir la famille d'adresses IP à utiliser en pile simple, ou l'ordre des familles d'adresses IP en double pile, vous pouvez indiquer les familles d'adresses dans le champ facultatif .spec.ipFamilies du Service.

Note:

Le champ .spec.ipFamilies n'est modifiable que sous certaines conditions : vous pouvez ajouter ou supprimer une famille d'adresses IP secondaire, mais vous ne pouvez pas changer la famille d'adresses IP principale d'un Service existant.

Vous pouvez donner à .spec.ipFamilies l'une des valeurs de tableau suivantes :

  • ["IPv4"]
  • ["IPv6"]
  • ["IPv4","IPv6"] (double pile)
  • ["IPv6","IPv4"] (double pile)

La première famille de la liste est utilisée pour le champ historique .spec.clusterIP.

Scénarios de configuration des Services en double pile

Les exemples suivants montrent le comportement de différents scénarios de configuration de Services en double pile.

Options de double pile pour les nouveaux Services

  1. Cette spécification de Service ne définit pas explicitement .spec.ipFamilyPolicy. Lorsque vous créez ce Service, Kubernetes lui attribue une adresse IP de cluster prise dans la première plage service-cluster-ip-range configurée, et renseigne SingleStack dans .spec.ipFamilyPolicy. (Les Services sans sélecteurs et les Services headless avec sélecteurs se comportent de la même façon.)

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      labels:
        app.kubernetes.io/name: MyApp
    spec:
      selector:
        app.kubernetes.io/name: MyApp
      ports:
        - protocol: TCP
          port: 80
    
  2. Cette spécification de Service définit explicitement PreferDualStack dans .spec.ipFamilyPolicy. Lorsque vous créez ce Service sur un cluster en double pile, Kubernetes lui attribue à la fois une adresse IPv4 et une adresse IPv6. Le plan de contrôle met à jour la .spec du Service pour enregistrer les adresses IP attribuées. Le champ .spec.clusterIPs est le champ principal et contient les deux adresses IP attribuées ; .spec.clusterIP est un champ secondaire dont la valeur est calculée à partir de .spec.clusterIPs.

    • Pour le champ .spec.clusterIP, le plan de contrôle enregistre l'adresse IP qui appartient à la même famille d'adresses que la première plage d'adresses IP des Services.
    • Sur un cluster en pile simple, les champs .spec.clusterIPs et .spec.clusterIP ne contiennent tous les deux qu'une seule adresse.
    • Sur un cluster où la double pile est activée, indiquer RequireDualStack dans .spec.ipFamilyPolicy a le même effet que PreferDualStack.
    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      labels:
        app.kubernetes.io/name: MyApp
    spec:
      ipFamilyPolicy: PreferDualStack
      selector:
        app.kubernetes.io/name: MyApp
      ports:
        - protocol: TCP
          port: 80
    
  3. Cette spécification de Service définit explicitement IPv6 et IPv4 dans .spec.ipFamilies, ainsi que PreferDualStack dans .spec.ipFamilyPolicy. Lorsque Kubernetes attribue une adresse IPv6 et une adresse IPv4 dans .spec.clusterIPs, .spec.clusterIP reçoit l'adresse IPv6, premier élément du tableau .spec.clusterIPs, ce qui remplace le comportement par défaut.

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      labels:
        app.kubernetes.io/name: MyApp
    spec:
      ipFamilyPolicy: PreferDualStack
      ipFamilies:
      - IPv6
      - IPv4
      selector:
        app.kubernetes.io/name: MyApp
      ports:
        - protocol: TCP
          port: 80
    

Valeurs par défaut de la double pile pour les Services existants

Les exemples suivants montrent le comportement par défaut lorsque la double pile est activée sur un cluster où des Services existent déjà. (La mise à niveau d'un cluster existant vers la version 1.21 ou ultérieure active la double pile.)

  1. Lorsque la double pile est activée sur un cluster, le plan de contrôle configure les Services existants (qu'ils soient IPv4 ou IPv6) en donnant à .spec.ipFamilyPolicy la valeur SingleStack et à .spec.ipFamilies la famille d'adresses du Service existant. L'adresse IP de cluster du Service existant est enregistrée dans .spec.clusterIPs.

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      labels:
        app.kubernetes.io/name: MyApp
    spec:
      selector:
        app.kubernetes.io/name: MyApp
      ports:
        - protocol: TCP
          port: 80
    

    Vous pouvez vérifier ce comportement en inspectant un Service existant avec kubectl.

    kubectl get svc my-service -o yaml
    
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app.kubernetes.io/name: MyApp
      name: my-service
    spec:
      clusterIP: 10.0.197.123
      clusterIPs:
      - 10.0.197.123
      ipFamilies:
      - IPv4
      ipFamilyPolicy: SingleStack
      ports:
      - port: 80
        protocol: TCP
        targetPort: 80
      selector:
        app.kubernetes.io/name: MyApp
      type: ClusterIP
    status:
      loadBalancer: {}
    
  2. Lorsque la double pile est activée sur un cluster, le plan de contrôle configure les Services headless existants avec sélecteurs en donnant à .spec.ipFamilyPolicy la valeur SingleStack et à .spec.ipFamilies la famille d'adresses de la première plage d'adresses IP des Services (configurée par l'option --service-cluster-ip-range de kube-apiserver), même si .spec.clusterIP vaut None.

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      labels:
        app.kubernetes.io/name: MyApp
    spec:
      selector:
        app.kubernetes.io/name: MyApp
      ports:
        - protocol: TCP
          port: 80
    

    Vous pouvez vérifier ce comportement en inspectant avec kubectl un Service headless existant doté de sélecteurs.

    kubectl get svc my-service -o yaml
    
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app.kubernetes.io/name: MyApp
      name: my-service
    spec:
      clusterIP: None
      clusterIPs:
      - None
      ipFamilies:
      - IPv4
      ipFamilyPolicy: SingleStack
      ports:
      - port: 80
        protocol: TCP
        targetPort: 80
      selector:
        app.kubernetes.io/name: MyApp
    

Passer un Service de la pile simple à la double pile, et inversement

Un Service peut passer de la pile simple à la double pile, et de la double pile à la pile simple.

  1. Pour passer un Service de la pile simple à la double pile, faites passer .spec.ipFamilyPolicy de SingleStack à PreferDualStack ou RequireDualStack, selon vos besoins. Lorsque vous passez ce Service en double pile, Kubernetes lui attribue la famille d'adresses manquante, si bien que le Service a désormais des adresses IPv4 et IPv6.

    Modifiez la spécification du Service en passant .spec.ipFamilyPolicy de SingleStack à PreferDualStack.

    Avant :

    spec:
      ipFamilyPolicy: SingleStack
    

    Après :

    spec:
      ipFamilyPolicy: PreferDualStack
    
  2. Pour passer un Service de la double pile à la pile simple, faites passer .spec.ipFamilyPolicy de PreferDualStack ou RequireDualStack à SingleStack. Lorsque vous passez ce Service en pile simple, Kubernetes ne conserve que le premier élément du tableau .spec.clusterIPs, affecte cette adresse IP à .spec.clusterIP et donne à .spec.ipFamilies la famille d'adresses de .spec.clusterIPs.

Services headless sans sélecteur

Pour les Services headless sans sélecteurs dont .spec.ipFamilyPolicy n'est pas défini explicitement, le champ .spec.ipFamilyPolicy vaut par défaut RequireDualStack.

Service de type LoadBalancer

Pour provisionner un équilibreur de charge en double pile pour votre Service :

  • Donnez au champ .spec.type la valeur LoadBalancer
  • Donnez au champ .spec.ipFamilyPolicy la valeur PreferDualStack ou RequireDualStack

Note:

Pour utiliser un Service de type LoadBalancer en double pile, votre fournisseur de cloud doit prendre en charge les équilibreurs de charge IPv4 et IPv6.

Trafic sortant

Si vous voulez autoriser le trafic sortant pour joindre des destinations extérieures au cluster (par exemple Internet) depuis un Pod qui utilise des adresses IPv6 non routables publiquement, vous devez permettre au Pod d'utiliser une adresse IPv6 routée publiquement, grâce à un mécanisme comme un proxy transparent ou le masquage d'adresses IP (IP masquerading). Le projet ip-masq-agent prend en charge le masquage d'adresses IP sur les clusters en double pile.

Note:

Vérifiez que votre fournisseur CNI prend en charge IPv6.

Prise en charge de Windows

Kubernetes sous Windows ne prend pas en charge le réseau en pile simple « IPv6 uniquement ». En revanche, le réseau en double pile IPv4/IPv6 est pris en charge pour les Pods et les nœuds, avec des Services d'une seule famille d'adresses.

Vous pouvez utiliser le réseau en double pile IPv4/IPv6 avec les réseaux l2bridge.

Note:

Sous Windows, les réseaux overlay (VXLAN) ne prennent pas en charge le réseau en double pile.

Pour en savoir plus sur les différents modes réseau de Windows, consultez la page Réseau sous Windows.

A suivre

9 - Routage tenant compte de la topologie

Le routage tenant compte de la topologie (Topology Aware Routing) fournit un mécanisme qui aide à garder le trafic réseau dans sa zone d'origine. Privilégier le trafic dans la même zone entre les Pods de votre cluster peut améliorer la fiabilité, les performances (latence et débit réseau) ou réduire les coûts.
Feature state: Beta since Kubernetes v1.23

Note:

Avant Kubernetes 1.27, cette fonctionnalité s'appelait Topology Aware Hints.

Le routage tenant compte de la topologie adapte le comportement du routage pour garder de préférence le trafic dans sa zone d'origine. Dans certains cas, cela peut réduire les coûts ou améliorer les performances réseau.

Motivation

Les clusters Kubernetes sont de plus en plus souvent déployés dans des environnements multizones. Le routage tenant compte de la topologie fournit un mécanisme qui aide à garder le trafic dans sa zone d'origine. Lorsqu'il calcule les points de terminaison d'un Service, le contrôleur EndpointSlice tient compte de la topologie (région et zone) de chaque point de terminaison et renseigne le champ hints (indications) pour l'attribuer à une zone. Les composants du cluster, comme kube-proxy, peuvent ensuite utiliser ces indications pour orienter le routage du trafic (en privilégiant les points de terminaison topologiquement les plus proches).

Activer le routage tenant compte de la topologie

Note:

Avant Kubernetes 1.27, ce comportement était contrôlé par l'annotation service.kubernetes.io/topology-aware-hints.

Vous pouvez activer le routage tenant compte de la topologie pour un Service en définissant l'annotation service.kubernetes.io/topology-mode sur Auto. Lorsque chaque zone dispose de suffisamment de points de terminaison disponibles, des indications de topologie sont ajoutées aux EndpointSlices pour attribuer chaque point de terminaison à une zone précise. Le trafic est alors acheminé plus près de son origine.

Quand cela fonctionne le mieux

Cette fonctionnalité donne les meilleurs résultats lorsque :

1. Le trafic entrant est réparti uniformément

Si une grande partie du trafic provient d'une seule zone, ce trafic risque de surcharger le sous-ensemble de points de terminaison attribués à cette zone. Cette fonctionnalité est déconseillée lorsque le trafic entrant est censé provenir d'une seule zone.

2. Le Service a au moins 3 points de terminaison par zone

Dans un cluster à trois zones, cela représente au moins 9 points de terminaison. Avec moins de 3 points de terminaison par zone, il y a une forte probabilité (environ 50 %) que le contrôleur EndpointSlice ne parvienne pas à répartir les points de terminaison de manière équilibrée et se rabatte sur le routage par défaut à l'échelle du cluster.

Fonctionnement

L'heuristique « Auto » tente d'attribuer à chaque zone un nombre de points de terminaison proportionnel. Cette heuristique donne de meilleurs résultats pour les Services qui ont un nombre important de points de terminaison.

Contrôleur EndpointSlice

Le contrôleur EndpointSlice est chargé de définir les indications sur les EndpointSlices lorsque cette heuristique est activée. Le contrôleur attribue à chaque zone une part proportionnelle des points de terminaison. Cette proportion dépend du nombre de cœurs CPU allouables des nœuds de cette zone. Par exemple, si une zone dispose de 2 cœurs CPU et une autre d'un seul, le contrôleur attribuera deux fois plus de points de terminaison à la zone qui dispose de 2 cœurs.

L'exemple suivant montre un EndpointSlice dont les indications ont été renseignées :

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: example-hints
  labels:
    kubernetes.io/service-name: example-svc
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
    hostname: pod-1
    zone: zone-a
    hints:
      forZones:
        - name: "zone-a"

kube-proxy

Le composant kube-proxy filtre les points de terminaison vers lesquels il achemine le trafic en fonction des indications définies par le contrôleur EndpointSlice. Dans la plupart des cas, kube-proxy peut ainsi acheminer le trafic vers des points de terminaison de la même zone. Il arrive que le contrôleur attribue des points de terminaison d'une autre zone pour mieux équilibrer leur répartition entre les zones. Une partie du trafic est alors acheminée vers d'autres zones.

Garde-fous

Le plan de contrôle de Kubernetes et le kube-proxy de chaque nœud appliquent certains garde-fous avant d'utiliser les indications de topologie. Si l'un d'eux n'est pas respecté, kube-proxy choisit des points de terminaison n'importe où dans le cluster, quelle que soit la zone.

  1. Nombre insuffisant de points de terminaison : s'il y a moins de points de terminaison que de zones dans le cluster, le contrôleur n'attribue aucune indication.

  2. Répartition équilibrée impossible : dans certains cas, il est impossible de répartir les points de terminaison de manière équilibrée entre les zones. Par exemple, si la zone-a est deux fois plus grande que la zone-b mais qu'il n'y a que 2 points de terminaison, celui attribué à la zone-a risque de recevoir deux fois plus de trafic que celui de la zone-b. Le contrôleur n'attribue pas d'indications s'il ne parvient pas à ramener cette valeur de « surcharge estimée » sous un seuil acceptable pour chaque zone. À noter : ce calcul ne repose pas sur des mesures en temps réel. Un point de terminaison peut toujours se retrouver surchargé.

  3. Informations insuffisantes sur un ou plusieurs nœuds : si un nœud n'a pas de label topology.kubernetes.io/zone ou ne remonte pas de valeur de CPU allouable, le plan de contrôle ne définit aucune indication de topologie sur les points de terminaison, et kube-proxy ne filtre donc pas les points de terminaison par zone.

  4. Un ou plusieurs points de terminaison n'ont pas d'indication de zone : kube-proxy considère alors qu'un passage aux indications de topologie, ou leur abandon, est en cours. Filtrer les points de terminaison d'un Service dans cet état serait risqué : kube-proxy se rabat donc sur tous les points de terminaison.

  5. Une zone n'apparaît dans aucune indication : si kube-proxy ne trouve aucun point de terminaison dont l'indication désigne la zone dans laquelle il s'exécute, il utilise les points de terminaison de toutes les zones. Cette situation se produit surtout lorsque vous ajoutez une nouvelle zone à un cluster existant.

Contraintes

  • Les indications de topologie ne sont pas utilisées lorsque internalTrafficPolicy vaut Local sur un Service. Les deux fonctionnalités peuvent coexister dans un même cluster sur des Services différents, mais pas sur le même Service.

  • Cette approche fonctionne mal pour les Services dont une grande partie du trafic provient d'un sous-ensemble de zones. Elle suppose au contraire que le trafic entrant est à peu près proportionnel à la capacité des nœuds de chaque zone.

  • Le contrôleur EndpointSlice ignore les nœuds qui ne sont pas prêts lorsqu'il calcule la part de chaque zone. Cela peut avoir des conséquences inattendues si une grande partie des nœuds ne sont pas prêts.

  • Le contrôleur EndpointSlice ignore les nœuds qui portent le label node-role.kubernetes.io/control-plane ou node-role.kubernetes.io/master. Cela peut poser problème si des charges de travail s'exécutent aussi sur ces nœuds.

  • Le contrôleur EndpointSlice ne tient pas compte des tolérances lors du déploiement ni lors du calcul de la part de chaque zone. Si les Pods d'un Service sont limités à un sous-ensemble des nœuds du cluster, cela n'est pas pris en compte.

  • Cela peut mal fonctionner avec la mise à l'échelle automatique. Par exemple, si une grande partie du trafic provient d'une seule zone, seuls les points de terminaison attribués à cette zone traiteront ce trafic. Il se peut alors que l'Horizontal Pod Autoscaler ne détecte pas cette situation, ou que les nouveaux pods démarrent dans une autre zone.

Heuristiques personnalisées

Kubernetes est déployé de bien des façons, et aucune heuristique unique d'attribution des points de terminaison aux zones ne convient à tous les cas d'usage. L'un des principaux objectifs de cette fonctionnalité est de permettre le développement d'heuristiques personnalisées lorsque l'heuristique intégrée ne convient pas à votre cas d'usage. Les premiers éléments permettant des heuristiques personnalisées ont été intégrés dans la version 1.27. Il s'agit d'une implémentation limitée, qui ne couvre peut-être pas encore certaines situations pertinentes et plausibles.

A suivre

10 - Réseau sous Windows

Kubernetes prend en charge les nœuds Linux comme les nœuds Windows, et vous pouvez mélanger les deux dans un même cluster. Cette page donne une vue d'ensemble des aspects réseau propres au système d'exploitation Windows.

Le réseau des conteneurs sous Windows

Le réseau des conteneurs Windows est fourni par des plugins CNI. Côté réseau, les conteneurs Windows se comportent comme des machines virtuelles. Chaque conteneur a une carte réseau virtuelle (vNIC), reliée à un commutateur virtuel Hyper-V (vSwitch). Le Host Networking Service (HNS) et le Host Compute Service (HCS) collaborent pour créer les conteneurs et rattacher leurs vNIC aux réseaux. HCS gère les conteneurs, tandis que HNS gère les ressources réseau, comme :

  • les réseaux virtuels (y compris la création des vSwitch) ;
  • les points de terminaison (ou vNIC) ;
  • les namespaces ;
  • les politiques, notamment l'encapsulation des paquets, les règles d'équilibrage de charge, les ACL et les règles de NAT.

Le HNS et le vSwitch de Windows mettent en œuvre l'isolation par namespaces et peuvent créer des cartes réseau virtuelles selon les besoins d'un pod ou d'un conteneur. En revanche, une grande partie de la configuration, comme le DNS, les routes et les métriques, est stockée dans la base de registre Windows, et non dans des fichiers sous /etc, comme c'est le cas sous Linux. La base de registre du conteneur est distincte de celle de l'hôte : des pratiques comme monter le fichier /etc/resolv.conf de l'hôte dans un conteneur n'ont donc pas le même effet que sous Linux. Ces réglages doivent être faits avec les API Windows, exécutées dans le contexte du conteneur. C'est pourquoi les implémentations CNI doivent appeler le HNS pour transmettre les informations réseau au pod ou au conteneur, au lieu de s'appuyer sur des montages de fichiers.

Modes réseau

Windows prend en charge cinq pilotes (ou modes) réseau différents : L2bridge, L2tunnel, Overlay (bêta), Transparent et NAT. Dans un cluster hétérogène comportant des nœuds de travail Windows et Linux, vous devez choisir une solution réseau compatible avec Windows et Linux. Le tableau suivant liste les plugins externes (out-of-tree) pris en charge sous Windows, et indique quand utiliser chaque plugin CNI :

Pilote réseauDescriptionModifications des paquets des conteneursPlugins réseauCaractéristiques des plugins réseau
L2bridgeLes conteneurs sont reliés à un vSwitch externe. Ils sont rattachés au réseau sous-jacent (underlay), mais le réseau physique n'a pas besoin de connaître leurs adresses MAC, car elles sont réécrites en entrée et en sortie.L'adresse MAC est remplacée par celle de l'hôte ; l'adresse IP peut l'être aussi via la politique HNS OutboundNAT.win-bridge, Azure-CNI, Flannel host-gateway (qui utilise win-bridge)win-bridge utilise le mode réseau L2bridge et relie les conteneurs au réseau sous-jacent des hôtes, ce qui offre les meilleures performances. Nécessite des routes définies par l'utilisateur (UDR) pour la connectivité entre nœuds.
L2TunnelCas particulier de l2bridge, utilisé uniquement sur Azure. Tous les paquets sont envoyés à l'hôte de virtualisation, où la politique SDN est appliquée.L'adresse MAC est réécrite, l'adresse IP est visible sur le réseau sous-jacent.Azure-CNIAzure-CNI intègre les conteneurs au vNET Azure et leur permet d'utiliser les fonctionnalités d'Azure Virtual Network. Ils peuvent par exemple se connecter de façon sécurisée aux services Azure ou utiliser les NSG Azure. Consultez quelques exemples avec azure-cni.
OverlayChaque conteneur reçoit une vNIC reliée à un vSwitch externe. Chaque réseau overlay a son propre sous-réseau IP, défini par un préfixe IP personnalisé. Le pilote réseau overlay utilise l'encapsulation VXLAN.Les paquets sont encapsulés avec un en-tête externe.win-overlay, Flannel VXLAN (qui utilise win-overlay)win-overlay est à utiliser lorsque l'on souhaite isoler les réseaux virtuels des conteneurs du réseau sous-jacent des hôtes (par exemple pour des raisons de sécurité). Permet de réutiliser les mêmes adresses IP dans différents réseaux overlay (qui ont des étiquettes VNID différentes) si vous disposez de peu d'adresses IP dans votre datacenter. Cette option nécessite la mise à jour KB4489899 sur Windows Server 2019.
Transparent (cas d'usage particulier pour ovn-kubernetes)Nécessite un vSwitch externe. Les conteneurs sont reliés à un vSwitch externe qui permet la communication intra-pod via des réseaux logiques (commutateurs et routeurs logiques).Les paquets sont encapsulés dans un tunnel GENEVE ou STT pour joindre les pods qui ne sont pas sur le même hôte.
Les paquets sont transmis ou rejetés selon les métadonnées de tunnel fournies par le contrôleur réseau OVN.
Le NAT est appliqué pour la communication nord-sud.
ovn-kubernetesDéploiement avec Ansible. Des ACL distribuées peuvent être appliquées via les politiques Kubernetes. Prise en charge de l'IPAM. L'équilibrage de charge est possible sans kube-proxy. Le NAT se fait sans iptables ni netsh.
NAT (non utilisé dans Kubernetes)Chaque conteneur reçoit une vNIC reliée à un vSwitch interne. Le DNS et le DHCP sont fournis par un composant interne appelé WinNAT.Les adresses MAC et IP sont remplacées par celles de l'hôte.natMentionné ici par souci d'exhaustivité.

Comme indiqué ci-dessus, le plugin CNI de Flannel est aussi pris en charge sous Windows, avec le backend réseau VXLAN (prise en charge bêta, délègue à win-overlay) et le backend réseau host-gateway (prise en charge stable, délègue à win-bridge).

Ce plugin peut déléguer à l'un des plugins CNI de référence (win-overlay, win-bridge) afin de fonctionner avec le démon Flannel sous Windows (Flanneld), qui attribue automatiquement un bail de sous-réseau à chaque nœud et crée le réseau HNS. Le plugin lit son propre fichier de configuration (cni.conf) et le complète avec les variables d'environnement du fichier subnet.env généré par FlannelD. Il confie ensuite la mise en place du réseau à l'un des plugins CNI de référence, et transmet au plugin IPAM (par exemple host-local) la configuration adéquate, qui contient le sous-réseau attribué au nœud.

Pour les objets Node, Pod et Service, les flux réseau suivants sont pris en charge pour le trafic TCP/UDP :

  • Pod → Pod (IP)
  • Pod → Pod (nom)
  • Pod → Service (adresse IP de cluster)
  • Pod → Service (PQDN, uniquement s'il ne contient pas de « . »)
  • Pod → Service (FQDN)
  • Pod → externe (IP)
  • Pod → externe (DNS)
  • Nœud → Pod
  • Pod → Nœud

Gestion des adresses IP (IPAM)

Les options d'IPAM suivantes sont prises en charge sous Windows :

Direct Server Return (DSR)

Feature state: Stable since Kubernetes v1.34
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.34. It was first available in the v1.14 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate WinDSR, Kubernetes ignores it but does not report any error.

Mode d'équilibrage de charge dans lequel la correction des adresses IP et le LBNAT se font directement sur le port vSwitch du conteneur : le trafic du Service arrive avec l'adresse IP du pod d'origine comme adresse source. Cela améliore les performances : les réponses, qui repasseraient normalement par l'équilibreur de charge, peuvent le contourner et aller directement au client. La charge de l'équilibreur diminue, et la latence globale aussi. Pour en savoir plus, lisez Direct Server Return (DSR) in a nutshell.

Équilibrage de charge et Services

Un Service Kubernetes est une abstraction qui définit un ensemble logique de Pods et un moyen d'y accéder sur le réseau. Dans un cluster qui comprend des nœuds Windows, vous pouvez utiliser les types de Service suivants :

  • NodePort
  • ClusterIP
  • LoadBalancer
  • ExternalName

Le réseau des conteneurs Windows diffère du réseau Linux sur plusieurs points importants. La documentation Microsoft sur le réseau des conteneurs Windows donne plus de détails et de contexte.

Sous Windows, vous pouvez utiliser les réglages suivants pour configurer les Services et le comportement de l'équilibrage de charge :

Réglages des Services sous Windows
FonctionnalitéDescriptionVersion minimale de Windows prise en chargeComment l'activer
Affinité de sessionGarantit que les connexions d'un client donné sont toujours transmises au même Pod.Windows Server 2022Donnez à service.spec.sessionAffinity la valeur ClientIP
Direct Server Return (DSR)Voir la section DSR ci-dessus.Windows Server 2019Ajoutez l'argument de ligne de commande suivant (pour la version 1.37) : --enable-dsr=true
Preserve-DestinationN'applique pas de DNAT au trafic du Service, ce qui préserve l'adresse IP virtuelle du Service cible dans les paquets qui arrivent au Pod backend. Désactive aussi le transfert entre nœuds.Windows Server, version 1903Ajoutez "preserve-destination": "true" dans les annotations du Service et activez DSR dans kube-proxy.
Réseau en double pile IPv4/IPv6Communications natives IPv4 vers IPv4, en parallèle des communications IPv6 vers IPv6, vers le cluster, depuis le cluster et au sein du clusterWindows Server 2019Consultez Double pile IPv4/IPv6
Préservation de l'adresse IP du clientGarantit que l'adresse IP source du trafic entrant est préservée. Désactive aussi le transfert entre nœuds.Windows Server 2019Donnez à service.spec.externalTrafficPolicy la valeur Local et activez DSR dans kube-proxy

Limites

Les fonctionnalités réseau suivantes ne sont pas prises en charge sur les nœuds Windows :

  • le mode réseau de l'hôte (host networking) ;
  • l'accès local à un NodePort depuis le nœud lui-même (cela fonctionne depuis d'autres nœuds ou des clients externes) ;
  • plus de 64 pods backend (ou adresses de destination distinctes) pour un même Service ;
  • la communication IPv6 entre pods Windows connectés à des réseaux overlay ;
  • la politique de trafic Local hors mode DSR ;
  • les communications sortantes en ICMP via les plugins win-overlay ou win-bridge, ou via le plugin Azure-CNI. Plus précisément, le plan de données de Windows (VFP) ne prend pas en charge la transposition des paquets ICMP. Concrètement :
    • les paquets ICMP destinés au même réseau (par exemple un ping d'un pod à un autre) fonctionnent normalement ;
    • les paquets TCP/UDP fonctionnent normalement ;
    • les paquets ICMP qui doivent traverser un réseau distant (par exemple un ping d'un pod vers Internet) ne peuvent pas être transposés : ils ne sont donc pas réacheminés vers leur source ;
    • comme les paquets TCP/UDP peuvent, eux, être transposés, vous pouvez remplacer ping <destination> par curl <destination> pour diagnostiquer la connectivité avec l'extérieur.

Autres limites :

  • Les plugins réseau de référence de Windows, win-bridge et win-overlay, ne mettent pas en œuvre la spécification CNI v0.4.0, car l'opération CHECK n'y est pas implémentée.
  • Le plugin CNI Flannel VXLAN a les limites suivantes sous Windows :
    • La connectivité nœud-pod n'est possible qu'avec les pods locaux, à partir de Flannel v0.12.0.
    • Flannel est limité au VNI 4096 et au port UDP 4789. Consultez la documentation officielle du backend Flannel VXLAN pour plus de détails sur ces paramètres.

11 - Allocation des ClusterIP des Services

Dans Kubernetes, les Services sont un moyen abstrait d'exposer une application qui s'exécute sur un ensemble de Pods. Les Services peuvent disposer d'une adresse IP virtuelle à l'échelle du cluster (avec un Service de type: ClusterIP). Les clients peuvent se connecter à cette adresse IP virtuelle, et Kubernetes répartit alors le trafic destiné à ce Service entre les différents Pods sous-jacents.

Comment les ClusterIP des Services sont-elles allouées ?

Lorsque Kubernetes doit attribuer une adresse IP virtuelle à un Service, cette attribution se fait de l'une des deux manières suivantes :

dynamiquement
le plan de contrôle du cluster choisit automatiquement une adresse IP libre dans la plage configurée pour les Services de type: ClusterIP.
statiquement
vous indiquez l'adresse IP de votre choix, dans la plage configurée pour les Services.

Dans tout le cluster, chaque ClusterIP de Service doit être unique. Tenter de créer un Service avec une ClusterIP précise qui a déjà été allouée renvoie une erreur.

Pourquoi réserver des ClusterIP des Services ?

Il peut arriver que vous souhaitiez que des Services soient joignables à des adresses IP bien connues, afin que d'autres composants et utilisateurs du cluster puissent les utiliser.

Le meilleur exemple est le Service DNS du cluster. Par convention informelle, certains outils d'installation de Kubernetes attribuent la 10e adresse IP de la plage des Services au Service DNS. Si vous avez configuré votre cluster avec la plage d'adresses IP des Services 10.96.0.0/16 et que vous voulez que l'adresse IP de votre Service DNS soit 10.96.0.10, il vous faudrait créer un Service comme celui-ci :

apiVersion: v1
kind: Service
metadata:
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: CoreDNS
  name: kube-dns
  namespace: kube-system
spec:
  clusterIP: 10.96.0.10
  ports:
  - name: dns
    port: 53
    protocol: UDP
    targetPort: 53
  - name: dns-tcp
    port: 53
    protocol: TCP
    targetPort: 53
  selector:
    k8s-app: kube-dns
  type: ClusterIP

Mais, comme expliqué précédemment, l'adresse IP 10.96.0.10 n'a pas été réservée. Si d'autres Services sont créés avec une allocation dynamique avant lui ou en même temps que lui, l'un d'eux risque de se voir attribuer cette adresse IP. Vous ne pourrez alors pas créer le Service DNS, car sa création échouera avec une erreur de conflit.

Comment éviter les conflits de ClusterIP des Services ?

La stratégie d'allocation mise en œuvre dans Kubernetes pour attribuer des ClusterIP aux Services réduit le risque de collision.

La plage de ClusterIP est divisée selon la formule min(max(16, cidrSize / 16), 256), que l'on peut décrire ainsi : jamais moins de 16 ni plus de 256, avec une progression graduelle entre les deux.

L'attribution dynamique d'adresses IP puise par défaut dans la bande supérieure ; une fois celle-ci épuisée, elle puise dans la bande inférieure. Les utilisateurs peuvent ainsi recourir à des allocations statiques dans la bande inférieure avec un faible risque de collision.

Exemples

Exemple 1

Cet exemple utilise la plage d'adresses IP 10.96.0.0/24 (notation CIDR) pour les adresses IP des Services.

Taille de la plage : 28 - 2 = 254
Décalage de la bande : min(max(16, 256/16), 256) = min(16, 256) = 16
Début de la bande statique : 10.96.0.1
Fin de la bande statique : 10.96.0.16
Fin de la plage : 10.96.0.254

pie showData
    title 10.96.0.0/24
    "Statique" : 16
    "Dynamique" : 238

Exemple 2

Cet exemple utilise la plage d'adresses IP 10.96.0.0/20 (notation CIDR) pour les adresses IP des Services.

Taille de la plage : 212 - 2 = 4094
Décalage de la bande : min(max(16, 4096/16), 256) = min(256, 256) = 256
Début de la bande statique : 10.96.0.1
Fin de la bande statique : 10.96.1.0
Fin de la plage : 10.96.15.254

pie showData
    title 10.96.0.0/20
    "Statique" : 256
    "Dynamique" : 3838

Exemple 3

Cet exemple utilise la plage d'adresses IP 10.96.0.0/16 (notation CIDR) pour les adresses IP des Services.

Taille de la plage : 216 - 2 = 65534
Décalage de la bande : min(max(16, 65536/16), 256) = min(4096, 256) = 256
Début de la bande statique : 10.96.0.1
Fin de la bande statique : 10.96.1.0
Fin de la plage : 10.96.255.254

pie showData
    title 10.96.0.0/16
    "Statique" : 256
    "Dynamique" : 65278

A suivre

12 - Politique de trafic interne des Services

Si deux Pods de votre cluster veulent communiquer et qu'ils s'exécutent effectivement tous les deux sur le même nœud, utilisez la politique de trafic interne des Services pour que le trafic réseau reste sur ce nœud. Éviter un aller-retour par le réseau du cluster peut contribuer à la fiabilité, aux performances (latence et débit réseau) ou à la réduction des coûts.
Feature state: Stable since Kubernetes v1.26

La politique de trafic interne des Services (Service Internal Traffic Policy) permet de restreindre le trafic interne afin qu'il ne soit acheminé que vers les points de terminaison situés sur le nœud d'où provient ce trafic. Le trafic « interne » désigne ici le trafic émis par les Pods du cluster courant. Cela peut aider à réduire les coûts et à améliorer les performances.

Utiliser la politique de trafic interne des Services

Vous pouvez activer la politique de trafic « interne uniquement » pour un Service en définissant son champ .spec.internalTrafficPolicy sur Local. Cela indique à kube-proxy de n'utiliser que les points de terminaison locaux au nœud pour le trafic interne au cluster.

Note:

Pour les Pods situés sur des nœuds qui n'ont aucun point de terminaison pour un Service donné, le Service se comporte comme s'il n'avait aucun point de terminaison (pour les Pods de ce nœud), même s'il en possède bel et bien sur d'autres nœuds.

L'exemple suivant montre un Service dont le champ .spec.internalTrafficPolicy est défini sur Local :

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  internalTrafficPolicy: Local

Fonctionnement

kube-proxy filtre les points de terminaison vers lesquels il achemine le trafic en fonction du paramètre spec.internalTrafficPolicy. Lorsqu'il vaut Local, seuls les points de terminaison locaux au nœud sont pris en compte. Lorsqu'il vaut Cluster (la valeur par défaut) ou qu'il n'est pas défini, Kubernetes prend en compte tous les points de terminaison.

A suivre