infra / kubernetes
Services : ClusterIP, NodePort, LoadBalancer
Explication
Ce que vous allez apprendre
- Expliquer pourquoi les Pods ont besoin d'une adresse stable via un Service, leur IP n'étant jamais fiable
- Décrire comment un Service découvre dynamiquement ses Pods cibles via labels/selector, comme un Deployment
- Distinguer les trois types de Service (ClusterIP, NodePort, LoadBalancer) et choisir le bon selon le besoin d'exposition
- Diagnostiquer un Service qui ne route aucun trafic en vérifiant ses endpoints
- Relier le fonctionnement d'un Service à celui de
kube-proxyvu à la leçon 1
Dans quel contexte ?
Une équipe backend déploie un microservice de paiement derrière un Service ClusterIP, appelé par le frontend via son nom DNS interne payment-service. Après une mise à jour qui recrée tous les Pods du service de paiement, le frontend continue de fonctionner sans aucune modification de configuration : le Service a automatiquement mis à jour la liste de ses endpoints vers les nouveaux Pods, et le nom DNS n'a jamais changé. C'est exactement ce découplage entre "où sont les Pods" et "comment les joindre" qui rend Kubernetes utilisable en production.
Le problème posé par une IP qui change
D'abord, un fait à connaître : un Pod recréé (après un crash, une mise à jour, un déplacement) obtient une NOUVELLE adresse IP à chaque fois.
Prérequis
Cette leçon suppose acquis le mécanisme de labels/selector vu avec les Deployments (leçon 4) : un Service l'utilise exactement de la même façon pour trouver ses Pods.
Ce que ça casserait sans solution
Si ton frontend appelait directement l'IP d'un Pod backend, il perdrait la connexion à chaque redémarrage de ce Pod. Il faut un point d'accès qui ne change jamais, même quand les Pods derrière lui vont et viennent.
La solution : un objet dédié à la stabilité
C'est exactement le rôle du Service. Comme le Deployment, il utilise un selector basé sur des labels pour trouver ses Pods, plutôt que de connaître leurs noms un par un.
Un mécanisme totalement automatique
Tous les Pods portant le bon label sont ajoutés ou retirés dynamiquement de la liste des cibles (les "endpoints") au fur et à mesure qu'ils apparaissent ou disparaissent. Le Service n'a même pas besoin de savoir comment ces Pods ont été créés.
Trois portées à bien distinguer
Une fois ce mécanisme compris, il reste à choisir la bonne portée. ClusterIP, le type par défaut, n'est joignable que depuis l'intérieur du cluster : c'est le bon choix pour la communication entre microservices internes.
| Type de Service | Portée | Cas d'usage |
|---|---|---|
| ClusterIP (défaut) | Interne au cluster uniquement | Communication entre microservices |
| NodePort | Port fixe ouvert sur chaque node | Test, démo, rarement en production |
| LoadBalancer | IP publique via le fournisseur cloud | Exposition externe en production |
Deux autres portées pour sortir du cluster
NodePort ouvre un port fixe sur chaque machine du cluster, pratique pour du test ou une démo, mais rarement utilisé tel quel en production. LoadBalancer demande au fournisseur cloud de provisionner un vrai équilibreur de charge avec une IP publique : c'est la solution d'exposition externe classique en production.
Le piège à vérifier systématiquement
Le piège le plus fréquent est un selector qui ne correspond à aucun Pod, à cause d'une faute de frappe dans un label. Le Service existe, semble fonctionner, mais ne route de trafic vers personne.
Piège fréquent
Un selector qui ne correspond à aucun Pod, souvent à cause d'une faute de frappe dans un label, crée un Service qui existe et semble fonctionner mais ne route de trafic vers personne. kubectl get endpoints <service> révèle immédiatement le problème : une liste vide signale un selector défaillant.
Le réflexe pour éviter ce piège
Vérifie toujours concrètement quels Pods sont réellement ciblés avec kubectl get endpoints, plutôt que de faire confiance uniquement à la définition du manifeste.
Maintenant que le réseau est stable, la leçon suivante s'attaque à un autre problème classique : séparer le code de sa configuration, avec les ConfigMaps.
Commandes & code
Services
Les Pods ont des IP éphémères. Un Service donne un point d'accès stable devant un groupe de Pods (via selector).
# service-clusterip.yaml - accessible uniquement DEPUIS le cluster (défaut)
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
type: ClusterIP
selector:
app: api # cible tous les Pods portant ce label
ports:
- port: 80 # port exposé par le Service
targetPort: 3000 # port réel du conteneur# service-nodeport.yaml - accessible depuis l'extérieur via <IP_NODE>:<nodePort>
apiVersion: v1
kind: Service
metadata:
name: api-nodeport
spec:
type: NodePort
selector:
app: api
ports:
- port: 80
targetPort: 3000
nodePort: 30080 # doit être dans la plage 30000-32767# service-loadbalancer.yaml - provisionne un load balancer chez le cloud provider
apiVersion: v1
kind: Service
metadata:
name: api-lb
spec:
type: LoadBalancer
selector:
app: api
ports:
- port: 443
targetPort: 3000kubectl apply -f service-clusterip.yaml
kubectl get svc
kubectl describe svc api-service
# Résolution DNS interne : "api-service.<namespace>.svc.cluster.local"
kubectl run debug --image=busybox -it --rm -- nslookup api-service
# Vérifier quels Pods sont réellement ciblés par le Service
kubectl get endpoints api-service| Type | Portée | Cas d'usage |
|---|---|---|
ClusterIP | Interne au cluster (défaut) | Communication service-à-service |
NodePort | IP de chaque node + port fixe | Dev/démo, sans load balancer cloud |
LoadBalancer | IP publique externe | Exposition prod (nécessite un cloud provider) |
ExternalName | Alias DNS vers un service externe | Intégrer une base de données externe |
Résumé
- Un Service route le trafic vers les Pods via
selector, jamais vers une IP de Pod en dur. ClusterIP= interne,NodePort= accès externe simple,LoadBalancer= prod cloud.kubectl get endpointsrévèle vite unselectormal configuré (0 endpoint = 0 Pod ciblé).- Le DNS interne suit
<service>.<namespace>.svc.cluster.local.
Exercices pratiques
Mission : le service de paiement injoignable
Objectif : Diagnostiquer un Service qui existe mais ne route aucun trafic, en vérifiant ses endpoints réels plutôt que sa seule définition YAML.
Contexte
Le frontend appelle payment-service via son nom DNS interne et reçoit systématiquement une erreur de connexion, alors que kubectl get svc montre que le Service existe bel et bien avec une ClusterIP assignée. Les Pods du Deployment payment-deployment portent le label app: payment, mais quelqu'un a écrit le Service il y a plusieurs semaines, avant un renommage d'équipe.