Aller au contenu

Exposer vos applications avec les services Kubernetes

Mise à jour :

logo kubernetes

**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 et version=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: v1
kind: Pod
metadata:
name: backend-pod
labels:
app: backend
version: v1
spec:
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: v1
kind: Service
metadata:
name: backend-service
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 80
targetPort: 8080

Dans cet exemple :

  • Le service backend-service cible tous les pods ayant le label app: backend.
  • Il expose le port 80 pour rediriger les requêtes vers le port 8080 des pods backend.

Utiliser des selectors avancés

Kubernetes permet d’utiliser plusieurs conditions pour affiner la sélection des pods.

  1. 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.

  1. 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.

  1. 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: v1
kind: Service
metadata:
name: backend-service
spec:
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: v1
kind: Service
metadata:
name: backend-service
spec:
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: v1
kind: Service
metadata:
name: backend-service
spec:
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: v1
kind: Service
metadata:
name: external-db
spec:
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 ou ExternalName).
  • Les pods ciblés (via un selector).
  • Les ports exposés (port externe et interne).

Exemple : création d’un service ClusterIP

apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
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 :

Terminal window
kubectl apply -f my-service.yaml

Pour créer un service sans YAML, on peut aussi utiliser kubectl expose :

Terminal window
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 :

Terminal window
kubectl get services

Exemple de sortie :

Terminal window
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-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 :

Terminal window
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 :

  1. Éditer directement le fichier YAML puis appliquer les modifications :

    Terminal window
    kubectl apply -f my-service.yaml
  2. Utiliser kubectl edit pour modifier le service en direct :

    Terminal window
    kubectl edit service my-service

    Cela 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 :

Terminal window
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 :

Terminal window
backend-service.default.svc.cluster.local

Depuis un pod situé dans le même namespace (default), il suffit d’utiliser le nom court :

Terminal window
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 :

Terminal window
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 :

Terminal window
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/v1
kind: NetworkPolicy
metadata:
name: backend-policy
spec:
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é.