Exposer vos applications avec les services Kubernetes
Mise à jour :
**Dans un cluster Kubernetes, les pods sont éphémères : ils peuvent être créés, détruits et recréés à tout moment. Cette dynamique complique la communication entre eux, car leurs adresses IP changent constamment. Comment alors assurer une communication stable entre les différentes applications déployées ?
Pourquoi utiliser les services Kubernetes ?
Les services Kubernetes résolvent ce problème en fournissant une couche d’abstraction réseau qui permet :
- Une communication stable entre les pods : un service assigne une adresse IP fixe et un nom DNS aux pods qu’il gère, facilitant ainsi leur découverte.
- L’équilibrage de charge : un service peut répartir le trafic entre plusieurs pods pour optimiser la performance et la résilience de l’application.
- L’exposition des applications : certains services permettent de rendre accessibles des applications à l’extérieur du cluster, que ce soit via un port, un load balancer, ou une ingress.
- La gestion des dépendances entre microservices : dans une architecture microservices, chaque composant peut interagir avec les autres sans se soucier des changements d’IP des pods sous-jacents.
Exemple concret
Imaginons une application web avec :
- Un frontend (React, Angular, etc.),
- Un backend (API en Node.js, Python, etc.),
- Une base de données (PostgreSQL, MySQL, etc.).
Le backend doit communiquer avec la base de données, mais celle-ci tourne dans
un pod dont l’IP change régulièrement. Grâce aux services Kubernetes, le backend
peut simplement appeler database-service:5432
au lieu de chercher
dynamiquement l’IP du pod contenant la base de données.
Les services Kubernetes garantissent ainsi une communication robuste et fiable entre les composants d’une application, en assurant une connectivité dynamique et transparente dans un environnement distribué.
Comment lier les pods à un service Kubernetes ?
Dans Kubernetes, les services utilisent un mécanisme puissant pour cibler les pods qu’ils doivent gérer : les labels et les selectors.
Les labels sont des paires clé-valeur associées aux objets Kubernetes (comme les pods) pour les identifier et les organiser. Les selectors permettent aux services de sélectionner dynamiquement les pods à inclure en fonction de ces labels.
Pourquoi utiliser les labels et selectors ?
Les labels offrent une flexibilité essentielle pour gérer les déploiements, car ils permettent de :
- Grouper des pods ayant une même fonction (ex: tous les pods d’une API backend).
- Distinguer différentes versions d’une application (ex:
version=beta
etversion=stable
). - Appliquer des règles de routage avancées sans modifier la configuration du service.
Définir des labels sur un pod
Dans un fichier YAML, un label est défini dans la section metadata.labels
.
Exemple de déploiement d’un pod avec un label :
apiVersion: v1kind: Podmetadata: name: backend-pod labels: app: backend version: v1spec: containers: - name: backend image: my-backend:latest
Ici, le pod backend-pod
possède deux labels :
app: backend
(pour identifier le service)version: v1
(pour distinguer différentes versions)
Sélectionner des pods avec un selector
Un service doit savoir quels pods il doit gérer. Pour cela, il utilise
un selector
qui cible les pods ayant des labels correspondants.
Exemple de service ciblant des pods avec le label app: backend
:
apiVersion: v1kind: Servicemetadata: name: backend-servicespec: selector: app: backend ports: - protocol: TCP port: 80 targetPort: 8080
Dans cet exemple :
- Le service
backend-service
cible tous les pods ayant le labelapp: backend
. - Il expose le port
80
pour rediriger les requêtes vers le port8080
des pods backend.
Utiliser des selectors avancés
Kubernetes permet d’utiliser plusieurs conditions pour affiner la sélection des pods.
- Sélectionner plusieurs labels avec AND
selector: app: backend version: v1
➡ Seuls les pods ayant les deux labels (app: backend
ET version: v1
)
seront sélectionnés.
- Sélectionner des pods avec une condition OR (matchExpressions)
selector: matchExpressions: - { key: app, operator: In, values: [backend, api] }
➡ Tous les pods ayant le label app
avec les valeurs backend
ou api
seront sélectionnés.
- Exclure certains pods (NOT)
selector: matchExpressions: - { key: version, operator: NotIn, values: [beta] }
➡ Tous les pods sauf ceux avec version=beta
seront sélectionnés.
Les différents types de services Kubernetes
Dans Kubernetes, un Service permet d’exposer un groupe de pods de manière stable et prévisible. Il existe quatre types de services, chacun ayant un rôle spécifique en fonction des besoins d’accessibilité et de connectivité au sein ou à l’extérieur du cluster.
1. ClusterIP : Communication interne au cluster
Par défaut, un service Kubernetes est de type ClusterIP. Il crée une adresse IP interne accessible uniquement depuis les autres pods du cluster.
Cas d’utilisation :
- Communication entre microservices (exemple : un backend API qui interagit avec une base de données).
- Services internes non accessibles depuis l’extérieur.
Exemple YAML d’un service ClusterIP :
apiVersion: v1kind: Servicemetadata: name: backend-servicespec: selector: app: backend ports: - protocol: TCP port: 80 targetPort: 8080 type: ClusterIP
➡ Ici, tous les pods du cluster peuvent accéder à backend-service:80
, qui
redirige vers le port 8080 des pods backend.
2. NodePort : Exposition du service sur chaque nœud
Avec NodePort, Kubernetes expose le service sur un port spécifique de
chaque nœud du cluster. Il devient ainsi accessible depuis l’extérieur via
http://<NodeIP>:<NodePort>
.
Cas d’utilisation :
- Tester un service en local avant d’utiliser un LoadBalancer.
- Exposer un service de manière simple sans configurer un équilibreur de charge.
Exemple YAML d’un service NodePort :
apiVersion: v1kind: Servicemetadata: name: backend-servicespec: type: NodePort selector: app: backend ports: - protocol: TCP port: 80 targetPort: 8080 nodePort: 30080
➡ Le service sera accessible via http://<IP-de-nœud>:30080
et redirigera vers
les pods backend sur le port 8080.
3. LoadBalancer : Accès externe avec un équilibreur de charge
Le service LoadBalancer s’appuie sur l’infrastructure cloud pour exposer un service à Internet via un équilibreur de charge externe. Il répartit les requêtes entre les pods disponibles.
Cas d’utilisation :
- Exposer une API ou une application web à Internet.
- Gérer automatiquement l’équilibrage de charge pour un service critique.
Exemple YAML d’un service LoadBalancer :
apiVersion: v1kind: Servicemetadata: name: backend-servicespec: type: LoadBalancer selector: app: backend ports: - protocol: TCP port: 80 targetPort: 8080
➡ Kubernetes demande à son fournisseur cloud (AWS, GCP, Azure, etc.) de créer un équilibreur de charge qui redirigera le trafic vers les pods backend sur le port 8080.
4. ExternalName : Redirection vers un service externe
Le type ExternalName ne crée pas de proxy réseau, mais redirige les requêtes vers un nom de domaine externe (ex: API tierce, base de données SaaS).
Cas d’utilisation :
- Connecter une application Kubernetes à un service externe (ex: une base de données gérée).
- Simplifier la configuration en utilisant un DNS interne.
Exemple YAML d’un service ExternalName :
apiVersion: v1kind: Servicemetadata: name: external-dbspec: type: ExternalName externalName: database.example.com
➡ Toute requête envoyée à external-db
sera redirigée vers
database.example.com
.
Quel type de service choisir ?
- Besoin d’un accès interne uniquement ? → ClusterIP
- Accès externe simple sans cloud provider ? → NodePort
- Exposition via un Load Balancer cloud ? → LoadBalancer
- Connexion à un service externe via DNS ? → ExternalName
En fonction de votre infrastructure et de vos besoins, le choix du bon type de service garantit une communication efficace et sécurisée entre vos applications Kubernetes.
Création et gestion des services Kubernetes
Une fois que l’on comprend l’importance des services et leurs différents types, il est temps de voir comment les créer et les gérer dans Kubernetes.
1. Définir un service dans un fichier YAML
Un service est défini dans un fichier YAML qui spécifie :
- Le type de service (
ClusterIP
,NodePort
,LoadBalancer
ouExternalName
). - Les pods ciblés (via un
selector
). - Les ports exposés (port externe et interne).
Exemple : création d’un service ClusterIP
apiVersion: v1kind: Servicemetadata: name: my-servicespec: selector: app: my-app ports: - protocol: TCP port: 80 targetPort: 8080 type: ClusterIP
➡ Ce service :
- Expose un groupe de pods ayant le label
app: my-app
. - Écoute sur le port 80 et redirige le trafic vers le port 8080 des pods cibles.
- Utilise ClusterIP (accessible uniquement depuis le cluster).
2. Créer un service avec kubectl
Une fois le fichier YAML créé, on peut appliquer la configuration avec :
kubectl apply -f my-service.yaml
Pour créer un service sans YAML, on peut aussi utiliser kubectl expose
:
kubectl expose deployment my-app --type=ClusterIP --port=80 --target-port=8080
➡ Cette commande expose directement un déploiement sous forme de service ClusterIP.
3. Vérifier l’état d’un service
Après la création du service, on peut vérifier qu’il est bien actif avec :
kubectl get services
Exemple de sortie :
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEmy-service ClusterIP 10.100.0.1 <none> 80/TCP 2m
➡ Le Cluster-IP 10.100.0.1
est assigné au service, et il est accessible
depuis les autres pods du cluster.
Pour plus de détails, on peut exécuter :
kubectl describe service my-service
4. Modifier un service existant
Si l’on souhaite modifier un service (ex: changer le port ou le type de service), on peut :
-
Éditer directement le fichier YAML puis appliquer les modifications :
Terminal window kubectl apply -f my-service.yaml -
Utiliser
kubectl edit
pour modifier le service en direct :Terminal window kubectl edit service my-serviceCela ouvre un éditeur de texte où l’on peut changer les paramètres.
5. Supprimer un service
Si un service n’est plus nécessaire, on peut le supprimer avec :
kubectl delete service my-service
6. Cas particulier : Debug d’un service
Si un service ne fonctionne pas comme prévu, voici quelques commandes utiles pour troubleshooter :
-
Vérifier les endpoints (pods associés au service)
Terminal window kubectl get endpoints my-service➡ Si aucun endpoint n’est affiché, le
selector
du service ne correspond à aucun pod. -
Lister les pods et leurs labels pour vérifier la correspondance avec le service
Terminal window kubectl get pods --show-labels -
Tester l’accessibilité du service depuis un pod
Terminal window kubectl exec -it my-pod -- curl my-service:80➡ Permet de voir si un pod peut se connecter au service.
Comment accéder aux services Kubernetes ?
Dans Kubernetes, chaque service créé obtient automatiquement un nom DNS interne. Ce système simplifie grandement la communication entre pods, puisqu’il n’est plus nécessaire de connaître les adresses IP des pods.
Résolution DNS par défaut
Par défaut, un service appelé backend-service
dans le
namespace default
sera accessible via le DNS interne :
backend-service.default.svc.cluster.local
Depuis un pod situé dans le même namespace (default
), il suffit d’utiliser le
nom court :
curl http://backend-service
Résolution DNS entre namespaces
Pour accéder à un service situé dans un autre namespace, il faut spécifier explicitement le namespace dans le nom DNS :
curl http://backend-service.autre-namespace.svc.cluster.local
Cette notation complète est nécessaire uniquement lorsqu’on communique d’un namespace à un autre.
Vérifier la résolution DNS
Pour tester que la résolution DNS fonctionne correctement, exécutez depuis un pod :
kubectl exec -it mon-pod -- nslookup backend-service
La réponse indiquera clairement l’adresse IP associée au service demandé, confirmant que la résolution DNS interne est opérationnelle.
Bonnes pratiques pour les services Kubernetes
Pour gérer efficacement vos services Kubernetes, voici les bonnes pratiques à respecter :
Utiliser les labels correctement
Je recommande d’utiliser systématiquement des labels clairs et cohérents sur les pods, afin de simplifier la sélection par les services :
labels: app: backend environment: production
Choisir le bon type de service
Je conseille de bien définir le type de service adapté :
- ClusterIP pour la communication interne.
- NodePort ou LoadBalancer pour l’accès externe.
- Ingress pour gérer les règles HTTP/HTTPS complexes.
Éviter d’exposer inutilement des services
Ne rendez accessibles à l’extérieur que les services nécessaires, et privilégiez ClusterIP par défaut.
Activer la surveillance des services
Mettez en place un suivi des services avec des outils comme Prometheus et Grafana, pour détecter rapidement les problèmes.
Sécuriser les accès aux services
Protégez vos services sensibles avec des NetworkPolicies, limitant l’accès aux pods autorisés uniquement :
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: backend-policyspec: podSelector: matchLabels: app: backend ingress: - from: - podSelector: matchLabels: app: frontend
En respectant ces pratiques, on s’assure que mes services Kubernetes sont efficaces, fiables et sécurisés.
Conclusion
Les services Kubernetes offrent une solution simple et robuste pour gérer la communication au sein d’un cluster. En utilisant correctement les labels, en choisissant le type de service adapté, et en exploitant efficacement le DNS interne, on peut simplifier considérablement le déploiement des applications.
Appliquer ces bonnes pratiques permet d’avoir une infrastructure plus fiable, sécurisée et facile à administrer. Kubernetes devient alors un outil puissant pour gérer efficacement les microservices et assurer leur disponibilité.