Gérer et manipuler un Service Kubernetes

Dans ce chapitre, nous verrons comment gérer plus efficacement le réseau de vos Pods grâce aux Services.

Introduction

Dans cet article, nous aborderons les services Kubernetes. Nous nous pencherons sur leur création et utilisation.

C'est quoi un service Kubernetes 🤔 ?

Un service peut être défini comme une abstraction par-dessus les Pods, qui attribue aux pods leurs propres adresses IP et un nom DNS unique, et peut aussi équilibrer la charge entre eux.

En effet, les pods ont une durée de vie très limité et sont à maintes reprises créés et détruits, ce qui par conséquent altère continuellement leurs adresses IP. Interviennent alors les services qui vont quant à eux permettre aux clients d'échanger de manière plus fiable avec les conteneurs s’exécutant dans le Pod à l’aide d'une adresse IP virtuelle statique grâce au travail du composant kube-proxy.

Il existe quatre types de services pour une utilisation particulière :

  • ClusterIP : C'est le type par défaut. Il expose le Service sur une adresse IP interne du cluster. De ce fait, le service n'est accessible que depuis l'intérieur du cluster.
  • NodePort : Il expose le service vers l'extérieur du cluster à l'aide du NAT (la plage de ports autorisés est entre 30000 et 32767).
  • LoadBalancer : Il utilise l’équilibreur de charge des fournisseurs de cloud. Ainsi, les services NodePort et ClusterIP sont créés automatiquement et sont acheminés par l'équilibreur de charge externe.
  • ExternalName : Ce service effectue une simple redirection CNAME (par exemple rediriger le trafic vers le nom de domaine "example.com").

Sans transition, commençons directement par la manipulation des services.

Manipulation des Services

Dans cet article nous n'étudierons que le type ClusterIP et NodePort qui sont les plus communément utilisés.

ClusterIP

Nous allons commencer par manipuler le service ClusterIP, utilisé par défaut sur Kubernetes.

Pour ce chapitre, j'utiliserai ma propre image hajdaini/flask avec le tag random qui permet d'afficher depuis une requête http, le nom du conteneur et une chaîne de caractères aléatoires ce qui aura pour but d'analyser plus facilement l'acheminement du trafic effectué par l'agent kube-proxy.

Créons d'abord notre template sous le format YAML de manière à construire notre propre service, avec le contenu suivant :

apiVersion: v1
kind: Service
metadata:
  name: flask-service
spec:
  type: ClusterIP
  selector:
    app: flask
  ports:
  - port: 5000
    targetPort: 5000
    name: flask-cluster-ip

Place maintenant aux explications :

type: ClusterIP

Sans surprise, c'est ici que nous définissons le type de notre service.


selector:
  app: flask

Ici, nous indiquons que le service flask-service ne sera utilisé que par les Pods ayant le label app: flask.


- port: 5000
  targetPort: 5000
  name: flask-cluster-ip

Dans cette phase, le targetPort est le port qui est utilisé par les Pods de notre Deployment et le port est le numéro de port qui sera utilisé par notre service afin de communiquer avec lui depuis l'intérieur de notre cluster K8.

Information

Sur docker ça correspond à targetPort:port de la commande docker run.

Dorénavant, nous allons exécuter la commande suivante dans le but de créer notre Service Kubernetes :

kubectl create -f service.yaml

Vérifions ensuite la liste des services disponibles dans notre cluster Kubernetes :

kubectl get svc

Résultat :

NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
flask-service   ClusterIP   10.102.220.242   <none>        5000/TCP   20h
kubernetes      ClusterIP   10.96.0.1        <none>        443/TCP    20d

Nous avons donc le service kubernetes qui est créé par défaut par K8s et notre service flask-service qui écoute sur l'IP 10.102.220.242 et sur le port 5000.

Ensuite, nous construisons notre Deployment qui utilisera mon image hajdaini/flask:random en deux répliques :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: flask-deployment
spec:
  selector:
    matchLabels:
      app: flask
  replicas: 2
  template:
    metadata:
      labels:
        app: flask
    spec:
      containers:
      - name: flask-random
        image: hajdaini/flask:random

Comme vous pouvez le constater, nous avons spécifié le label app: flask, afin qu'il soit pris en compte par notre Service flask-service. Dès lors, créons notre Deployment :

kubectl create -f flask.yaml

À présent, récupérons le nom des Pods de notre Deployment :

kubectl get pods -o wide

Résultat :

NAME                               READY   STATUS    RESTARTS   AGE     IP              NODE       NOMINATED NODE   READINESS GATES
flask-deployment-8cfb9777b-s8hnp   1/1     Running   0          3m58s   192.168.1.177   worker-1   <none>           <none>
flask-deployment-8cfb9777b-wdxt9   1/1     Running   0          3m58s   192.168.1.176   worker-1   <none>           <none>

Comme je l'ai précisé plus haut, le service de type ClusterIP n'est accessible que depuis l'intérieur de notre cluster. Je vais donc devoir passer par le protocole ssh depuis mon nœud dénommer worker-1 dans l'intention de communiquer avec mon service, comme suit :

ssh vagrant@[WORKER_IP] curl -s http://[CLUSTER_IP]:5000

Rappel

l'Ip de mon nœud worker-1 est 192.168.50.11.

Soit, dans mon cas :

ssh vagrant@192.168.50.11 curl -s http://10.102.220.242:5000

Résultat :

My name is flask-deployment-8cfb9777b-wdxt9 and I say to you "WVYVOS"

On remarque que c'est le Pod nommé flask-deployment-8cfb9777b-wdxt9 qui répond à notre requête depuis le service flask-service sur le port 5000.

Maintenant utilisons une boucle for afin de vérifier comment l'agent kube-proxy achemine notre trafic :

ssh vagrant@192.168.50.11 "for i in {1..5}; do curl -s http://10.102.220.242:5000 && echo ""; done"

Résultat :

My name is flask-deployment-8cfb9777b-s8hnp and I say to you "XJY8F9"
My name is flask-deployment-8cfb9777b-wdxt9 and I say to you "Z25FZC"
My name is flask-deployment-8cfb9777b-s8hnp and I say to you "UV41OH"
My name is flask-deployment-8cfb9777b-wdxt9 and I say to you "A4MKPX"
My name is flask-deployment-8cfb9777b-wdxt9 and I say to you "V802S9"

On peut s'apercevoir qu'un LoadBalancer très basique est adopté par l'agent kube-proxy afin de rediriger le trafic sur les différents Pods de notre Deployment.

À partir de maintenant , vous pouvez détruire et recréer vos Pods et communiquer avec eux sereinement, sans vous soucier de leur nouvelle adresse IP, puisque dorénavant vous utilisez l'adresse IP de votre ClusterIP, offrant ainsi une meilleure stabilité.

NodePort

Imaginons, que nous avons besoin cette fois-ci d'accéder à notre Pod depuis l'extérieur de notre cluster Kubernetes. Nous exploiterons ainsi cette le type NodePort.

Sans la nécessité de détruire notre ancien service, nous allons modifier et appliquer les changements de notre ancien template YAML, en suivant les étapes suivantes :

Premièrement modifions notre ancien template YAML, comme suit :

apiVersion: v1
kind: Service
metadata:
  name: flask-service
spec:
  type: NodePort
  selector:
    app: flask
  ports:
  - port: 5000
    targetPort: 5000
    nodePort: 30001
    name: flask-np

Voici quelques explications sur nos nouveaux changements :

type: NodePort

Ici, Nous changeons d'abord le type de notre service.


ports:
- port: 5000
  targetPort: 5000
  nodePort: 30001
  name: flask-np

Ensuite, dans cette étape, nous translatons l'IP du service vers l'IP du nœud qui accueillera nos Pods et le port du service (ici 5000) vers le port 30001.

Information

Pour rappel la plage d'IP dans un service de type NodePort se situe entre 30000 et 32767. On ne peut donc pas dépasser cette limite dans le champ nodePort.

Deuxièmement, appliquons les changements de notre service :

kubectl apply -f flask.yaml

Résultat :

service/flask-service configured

Vérifions ensuite la liste des services disponibles dans note cluster :

kubectl get svc

Résultat :

NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
flask-service   NodePort    10.102.220.242   <none>        5000:30001/TCP   21h
kubernetes      ClusterIP   10.96.0.1        <none>        443/TCP          20d

Désormais, nous somme capables d'accéder à nos Pods depuis l'extérieur de notre cluster à travers l'IP de notre nœud (dans mon cas c'est le nœud nommé worker-1), comme suit :

curl -s http://192.168.50.11:30001

Résultat :

My name is flask-deployment-8cfb9777b-nwhrl and I say to you "P68NVW"

La commande kubectl expose

Plutôt que de rédiger un template YAML de notre service, on peut créer un service à l'aide de la commande kubectl expose afin d'exposer les pods notre Deployment.

Par exemple pour exposer notre Deployment flask-deployment à l'aide d'un service de type ClusterIP, comme vu précédemment, nous suivrons les étapes suivantes :

D'abord je commence par supprimer mon service Kubernetes flask-service. Pour ce faire nous lançons la commande suivante :

kubectl delete svc flask-service

Après ceci, on expose les pods de notre Deployment :

kubectl expose deployment flask-deployment --name flask-service \               1 ↵
    --type ClusterIP --protocol TCP --port 5000 --target-port 5000 --selector='app=flask'

Voici la commande pour exposer notre Deployment avec un service de type NodePort :

kubectl expose deployment flask-deployment --name flask-service \
    --type NodePort --protocol TCP --port 5000 --target-port 5000 --selector='app=flask'

Conclusion

On peut constater que les services restent à bon moyen de gérer plus efficacement la partie réseau de nos Pods. Comme pour chaque fin d'article, voici un antisèche des commandes des services kubernetes :

# Afficher la liste des Services
kubectl get service [En option <SERVICE NAME>]

# Créer un Service depuis un template
kubectl create -f <template.yaml>

# Créer un Service depuis sans template
kubectl expose deployment <DEPLOYMENT NAME>
    --name : nom du service
    --type : type du service
    --protocol : protocole à utiliser (TCP/UDP) 
    --port : port utilisé par le service 
    --target-port : port utilisé utilisé par le Pod 
    --selector='clé=valeur': le sélecteur utilisé par service 

# Supprimer un Service
kubectl delete service <SERVICE NAME>

# Appliquer des nouveaux changements à votre Service sans le détruire
kubectl apply -f <template.yaml>

# Modifier et appliquer les changements de votre Service instantanément sans le détruire
kubectl edit service <SERVICE NAME>

# Afficher les détails d'un Service
kubectl describe service <SERVICE NAME>

Espace commentaire

Écrire un commentaires

vous devez être connecté pour poster un message !

10 commentaires

Je vous remercie pour ton tuto, c'est fantastique 💪 !
Je te remercie 😻 !
utilisateur sans photo de profile

@vmichel

Ton billet est super. J'ai appris tellement de choses en le lisant. Merci pour cette gros aide @ajdaini-hatim 💪 !
Je vous remercie pleinement !
utilisateur sans photo de profile

@fhubert

Ton travail est vraiment beaucoup sincère. J'ai été bluffé par la la clarté et recherche. Mes remerciements @ajdaini-hatim pour cette sincère ressource primordiale` ��
Je voulais juste vous applaudir pour ton tuto exceptionnel. Il est enrichissant. Merci pour ton pleinement tuto
Remerciement pour ton article, j'ai appris tellement de choses
Votre cours en ligne m'a particulièrement aidé @ajdaini-hatim

Salut @melhamdi Tu ne peux pas utiliser matchLabels dans le fichier de configuration d'un Service car il n'est pas encore pris en charge par ce type de ressource k8s, donc à la place tu peux simplement ajouter les labels que tu souhaites.

utilisateur sans photo de profile

@melhamdi

bonjour,

une question par rapport aux selecteurs,

pourqoui on utilse parfois le "selector" avec "matchlabels" et parfois on met directement le "selector" sans "matchlabels" ?

Merci d'avance

D'autres articles

Rejoindre la communauté

Recevoir les derniers articles gratuitement en créant un compte !

S'inscrire