
Un Service Kubernetes permet d'exposer un ou plusieurs Pods via une adresse IP stable et un nom DNS interne. Comme les Pods sont éphémères et changent d'adresse IP, le Service fournit un point d'accès durable pour la communication réseau entre applications ou vers l'extérieur du cluster. Ce guide vous montre comment créer des Services, choisir le bon type (ClusterIP, NodePort, LoadBalancer) et diagnostiquer les problèmes de connectivité.
Prérequis : un cluster Kubernetes fonctionnel avec au moins un Deployment ou des Pods.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Créer un Service avec kubectl expose et avec un fichier YAML
- Choisir le bon type : ClusterIP, NodePort, LoadBalancer, Headless
- Comprendre la résolution DNS entre Pods et Services
- Utiliser les labels/selectors pour cibler les bons Pods
- Diagnostiquer quand un Service ne fonctionne pas
Ce qu'est un Service Kubernetes
Section intitulée « Ce qu'est un Service Kubernetes »Un Service est une abstraction réseau qui expose un groupe de Pods via une adresse IP stable et un nom DNS.
Pour un débutant, retenez simplement :
- Les Pods changent d'IP à chaque recréation
- Le Service garde une IP fixe qui redirige vers les Pods
- Les autres Pods appellent le Service par son nom, pas par IP
Pourquoi utiliser des Services
Section intitulée « Pourquoi utiliser des Services »Sans Services, chaque Pod devrait connaître l'adresse IP des autres Pods, une adresse qui change à chaque redémarrage.
Les Services résolvent ce problème en fournissant :
| Fonctionnalité | Description |
|---|---|
| IP stable | Une adresse fixe qui ne change pas quand les Pods sont recréés |
| Nom DNS | Un nom comme backend-service au lieu d'une IP |
| Load balancing | Répartition automatique du trafic entre plusieurs Pods |
| Découverte | Les Pods trouvent les autres services par leur nom |
Exemple concret
Section intitulée « Exemple concret »Une application web avec :
- Un frontend (React)
- Un backend (API Node.js)
- Une base de données (PostgreSQL)
Le frontend appelle backend-service:8080 au lieu de chercher l'adresse du Pod backend. Si ce Pod redémarre avec une nouvelle adresse, le Service continue de rediriger, sans que rien ne change côté appelant.
Créer son premier Service
Section intitulée « Créer son premier Service »Deux chemins mènent au même Service. La méthode impérative tient en une commande et convient pour exposer rapidement un Deployment existant. La méthode déclarative passe par un fichier YAML que vous versionnez, et c'est celle que vous emploierez en équipe. Commencez par la première pour voir un Service fonctionner, la seconde arrive juste après.
Méthode impérative : kubectl expose
Section intitulée « Méthode impérative : kubectl expose »La façon la plus rapide de créer un Service pour un Deployment existant :
kubectl expose deployment nginx --name=nginx-service --port=80 --target-port=80Résultat : un Service ClusterIP nommé nginx-service qui expose le port 80.
Vérifiez sa création :
kubectl get svc nginx-serviceNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEnginx-service ClusterIP 10.96.243.54 <none> 80/TCP 5sMéthode déclarative : fichier YAML
Section intitulée « Méthode déclarative : fichier YAML »Pour un contrôle total, créez un fichier manifest :
apiVersion: v1kind: Servicemetadata: name: nginx-servicespec: selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80 type: ClusterIPLes champs essentiels :
| Champ | Description |
|---|---|
selector | Labels des Pods à cibler (ici app: nginx) |
port | Port exposé par le Service |
targetPort | Port sur lequel écoutent les Pods |
type | Type de Service (ClusterIP par défaut) |
Appliquez-le :
kubectl apply -f nginx-service.yamlLe champ selector est celui qui décide de tout : il doit correspondre aux libellés de vos Pods. Un Service dont le selector ne trouve personne est créé sans la moindre erreur, répond à son nom DNS, et ne route vers aucune destination.
Observer et inspecter un Service
Section intitulée « Observer et inspecter un Service »Une fois votre Service créé, vérifiez qu'il fonctionne.
kubectl get svc : vue d'ensemble
Section intitulée « kubectl get svc : vue d'ensemble »C'est la première commande à lancer après une création. Elle dit en une ligne quel type de Service vous avez obtenu, quelle adresse de cluster lui a été attribuée, et, avec -o wide, quel selector il utilise pour trouver ses Pods.
kubectl get svc -o wideNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTORnginx-service ClusterIP 10.96.243.54 <none> 80/TCP 2m app=nginxLes colonnes importantes :
| Colonne | Signification |
|---|---|
| TYPE | ClusterIP, NodePort, LoadBalancer... |
| CLUSTER-IP | Adresse IP interne du Service |
| PORT(S) | Ports exposés |
| SELECTOR | Labels utilisés pour trouver les Pods |
kubectl describe : détails complets
Section intitulée « kubectl describe : détails complets »kubectl describe svc nginx-serviceCette commande montre :
- Endpoints : IPs des Pods ciblés par le Service
- Session Affinity : sticky sessions ou non
- Events : historique (rarement utile pour les Services)
Name: nginx-serviceNamespace: defaultSelector: app=nginxType: ClusterIPIP: 10.96.243.54Port: <unset> 80/TCPTargetPort: 80/TCPEndpoints: 10.244.1.32:80,10.244.1.33:80,10.244.1.34:80Session Affinity: NoneLa ligne Endpoints est la seule qui dise si le Service sert à quelque chose. Vide, le Service existe, répond à son nom DNS, et ne route vers personne. Kubernetes ne signale aucune erreur : de son point de vue, c'est un état parfaitement valide.
Deux causes très différentes produisent la même ligne vide, et confondre les deux fait perdre beaucoup de temps :
| Cause | Ce qui se passe | Comment la reconnaître |
|---|---|---|
| Selector sans correspondance | aucun Pod ne porte les libellés attendus | kubectl get pods --show-labels ne montre aucun Pod avec ces libellés |
Pods présents mais pas Ready | les Pods sont bien sélectionnés, mais leur readiness probe échoue | kubectl get pods affiche 0/1 Running |
La seconde est de loin la plus fréquente en production, et c'est précisément celle que describe ne vous dit pas.
Voir les Pods réellement ciblés
Section intitulée « Voir les Pods réellement ciblés »La liste des Pods derrière un Service vit dans un objet EndpointSlice. C'est lui qu'il faut interroger :
kubectl get endpointslice -l kubernetes.io/service-name=nginx-serviceNAME ADDRESSTYPE PORTS ENDPOINTS AGEnginx-service-mf9f8 IPv4 80 10.244.1.32,10.244.1.33,10.244.1.34 5mChaque adresse correspond à un Pod qui recevra du trafic. Le suffixe aléatoire du nom sera différent chez vous.
Un Pod qui n'est pas Ready ne reçoit aucun trafic
Section intitulée « Un Pod qui n'est pas Ready ne reçoit aucun trafic »C'est la seconde cause, et le mécanisme que beaucoup découvrent en plein
incident : en fonctionnement normal, un Service ne route que vers les Pods Ready. Un Pod peut être
Running, son conteneur tourner parfaitement, et ne recevoir aucune requête
parce que sa readiness probe échoue.
kubectl get pods -l app=webNAME READY STATUS RESTARTS AGEweb-686dd5f4b7-d7z6q 0/1 Running 0 31sweb-686dd5f4b7-gwznn 0/1 Running 0 31sLe 0/1 de la colonne READY est le signal : les Pods existent, ils tournent,
et ils ne sont pas prêts. Le Service les écarte, sa ligne Endpoints est vide,
et rien n'explique pourquoi. C'est en regardant les Pods, pas le Service,
que le diagnostic se fait.
Dernière remarque de vocabulaire : vous verrez souvent kubectl get endpoints
dans les tutoriels. La commande fonctionne encore, mais elle vous répond
v1 Endpoints is deprecated in v1.33+. Prenez l'habitude d'endpointslice
dès maintenant.
Types de Services
Section intitulée « Types de Services »Kubernetes propose quatre types de Services selon vos besoins d'exposition.
ClusterIP : communication interne (défaut)
Section intitulée « ClusterIP : communication interne (défaut) »Le Service est accessible uniquement depuis l'intérieur du cluster.
spec: type: ClusterIP # ou omettez, c'est le défautCas d'usage : communication entre microservices (backend ↔ base de données).
ClusterIP est le type que vous emploierez le plus. C'est le défaut, et c'est le bon choix pour tout ce qui ne doit pas être joignable depuis l'extérieur du cluster : une API interne, une base de données, un cache.
NodePort : exposition sur chaque nœud
Section intitulée « NodePort : exposition sur chaque nœud »Le Service est accessible via un port sur chaque nœud du cluster.
spec: type: NodePort ports: - port: 80 targetPort: 80 nodePort: 30080 # optionnel, entre 30000-32767Accès : http://<IP-du-noeud>:30080
Cas d'usage : tests locaux, environnements sans cloud provider.
Le nodePort est alloué à l'échelle du cluster, pas nœud par nœud : deux Services ne peuvent pas demander le même numéro. La création du second échoue explicitement :
The Service "np2" is invalid: spec.ports[0].nodePort: Invalid value: 30099:provided port is already allocatedOmettre nodePort laisse Kubernetes en choisir un libre dans la plage 30000-32767, et supprime entièrement la question.
LoadBalancer : exposition via cloud provider
Section intitulée « LoadBalancer : exposition via cloud provider »Le Service demande un équilibreur de charge externe au cloud provider (AWS, GCP, Azure...).
spec: type: LoadBalancer ports: - port: 80 targetPort: 80Accès : via l'IP externe fournie par le cloud (EXTERNAL-IP).
Cas d'usage : applications en production sur le cloud.
kubectl get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEnginx-service LoadBalancer 10.96.243.54 203.0.113.50 80:32117/TCP 2mQuel type choisir ?
Section intitulée « Quel type choisir ? »La question se tranche sur une seule ligne : qui doit joindre ce Service ? Un appelant à l'intérieur du cluster n'a jamais besoin de plus qu'un ClusterIP. Dès que l'appel vient de l'extérieur, le choix dépend de l'endroit où tourne le cluster.
| Besoin | Type recommandé |
|---|---|
| Communication interne | ClusterIP |
| Test local sans cloud | NodePort |
| Production sur cloud | LoadBalancer |
Cas particulier : ExternalName
Section intitulée « Cas particulier : ExternalName »Le type ExternalName est à part : il ne sélectionne pas de Pods et ne fait pas de proxy. Il redirige simplement vers un nom DNS externe.
spec: type: ExternalName externalName: database.example.comCas d'usage : pointer vers une base de données managée (RDS, Cloud SQL...). Les Pods appellent external-db et sont redirigés vers database.example.com.
Cas particulier : Headless Services
Section intitulée « Cas particulier : Headless Services »Un Headless Service (clusterIP: None) ne possède pas d'IP de cluster. Le DNS retourne directement les IPs des Pods au lieu de l'IP du Service.
spec: clusterIP: None selector: app: nginx| Service normal | Headless Service |
|---|---|
| DNS → IP du Service | DNS → IPs des Pods |
| Load balancing par kube-proxy | Client choisit le Pod |
Cas d'usage : bases de données stateful, StatefulSets avec identité réseau stable. Voir le guide StatefulSets pour plus de détails.
Résolution DNS
Section intitulée « Résolution DNS »Kubernetes crée automatiquement des enregistrements DNS pour chaque Service.
Format du nom DNS
Section intitulée « Format du nom DNS »Chaque Service reçoit un nom complet et prévisible, construit à partir de son nom et de son namespace. C'est ce nom que CoreDNS résout, et c'est celui dont dérivent toutes les formes abrégées.
<service-name>.<namespace>.svc.cluster.localExemples
Section intitulée « Exemples »En pratique vous écrirez rarement le nom complet. Depuis un Pod, le nom court suffit tant que le Service est dans le même namespace : le suffixe .<namespace> ne devient nécessaire que pour franchir une frontière de namespace, et le FQDN presque jamais.
| Appel depuis | Nom à utiliser |
|---|---|
| Même namespace | nginx-service |
| Autre namespace | nginx-service.production |
| Nom complet (FQDN) | nginx-service.production.svc.cluster.local |
Tester la résolution DNS
Section intitulée « Tester la résolution DNS »Un piège attend ici tous les débutants, et mieux vaut le connaître avant de le rencontrer. Lançons un Pod outil, puis interrogeons le nom court.
kubectl run outil --image=busybox:1.37@sha256:9db7b59979c38555a39def84a31fb98b5296952f9e3afd4f6f11f05b07adfab0 --restart=Never --command -- sleep 300kubectl exec outil -- nslookup webServer: 10.96.0.10Address: 10.96.0.10:53
Name: web.default.svc.cluster.localAddress: 10.96.195.43
** server can't find web.svc.cluster.local: NXDOMAIN** server can't find web.cluster.local: NXDOMAINLa résolution a réussi, la quatrième ligne le prouve. Mais nslookup essaie ensuite tous les suffixes de la liste de recherche, signale chaque échec, et sort en code 1. Ce mur de NXDOMAIN n'est pas une panne, c'est le fonctionnement normal de l'outil, et il fait croire à un cluster cassé.
Ce qui explique ce comportement vit dans le Pod :
kubectl exec outil -- cat /etc/resolv.confsearch default.svc.cluster.local svc.cluster.local cluster.localnameserver 10.96.0.10options ndots:510.96.0.10 est l'adresse du Service kube-dns, dans le namespace kube-system : c'est le résolveur interne du cluster. La ligne search énumère les suffixes essayés, dans l'ordre.
Une application, elle, ne voit rien de tout cela : la bibliothèque C s'arrête au premier suffixe qui répond. Depuis le même Pod :
kubectl exec outil -- wget -qO- http://webLa page arrive. Testez donc la connectivité avec l'outil que votre application utilise, wget ou curl, plutôt qu'avec nslookup, qui rapporte des échecs sans conséquence.
Utilisez toujours le nom du Service, jamais son adresse de cluster. Le nom reste valide quand l'IP change, et c'est précisément ce que le Service apporte.
Option avancée : sessionAffinity
Section intitulée « Option avancée : sessionAffinity »Par défaut, kube-proxy répartit le trafic aléatoirement. Pour garder un client sur le même Pod, ajoutez sessionAffinity: ClientIP :
spec: sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 600Cas d'usage : websockets, sessions en mémoire. Attention : ne fonctionne pas bien derrière un proxy qui masque l'IP client.
Service ou Ingress ?
Section intitulée « Service ou Ingress ? »Une question fréquente : quand utiliser un Service, quand utiliser un Ingress ?
| Service | Ingress | |
|---|---|---|
| Couche | L4 (TCP/UDP) | L7 (HTTP/HTTPS) |
| Routage | Par IP/port | Par path, host, headers |
| TLS | Non | Oui (terminaison) |
| Usage | Communication interne ou exposition simple | API publique, sites web |
Règle simple : utilisez un Service pour la communication interne ou l'exposition basique, un Ingress pour du routage HTTP avancé.
Ce qu'un Service ne fait PAS
Section intitulée « Ce qu'un Service ne fait PAS »Un Service Kubernetes est un outil Layer 4 (TCP/UDP). Il a des limitations :
| Besoin | Solution |
|---|---|
| Routage HTTP (path, host) | Ingress |
| Terminaison TLS | Ingress + cert-manager |
| Filtrage de sécurité | NetworkPolicies |
| Rate limiting, circuit breaker | Service mesh |
Un point mérite d'être dit sans détour : un Service n'offre aucune sécurité. Tout Pod du cluster peut appeler n'importe quel Service, dans n'importe quel namespace. Restreindre ces flux relève des NetworkPolicies, et de rien d'autre.
Debug : diagnostiquer un Service
Section intitulée « Debug : diagnostiquer un Service »Quand un Service ne répond pas, quatre vérifications suffisent presque toujours, et elles se font dans cet ordre. Les trois premières partent du Service et descendent vers les Pods ; la quatrième change de point de vue et teste depuis l'intérieur du cluster.
1. Aucun endpoint
Section intitulée « 1. Aucun endpoint »Symptôme : Endpoints: <none> dans kubectl describe svc.
# Vérifier les labels des Podskubectl get pods --show-labels
# Comparer avec le selector du Servicekubectl get svc nginx-service -o jsonpath='{.spec.selector}'Deux causes possibles, à écarter dans cet ordre : le selector ne correspond à aucun libellé, ou bien les Pods sont sélectionnés mais pas Ready. L'EndpointSlice tranche en une commande, là où describe reste muet.
2. TargetPort incorrect
Section intitulée « 2. TargetPort incorrect »Symptôme : curl nginx-service timeout ou connexion refusée.
kubectl describe svc nginx-service | grep TargetPortkubectl exec -it nginx-pod -- netstat -tlnpCause : le targetPort ne correspond pas au port de l'application.
3. DNS défaillant
Section intitulée « 3. DNS défaillant »Symptôme : le nom du Service ne se résout plus du tout, y compris en FQDN. Avant de conclure, écartez le faux positif vu plus haut : des lignes NXDOMAIN après une résolution réussie sont normales. Un vrai échec DNS ne renvoie aucune adresse.
kubectl -n kube-system get pods -l k8s-app=kube-dnskubectl run dns-test --image=busybox:1.37@sha256:9db7b59979c38555a39def84a31fb98b5296952f9e3afd4f6f11f05b07adfab0 --rm -it --restart=Never -- nslookup kubernetes4. Test depuis un Pod de debug
Section intitulée « 4. Test depuis un Pod de debug »Les trois vérifications précédentes partent de l'API. Celle-ci se place du point de vue d'un Pod, c'est-à-dire exactement là où votre application échoue. Une image outillée évite de découvrir que le conteneur applicatif n'a ni curl ni dig.
kubectl run debug --image=nicolaka/netshoot:v0.16@sha256:b09d9b21381f47a79b3cbcb30da25266dc17186ea00ae65e99fdc51396f48e70 --rm -it --restart=Never -- bashDepuis ce Pod : curl, nslookup, dig, tcpdump. Commencez par curl http://<service> : si la page arrive, le problème n'est pas réseau.
Testez vos connaissances
Section intitulée « Testez vos connaissances »Sept questions pour vérifier que l'essentiel est acquis : choix du type, rôle du selector, lecture des EndpointSlices et diagnostic d'un Service qui ne répond pas.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »- Un Service donne une IP stable et un nom DNS à un groupe de Pods
- Le selector doit correspondre aux labels des Pods (sinon pas d'endpoints)
- ClusterIP (défaut) : communication interne uniquement
- NodePort : exposition sur un port de chaque nœud (30000-32767)
- LoadBalancer : demande un équilibreur externe au cloud provider
- Headless (
clusterIP: None) : DNS retourne directement les IPs des Pods - EndpointSlices : API moderne qui stocke les adresses des Pods
- Un Service ne fait pas de routage HTTP, TLS ou filtrage sécurité, utilisez Ingress et NetworkPolicies
FAQ : questions fréquentes sur les Services
Section intitulée « FAQ : questions fréquentes sur les Services »Ces questions reviennent souvent quand on connecte des applications dans Kubernetes. Les réponses ci-dessous reprennent les points clés du guide, types de Services, choix entre ClusterIP, NodePort et LoadBalancer, diagnostic des endpoints manquants.
- ClusterIP (défaut) : joignable uniquement à l'intérieur du cluster.
- NodePort : ouvre un port identique sur chaque nœud, accessible depuis l'extérieur.
- LoadBalancer : demande au cloud un équilibreur avec une IP publique.
- ExternalName : crée un simple alias DNS vers un service externe.
clusterIP: None) expose directement les IP des Pods.- ClusterIP reste interne : idéal pour la communication entre microservices.
- NodePort ouvre le même port sur tous les nœuds (plage 30000-32767), pratique en test, rarement en production directe.
- LoadBalancer provisionne un équilibreur géré par le cloud avec une IP publique stable.
kubectl get endpoints mon-service ne renvoie aucune adresse, c'est presque toujours que le selector et les labels diffèrent (faute de frappe, casse, clé manquante). Vérifiez aussi qu'au moins un Pod cible est en état Ready : un Pod non prêt est exclu des endpoints.clusterIP: None. Contrairement à un Service classique, il n'expose aucune IP virtuelle et ne fait aucun équilibrage. À la place, sa résolution DNS renvoie directement les adresses IP de chaque Pod. C'est ce dont ont besoin les applications avec état, comme les bases de données répliquées, où le client doit joindre une instance précise.nom-service.namespace.svc.cluster.local. Un Pod du même namespace peut se contenter du nom court (nom-service). Depuis un autre namespace, il faut ajouter le namespace (nom-service.autre-namespace). C'est CoreDNS, le serveur DNS du cluster, qui assure cette résolution.Mettre en pratique
Section intitulée « Mettre en pratique »Du premier Service à la panne. Vous exposez d'abord trois replicas par un ClusterIP et prouvez la répartition réelle entre les Pods, puis vous reprenez un Service qui ne dessert plus rien, dont le selector, le port et une politique réseau sont tous les trois en cause. Le second lab est le dépannage de ce que le premier vient de construire, pas un sujet voisin.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Les Ingress : L'exposition HTTP et HTTPS de vos Services vers l'extérieur du cluster.
- Network Policies : Le filtrage du trafic qui atteint les Pods derrière vos Services.
- Pod Networking : Le chemin réseau réel emprunté par une requête envoyée à une ClusterIP.