Ce module vous apprend les concepts essentiels pour exposer et faire communiquer vos applications dans Kubernetes. Nous couvrons uniquement les éléments indispensables pour démarrer.
Imaginez que vous déployez une application web avec 3 répliques. Chaque Pod a sa propre adresse IP :
# Vos Pods d'application
Pod 1: IP 10.244.1.10
Pod 2: IP 10.244.1.11
Pod 3: IP 10.244.1.12
Problèmes :
Un Service fournit :
Kubernetes implémente un modèle de réseau "plat" avec quelques règles simples :
Container to container est essentiellement dans un pod.
Les PODs peuvent contenir un groupe de containers avec la même IP. La communication entre les container dans un pods passent alors par localhost :

Exemple:
apiVersion: v1
kind: Pod
metadata:
name: multi-container-pod
spec:
containers:
- name: nginx-container
image: nginx:1.29.5
ports:
- containerPort: 80
- name: busybox-container
image: radial/busyboxplus:curl
command:
- "sh"
- "-c"
- "while true; do echo 'BusyBox is running'; sleep 10; done"
Création du pod avec 2 containers :
kubectl create -f multi-container.yml
exec dans le container busybox-container afin de tester la connectivité avec le container nginx-container :
$ kubectl exec -it multi-container-pod -c busybox-container -- ash
[ root@multi-container-pod:/ ]$ ls
bin/ dev/ etc/ home/ lib/ lib64 linuxrc media/ mnt/ opt/ proc/ root/ run sbin/ sys/ tmp/ usr/ var/
[ root@multi-container-pod:/ ]$ curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

Si l'on regarde sur un noeud via un ip a, on aura plusieurs eth<id> qui seront reliés à un bridge.
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:23:76:96:b3 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
3: kube-bridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether b2:10:71:b8:46:7f brd ff:ff:ff:ff:ff:ff
98248: eth0@if98249: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 0a:ac:68:ee:b7:b4 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.0.13/23 scope global eth0
valid_lft forever preferred_lft forever
98252: eth1@if98253: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:10 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.16/16 scope global eth1
valid_lft forever preferred_lft forever
Afin de tester le réseau, nous allons créer un déploiement (nous verrons ça un peu plus tard) qui exécute 2 pods nginx.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.29.5
ports:
- containerPort: 80
Comme tout objet Kubernetes, on peut get les déploiements :
$ kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 2/2 2 2 11s
On peut aussi describe le déploiement :
$ kubectl describe deploy
Name: nginx
Namespace: default
CreationTimestamp: Wed, 30 Aug 2023 09:19:38 +0000
Labels: app=nginx
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=nginx
...
On va maintenant récupérer les IPs des 2 pods nginx :
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-69c866fc5d-bq8wg 1/1 Running 0 2m41s 10.5.1.5 node2 <none> <none>
nginx-69c866fc5d-wkcqg 1/1 Running 0 2m41s 10.5.1.4 node2 <none> <none>
On va ensuite créer un pod avec une image busybox afin de pouvoir tester la connectivité entre les pods (ping, curl) :
apiVersion: v1
kind: Pod
metadata:
name: busybox
spec:
containers:
- name: busybox
image: radial/busyboxplus:curl
args:
- sleep
- "1000"
On va ensuite exécuter un ping + un curl sur l'IP d'un serveur nginx. Pour se faire nous allons utiliser la commande kubectl exec :
$ kubectl exec busybox -- ping 10.5.1.5
PING 10.5.1.5 (10.5.1.5): 56 data bytes
64 bytes from 10.5.1.5: seq=0 ttl=64 time=0.326 ms
64 bytes from 10.5.1.5: seq=1 ttl=64 time=0.110 ms
64 bytes from 10.5.1.5: seq=2 ttl=64 time=0.086 ms
^C
$ kubectl exec busybox -- ping 10.5.1.4
PING 10.5.1.4 (10.5.1.4): 56 data bytes
64 bytes from 10.5.1.4: seq=0 ttl=64 time=0.317 ms
64 bytes from 10.5.1.4: seq=1 ttl=64 time=0.115 ms
64 bytes from 10.5.1.4: seq=2 ttl=64 time=0.188 ms
$ kubectl exec busybox -- curl -s 10.5.1.5
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
On voit donc, que sans configuration précise, le pod busybox peut contacter les autres pods !
On sait qu'un service aura une IP et un DNS :

On va garder le même déploiement nginx que dans l'exemple précédent. On va aussi garder le pod busybox.
A cela on va ajouter un service :
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
Après l'avoir apply/create :
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 46m
nginx-service ClusterIP 10.97.203.91 <none> 80/TCP 4m36s
Puis on va s'exec dans le pod busybox et essayer de contacter le service de différentes manières :
$ kubectl exec -it busybox -- sh
[ root@busybox:/ ]$ curl 10.97.203.91
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
[ root@busybox:/ ]$ curl nginx-service
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
[ root@busybox:/ ]$ curl nginx-service.default.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
Il y a donc différentes manières d'atteindre le service :
<service>.<namespace>.svc.cluster.localMaintenant que vous comprenez pourquoi les communications directes entre Pods posent problème, découvrons comment les Services résolvent ces défis.
Un Service agit comme un intermédiaire stable qui :
Kubernetes inclut un serveur DNS (CoreDNS) qui traduit les noms en adresses IP :
# Au lieu de retenir des IPs qui changent :
curl http://10.244.1.10:80
# Vous utilisez un nom simple et stable :
curl http://mon-application
Avantage : Le nom reste le même, même si les Pods redémarrent !
# 1. Créer une application web avec plusieurs répliques
kubectl create deployment webapp --image=nginx:1.29.5 --replicas=3
# 2. Créer un Service pour l'exposer (Type ClusterIP par défaut)
kubectl expose deployment webapp --port=80 --name=mon-service
# 3. Vérifier que le Service est créé
kubectl get services
# Vous verrez : mon-service avec une IP stable !
# 4. Tester la communication par nom DNS
kubectl run test-pod --image=busybox:1.36 -it --rm -- sh
# Dans le Pod de test :
nslookup mon-service # Résolution DNS
curl http://mon-service # Communication stable !
curl http://mon-service # Même nom, mais différents Pods répondent
# 5. Nettoyer
kubectl delete deployment webapp
kubectl delete service mon-service
Magie : Le nom mon-service fonctionne toujours, même si les Pods redémarrent !
Kubernetes propose 4 types de Services pour différents besoins. Chacun a son rôle spécifique :
Usage : Communication entre applications à l'intérieur du cluster
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
type: ClusterIP # Par défaut
selector:
app: backend # Sélectionne les Pods avec ce label
ports:
- port: 80 # Port du Service
targetPort: 8080 # Port des Pods
Exemple d'usage :
Usage : Permettre l'accès depuis l'extérieur du cluster
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
type: NodePort
selector:
app: frontend
ports:
- port: 80 # Port du Service
targetPort: 80 # Port des Pods
nodePort: 30080 # Port sur chaque serveur (30000-32767)
externalTrafficPolicy: Local # ou Cluster (contrôle routage trafic externe)
externalTrafficPolicy :
Accès : http://IP-DU-SERVEUR:30080
Usage : Exposition externe avec Load Balancer géré par le cloud provider
apiVersion: v1
kind: Service
metadata:
name: webapp-loadbalancer
spec:
type: LoadBalancer
selector:
app: webapp
ports:
- port: 80
targetPort: 8080
Avantages :
Exemple d'utilisation : Applications web en production
Usage : Rediriger vers un service externe (hors cluster)
apiVersion: v1
kind: Service
metadata:
name: database-externe
spec:
type: ExternalName
externalName: db.example.com # Serveur externe
ports:
- port: 5432
Cas d'usage :
Avantage : Vos applications utilisent le même nom DNS même si le service est externe
| Type | Accès | Usage Typique | Coût |
|---|---|---|---|
| ClusterIP | Interne seulement | API, bases de données internes | Gratuit |
| NodePort | IP Node:Port | Développement, tests | Gratuit |
| LoadBalancer | IP publique dédiée | Production cloud | Payant (cloud) |
| ExternalName | Redirection DNS | Intégration externe | Gratuit |
# === Scénario : Application Frontend + Backend ===
# 1. Créer l'application Backend (interne seulement)
kubectl create deployment backend --image=nginx:1.29.5 --replicas=2
kubectl label deployment backend app=backend
# 2. Exposer le Backend avec ClusterIP (défaut)
kubectl expose deployment backend --port=80 --name=backend-service
# 3. Créer l'application Frontend (accès externe)
kubectl create deployment frontend --image=nginx:1.29.5 --replicas=2
kubectl label deployment frontend app=frontend
# 4. Exposer le Frontend avec NodePort
kubectl expose deployment frontend --port=80 --type=NodePort --name=frontend-service
# 5. Vérifier les Services
kubectl get services
# 6. Tester la communication interne
kubectl run test --image=busybox:1.36 -it --rm -- sh
# Dans le Pod : curl http://backend-service
# 7. Tester l'accès externe
# Récupérer le port NodePort
kubectl get service frontend-service
# Accéder via : http://IP-DU-SERVEUR:PORT-NODEPORT
# 8. Nettoyer
kubectl delete deployments backend frontend
kubectl delete services backend-service frontend-service
apiVersion: v1
kind: Service
metadata:
name: mon-service # Nom du Service
labels:
app: web # Labels du Service
spec:
selector: # Sélecteur des Pods cibles
app: web
version: stable
type: ClusterIP # Type de Service
ports:
- name: http # Nom du port (optionnel)
port: 80 # Port exposé par le Service
targetPort: 8080 # Port des Pods cibles
sessionAffinity: None # ou ClientIP
Points clés :
selector : Comment le Service trouve ses Podsport : Port sur lequel le Service écoutetargetPort : Port sur lequel les Pods écoutent# Formats disponibles (du plus simple au plus complet)
mon-service # Même namespace
mon-service.autre-namespace # Autre namespace
mon-service.autre-namespace.svc.cluster.local # Nom complet
# 1. Créer des applications dans différents namespaces
kubectl create namespace frontend
kubectl create namespace backend
# 2. Créer les applications
kubectl create deployment api --image=nginx:1.29.5 -n backend
kubectl create deployment web --image=nginx:1.29.5 -n frontend
# 3. Créer les Services
kubectl expose deployment api --port=80 -n backend
kubectl expose deployment web --port=80 -n frontend
# 4. Tester la résolution DNS
kubectl run dns-test --image=busybox:1.36 -n frontend -it --rm -- sh
# Dans le Pod :
# Service dans le même namespace
nslookup web
curl http://web
# Service dans un autre namespace
nslookup api.backend
curl http://api.backend
# 5. Nettoyer
kubectl delete namespaces frontend backend
Le Service répartit automatiquement le trafic entre tous les Pods en bonne santé.
# 1. Créer une application avec plusieurs répliques
kubectl create deployment webapp --image=nginx:1.29.5 --replicas=3
kubectl expose deployment webapp --port=80 --name=webapp-service
# 2. Personnaliser chaque Pod pour les distinguer
kubectl get pods -l app=webapp
# Pour chaque Pod, modifier sa page d'accueil
kubectl exec webapp-XXXXX -- sh -c 'echo "Réponse du Pod $HOSTNAME" > /usr/share/nginx/html/index.html'
# 3. Tester la répartition de charge
kubectl run test --image=busybox:1.36 -it --rm -- sh
# Dans le Pod, faire plusieurs requêtes :
for i in $(seq 1 10); do curl -s http://webapp-service; done
# Vous verrez des réponses de différents Pods !
# 4. Nettoyer
kubectl delete deployment webapp
kubectl delete service webapp-service
Les Services NodePort et LoadBalancer ont des limitations :
L'Ingress résout ces problèmes avec un point d'entrée unique et intelligent.
Un Ingress gère l'accès externe HTTP/HTTPS vers les services dans le cluster avec :

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- monsite.com
- api.monsite.com
secretName: webapp-tls
rules:
- host: monsite.com # Nom de domaine
http:
paths:
- path: / # Chemin
pathType: Prefix
backend:
service:
name: frontend-service # Service cible
port:
number: 80
- host: api.monsite.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: backend-service
port:
number: 80
# Route différents domaines vers différents services
rules:
- host: frontend.monsite.com
http:
paths:
- path: /
backend:
service:
name: frontend-service
- host: api.monsite.com
http:
paths:
- path: /
backend:
service:
name: api-service
# Route différents chemins vers différents services
rules:
- host: monsite.com
http:
paths:
- path: /web
pathType: Prefix
backend:
service:
name: frontend-service
- path: /api
pathType: Prefix
backend:
service:
name: api-service
# === Scénario : Site web avec API ===
# 1. Créer les applications
kubectl create deployment frontend --image=nginx:1.29.5 --replicas=2
kubectl create deployment backend --image=nginx:1.29.5 --replicas=2
# 2. Personnaliser les réponses pour les distinguer
kubectl exec deployment/frontend -- sh -c 'echo "<h1>Frontend App</h1>" > /usr/share/nginx/html/index.html'
kubectl exec deployment/backend -- sh -c 'echo "<h1>Backend API</h1>" > /usr/share/nginx/html/index.html'
# 3. Créer les Services
kubectl expose deployment frontend --port=80 --name=frontend-service
kubectl expose deployment backend --port=80 --name=backend-service
# 4. Créer l'Ingress avec routage par chemin
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: monsite.local # Utilisez votre domaine ou IP
http:
paths:
- path: /web
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: backend-service
port:
number: 80
EOF
# 5. Vérifier l'Ingress
kubectl get ingress
kubectl describe ingress webapp-ingress
# 6. Tester l'accès (adapté selon votre setup)
# Si vous utilisez minikube :
minikube ip # Récupérer l'IP
# Ajouter à /etc/hosts : <minikube-ip> monsite.local
# Puis tester :
curl http://monsite.local/web # → Frontend App
curl http://monsite.local/api # → Backend API
# 7. Nettoyer
kubectl delete ingress webapp-ingress
kubectl delete services frontend-service backend-service
kubectl delete deployments frontend backend
Un Ingress Controller est requis pour que les Ingress fonctionnent. Les plus populaires :
# Installation (exemple pour minikube)
minikube addons enable ingress
# Ou pour un cluster standard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.13.2/deploy/static/provider/cloud/deploy.yaml
# Installation de cert-manager (gestionnaire automatique de certificats)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
# Ingress avec SSL automatique
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: secure-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- monsite.com
secretName: monsite-tls # Certificat généré automatiquement
rules:
- host: monsite.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp-service
port:
number: 80
metadata:
annotations:
# Redirection
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/redirect-to-https: "true"
# Limites
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/connection-proxy-header: "keep-alive"
# CORS
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "*"
# Authentification
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
# Taille des uploads
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
apiVersion: v1
kind: Service
spec:
sessionAffinity: ClientIP # Même client → même Pod
sessionAffinityConfig:
clientIP:
timeoutSeconds: 3600 # Durée de l'affinité
# Ingress principal (95% du trafic)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-main
spec:
rules:
- host: monapp.com
http:
paths:
- path: /
backend:
service:
name: app-v1
---
# Ingress canary (5% du trafic)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "5"
spec:
rules:
- host: monapp.com
http:
paths:
- path: /
backend:
service:
name: app-v2
# Vérifier les Services
kubectl get services
kubectl describe service mon-service
# Vérifier que les Pods sont sélectionnés
kubectl get endpoints mon-service
# Vérifier les labels des Pods
kubectl get pods --show-labels
# Tester la résolution DNS
kubectl run debug --image=busybox:1.36 -it --rm -- nslookup mon-service
Service ne répond pas
# Vérifier que les Pods ont les bons labels
kubectl get pods --show-labels
kubectl describe service mon-service
DNS ne fonctionne pas
# Vérifier CoreDNS
kubectl get pods -n kube-system | grep coredns
Services : Exposent vos Pods avec une IP et un nom DNS stables
4 Types de Services :
ClusterIP : Communication interne (défaut)NodePort : Accès externe simple pour dev/testLoadBalancer : Production cloud avec IP publiqueExternalName : Redirection vers services externesService Discovery : Utilisez les noms DNS au lieu des IP
Load Balancing : Automatique entre tous les Pods en bonne santé
Choix du type : Selon votre environnement (local, cloud, intégration)
Dans le module suivant, nous découvrirons le stockage persistant pour préserver vos données même quand les Pods redémarrent.
TP associé : TP 4 : Services et Réseau
Ce que vous avez appris :
Ce qui vous attend :