Un Service Kubernetes existe en trois types, mais ce ne sont pas trois alternatives équivalentes entre lesquelles choisir librement : chaque type ajoute une couche d’exposition supplémentaire au-dessus du précédent, sans jamais le remplacer. Comprendre cet empilement évite de choisir un type par réflexe plutôt que par besoin réel.

ClusterIP : la base, invisible depuis l’extérieur

ClusterIP, le type par défaut, attribue une adresse IP virtuelle stable, routable uniquement depuis l’intérieur du cluster. Un pod peut joindre un autre service via cette IP (ou son nom DNS) sans jamais connaître l’adresse réelle des pods qui le composent, un mécanisme de découverte déjà couvert en détail. Rien dans ce type n’expose quoi que ce soit à l’extérieur du cluster : c’est la couche de base sur laquelle les deux autres types s’appuient.

apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  type: ClusterIP
  ports:
    - port: 8080

NodePort : ClusterIP, plus un port ouvert sur chaque nœud

NodePort crée d’abord un ClusterIP classique, puis ouvre en plus un port identique (dans une plage réservée, 30000-32767 par défaut) sur l’adresse IP de chaque nœud du cluster. N’importe quel nœud, joint sur ce port, route le trafic vers le service, peu importe où tournent réellement les pods concernés.

apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  type: NodePort
  ports:
    - port: 8080
      nodePort: 31000

Ce type expose un service à l’extérieur sans dépendre d’un load balancer externe, au prix d’une expérience peu pratique : le client doit connaître l’adresse d’un nœud spécifique et un port dans une plage haute, non standard, rarement adapté à un accès public direct.

LoadBalancer : NodePort, plus un load balancer cloud provisionné automatiquement

LoadBalancer va un cran plus loin : il crée le NodePort sous-jacent (lui-même construit sur un ClusterIP), puis demande au fournisseur cloud (ou à une implémentation comme MetalLB en environnement on-premise) de provisionner un load balancer externe qui distribue le trafic vers les NodePort de chaque nœud.

apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  type: LoadBalancer
  ports:
    - port: 80

Ce type offre l’expérience la plus proche d’une adresse publique stable et directement utilisable, au prix d’un load balancer par Service : dix Services de type LoadBalancer facturent dix load balancers cloud distincts, une réalité de coût qui explique pourquoi un Ingress ou une Gateway API s’intercale généralement devant plusieurs Services ClusterIP, plutôt que de multiplier les LoadBalancer.

Pourquoi l’empilement compte pour le diagnostic

Un problème de connectivité sur un Service LoadBalancer peut se situer à n’importe lequel des trois niveaux empilés : le load balancer externe lui-même, le NodePort sur les nœuds, ou le ClusterIP et le routage vers les pods. Diagnostiquer en partant du niveau le plus bas (ClusterIP fonctionne-t-il depuis l’intérieur du cluster ?) avant de remonter vers l’extérieur isole rapidement où la chaîne casse, plutôt que de supposer que le problème vient forcément du load balancer parce que c’est la couche la plus visible depuis l’extérieur.

# Vérifier le niveau le plus bas en premier :
# le ClusterIP répond-il depuis l'intérieur du cluster ?
kubectl run test --image=busybox -it --rm -- wget -O- backend:8080

À retenir

ClusterIP, NodePort et LoadBalancer ne sont pas trois choix équivalents mais trois couches empilées : NodePort inclut un ClusterIP, LoadBalancer inclut un NodePort. Chaque couche ajoutée résout un problème d’exposition supplémentaire à un coût croissant, LoadBalancer étant le plus coûteux à l’échelle (un load balancer externe par Service), ce qui explique pourquoi Ingress et Gateway API existent pour mutualiser cette exposition devant plusieurs Services. Diagnostiquer un problème de connectivité en partant du niveau le plus bas de cet empilement, plutôt que du plus visible, reste le réflexe le plus efficace, une base qui sous-tend toute décision réseau prise pendant une migration Kubernetes.