Kubernetes basique : Pod, Deployment et Service
1. Vue d’ensemble
Kubernetes dispose de dizaines de types de ressources, mais 80 % du travail quotidien tourne autour de trois concepts : Pod, Deployment, Service. Cet article vous aide à comprendre la nature de chaque ressource, comment elles s’articulent ensemble, et à rédiger un ensemble de manifestes YAML pour déployer une application qui fonctionne réellement sur un cluster.
Après la lecture, vous pourrez déployer vous-même une application Spring Boot sur Kubernetes, la redimensionner avec plusieurs répliques, et l’exposer à l’extérieur via un Service - tout cela avec juste kubectl et quelques fichiers YAML.
Remarque : Cet article suppose que vous disposez déjà d’un cluster (Minikube, Kind ou cloud) et que
kubectlpointe vers le bon contexte.
2. Pod - La plus petite unité de déploiement
2.1. Qu’est-ce qu’un Pod
Un Pod est un groupe d’un ou plusieurs conteneurs partageant un network namespace (même IP, même espace de ports) et des volumes. En pratique, la plupart des Pods ne contiennent qu’un seul conteneur d’application.
apiVersion: v1
kind: Pod
metadata:
name: order-service-pod
labels:
app: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
Explication :
metadata.labelsajoute l’étiquetteapp: order-service- c’est ainsi que Kubernetes « identifie » les Pods ; les Services et Deployments utilisent cette étiquette pour sélectionner les objets liés.resources.requestsest la ressource minimale que le scheduler doit avoir pour placer le Pod sur un nœud ;limitsest le plafond, au-delà duquel le Pod sera limité (CPU) ou tué (mémoire - OOMKilled).containerPort: 8080est simplement une déclaration informative, cela n’ouvre pas le port vers l’extérieur.
Appliquez et vérifiez :
kubectl apply -f order-service-pod.yaml
kubectl get pods
NAME READY STATUS RESTARTS AGE
order-service-pod 1/1 Running 0 5s
2.2. Pourquoi ne pas créer les Pods directement
Un Pod créé « nu » comme ci-dessus a un gros problème : si le nœud contenant le Pod meurt ou si le Pod s’arrête, personne ne le recréera automatiquement. Un Pod ne dispose pas de mécanisme d’auto-réparation ni de mise à l’échelle.
Remarque : En environnement de production, on ne crée presque jamais directement des Pods. Utilisez toujours un Deployment (ou StatefulSet, Job) pour gérer les Pods.
3. Deployment - Gérer le cycle de vie des Pods
3.1. Déclarer un Deployment
Un Deployment crée et gère un ensemble de Pods via une ReplicaSet, garantissant qu’il y a toujours le bon nombre de répliques en cours d’exécution, et supportant les mises à jour progressives (rolling updates) sans temps d’arrêt.
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
Explication :
replicas: 3demande au Deployment de maintenir constamment 3 Pods en cours d’exécution - si un Pod meurt, le Deployment crée automatiquement un nouveau Pod pour le remplacer.selector.matchLabelsdoit correspondre àtemplate.metadata.labels. C’est le lien obligatoire entre le Deployment et les Pods qu’il gère.readinessProbedétermine si le Pod reçoit le trafic ou non (utilisé par le Service) ;livenessProbedétermine si le Pod a besoin de redémarrer ou non. Confondre ces deux probes est une erreur très courante pour les débutants en Kubernetes.
Appliquez et observez :
kubectl apply -f order-service-deployment.yaml
kubectl get deployments
kubectl get pods -l app=order-service
NAME READY UP-TO-DATE AVAILABLE AGE
order-service 3/3 3 3 12s
NAME READY STATUS RESTARTS AGE
order-service-7d4f9c8b6d-2kx9p 1/1 Running 0 12s
order-service-7d4f9c8b6d-7h2mn 1/1 Running 0 12s
order-service-7d4f9c8b6d-xq1lw 1/1 Running 0 12s
3.2. Mise à l’échelle et mise à jour progressive
Augmentez ou diminuez les répliques sans modifier le YAML :
kubectl scale deployment order-service --replicas=5
Une mise à jour progressive (rolling update) lorsqu’on change l’image - le Deployment remplace automatiquement les anciens Pods par de nouveaux Pods selon une stratégie progressive, sans temps d’arrêt :
kubectl set image deployment/order-service order-service=nonladev/order-service:1.1
kubectl rollout status deployment/order-service
Waiting for deployment "order-service" rollout to finish: 2 out of 3 new replicas have been updated...
deployment "order-service" successfully rolled out
Si la nouvelle version a un bug, revenez immédiatement à la version précédente :
kubectl rollout undo deployment/order-service
3.3. Configurer la stratégie de mise à jour progressive
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
maxUnavailable: 1 permet à un maximum de 1 Pod de « disparaître » pendant la mise à jour ; maxSurge: 1 permet de créer un maximum de 1 Pod supplémentaire au-delà du nombre de répliques déclaré. Avec replicas: 3, le cluster aura tantôt 2-4 Pods pendant le processus de transition, mais jamais moins de 2.
4. Service - Exposer les Pods de manière stable
4.1. Pourquoi un Service est nécessaire
Les Pods ont leur propre IP, mais cette IP change chaque fois qu’un Pod est recréé (crash, mise à jour progressive, mise à l’échelle). Un Service fournit une adresse stable (ClusterIP + nom DNS) devant un groupe de Pods, équilibrant automatiquement le trafic en fonction du sélecteur d’étiquettes - peu importe combien de fois les Pods en dessous changent.
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Explication :
selector.app: order-servicecorrespond à l’étiquette sur les Pods (attachée par le Deployment viatemplate.metadata.labels) - le Service trouve et suit automatiquement ces Pods via un objet intermédiaire appelé Endpoints.port: 80est le port sur lequel le Service écoute à l’intérieur du cluster ;targetPort: 8080est le port que le conteneur expose réellement. Ces deux nombres ne doivent pas nécessairement être identiques.type: ClusterIP(par défaut) n’est accessible que depuis l’intérieur du cluster - convient aux services internes entre microservices.
Vérifiez le Service et les Endpoints :
kubectl apply -f order-service-service.yaml
kubectl get svc order-service
kubectl get endpoints order-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
order-service ClusterIP 10.96.142.201 <none> 80/TCP 4s
NAME ENDPOINTS AGE
order-service 10.244.1.5:8080,10.244.1.6:8080,+1 more 4s
4.2. Les différents types de Service
| Type | Portée d’accès | Quand l’utiliser |
|---|---|---|
ClusterIP |
Uniquement au sein du cluster | Communication entre microservices |
NodePort |
Port fixe (30000-32767) ouvert sur tous les nœuds | Tests rapides, environnement dev/on-prem |
LoadBalancer |
IP publique fournie par le fournisseur cloud | Exposer le service sur Internet en cloud |
Exemple d’exposition externe pour test rapide sur Minikube :
apiVersion: v1
kind: Service
metadata:
name: order-service-external
spec:
selector:
app: order-service
ports:
- port: 80
targetPort: 8080
nodePort: 30080
type: NodePort
minikube service order-service-external --url
Remarque : Utiliser
NodePorten production est une anti-pattern - plage de ports limitée, pas de terminaison TLS ni de routage basé sur la santé comme avec Ingress. Pour la production en cloud, préférezLoadBalancerou un Ingress Controller.
5. Combiner les trois dans une application réelle
L’ensemble du pipeline : le client appelle le Service → le Service équilibre la charge vers les Pods gérés par le Deployment.
Client
│
▼
Service (order-service, ClusterIP 10.96.142.201:80)
│ (label selector: app=order-service)
▼
┌─────────────┬─────────────┬─────────────┐
│ Pod #1 │ Pod #2 │ Pod #3 │
│ :8080 │ :8080 │ :8080 │
└─────────────┴─────────────┴─────────────┘
▲ géré par
Deployment (order-service, replicas: 3)
Pour faciliter le déploiement en une seule fois, vous pouvez combiner le Deployment et le Service dans un seul fichier, séparés par --- :
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 80
targetPort: 8080
kubectl apply -f order-service-all.yaml
6. Écrire des tests pour l’endpoint de readiness avant le déploiement
Avant que les probes de Kubernetes ne s’appuient sur /actuator/health/readiness, vous devez vous assurer que cet endpoint retourne le bon état dans votre code Spring Boot.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management.endpoint.health.probes.enabled=true
management.health.readiness-state.enabled=true
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class OrderServiceHealthTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void readinessProbe_shouldReturnUp() {
ResponseEntity<String> response =
restTemplate.getForEntity("/actuator/health/readiness", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).contains("\"status\":\"UP\"");
}
}
Ce test appelle TestRestTemplate sur l’endpoint readiness réel, confirmant HTTP 200 et que le corps contient UP. C’est précisément l’endpoint que le readinessProbe dans le Deployment appellera périodiquement - si le test échoue ici, le Pod ne sera jamais « Ready » sur le cluster.
7. Erreurs courantes
- Le sélecteur ne correspond pas à l’étiquette : le Service ou le Deployment ne trouve aucun Pod (Endpoints vides) parce que
selectoretlabelssont mal écrits ou décalés. Utilisez toujourskubectl get endpoints <svc-name>pour vérifier. - Absence de readinessProbe : le Service achemine toujours le trafic vers les Pods qui n’ont pas fini de démarrer l’application, causant des erreurs 502/503 juste après le déploiement.
- Resource requests/limits manquantes : les Pods peuvent être placés sur le mauvais nœud par le scheduler, ou être tués par OOMKilled de manière inattendue quand le trafic augmente.
- Utiliser un Pod nu en production : pas d’auto-réparation, perdre un Pod c’est le perdre définitivement, il faut le recréer manuellement.
- Tag d’image
latest: la mise à jour progressive ne détecte pas le changement, Kubernetes pense que l’image ne change pas et ne la tire pas à nouveau. Utilisez toujours une étiquette de version spécifique.
8. Conclusion
Un Pod est la plus petite unité pour exécuter un conteneur, un Deployment gère le cycle de vie et la mise à l’échelle d’un ensemble de Pods, et un Service fournit une adresse stable pour y accéder même si les Pods en dessous changent continuellement. Maîtriser ces trois piliers est la base pour progresser vers Ingress, ConfigMap/Secret, ou les charts Helm par la suite. L’étape suivante devrait être : déployer une application Spring Boot réelle, observer la mise à jour progressive, et essayer de tuer manuellement un Pod pour voir comment le Deployment se remet automatiquement de l’erreur.