Préparation à la CKA - 08 - Services - ClusterIP

Publié le 01/10/2024 par Ali Sanhaji

Services

Dans les articles précédents, nous avons vu comment déployer des applications conteneurisées sur Kubernetes en utilisant des Pods, puis des Deployments qui contrôlent ces pods via des ReplicaSets. On arrive donc à avoir nos conteneurs sur un cluster kubernetes. Maintenant, s’il s’agit d’une application web par exemple, il faut pouvoir y accéder. Comment fait-on sur Kubernetes ?

Nous passons par un objet de type Service, qui va recevoir la requête et la transmettre à un des pods en backend du deployment que l’on veut exposer. Ce Service nous donne au final une adresse IP sur laquelle nous connecter pour être redirigé vers l’un des pods en backend.

Le Service sélectionne les Pods backend à l’aide d’un selector basé sur les labels. Seuls les Pods dont les labels correspondent au selector du Service seront considérés comme des endpoints.

Selon le type de Service que l’on utilise, on aura une adresse IP différente. Commençons par le plus basique, le Service de type ClusterIP.

ClusterIP

Lors de la création d’un cluster avec kubeadm, il est possible de spécifier l’argument --pod-network-cidr=10.244.0.0/16, qui définit l’espace d’adressage utilisé pour les Pods. Cette option nous permet de spécifier l’espace d’adressage dans lequel on va piocher des adresses IP afin de les assigner aux pods que l’on va créer sur le cluster.

Il existe plusieurs arguments à la commande KubeADM, parmi lesquels —service-cidr (que nous aurions pu spécifier) qui a pour valeur par défaut 10.96.0.0/12. C’est dans cet espace d’adressage que seront piochées les adresses qui seront attribuées aux objets Service de type ClusterIP que l’on va créer. On parle de ClusterIP car l’adresse IP attribuée provient de l’espace d’adressage réservé aux Services du cluster. Pour pouvoir accéder à ces adresses IP, il faut pouvoir accéder au réseau spécifié, donc soit y accéder en étant dans le même réseau, ou en ayant du routage vers ce réseau.

Exemple Voyant un exemple en déployant une application simple, un serveur web Apache, et en exposant ce déploiement avec un Service de type ClusterIP :

Tout d’abord, on crée le déploiement :

kubectl create deployment apache-clusterip --image=bitnami/apache:latest
deployment.apps/apache-clusterip created

Le pod a bien été déployé :

kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
apache-clusterip-7c5f85d878-tpzkh   1/1     Running   0          6s

Nous allons mettre deux replicas sur ce déploiement pour voir le résultat sur le service :

kubectl scale deployment apache-clusterip --replicas=2
deployment.apps/apache-clusterip scaled
kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
apache-clusterip-7c5f85d878-sfxhk   1/1     Running   0          4s
apache-clusterip-7c5f85d878-tpzkh   1/1     Running   0          29s

Pour exposer un déploiement avec un objet de type Service, on peut utiliser la sous-commande expose :

kubectl expose deployment apache-clusterip --port=8080 --target-port=8080 --type=ClusterIP
service/apache-clusterip exposed

Le —type=ClusterIP est optionnel car c’est la valeur par défaut de l’argument.

On peut alors voir qu’un service a bien été créé :

kubectl get service
NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
apache-clusterip   ClusterIP   10.110.74.224   <none>        8080/TCP   25s

Si on voit le détail du service :

kubectl describe svc apache-clusterip
Name:              apache-clusterip
Namespace:         default
Labels:            app=apache-clusterip
Annotations:       <none>
Selector:          app=apache-clusterip
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.110.74.224
IPs:               10.110.74.224
Port:              <unset>  8080/TCP
TargetPort:        8080/TCP
Endpoints:         10.244.1.28:8080,10.244.2.27:8080
Session Affinity:  None
Events:            <none>

L’adresse 10.110.74.224 a été assignée à ce service, et c’est à partir de celle-ci qu’on va accéder au déploiement des serveurs Apache.

Ce service va écouter les appels vers l’adresse IP 10.110.74.224 sur le port 8080, et renvoyer vers l’un des deux endpoints 10.244.1.28:8080, 10.244.2.27:8080 sur le même port, comme nous l’avons spécifié avec —target-port, car l’image Apache qu’on a déployé écoute sur le port 8080.

Le routage vers les Pods backend est assuré par kube-proxy, qui met en place des règles réseau (iptables ou IPVS selon la configuration) sur chaque nœud du cluster.

C’est lui qui intercepte le trafic destiné à l’IP du Service et le redirige vers l’un des Pods correspondant aux endpoints.

Vous l’avez compris, les adresses IP des endpoints ne sont autres que celles des deux pods Apache que nous avons déployé tout à l’heure :

kubectl get pods -o wide
NAME                                READY   STATUS    RESTARTS   AGE     IP            NODE
apache-clusterip-7c5f85d878-sfxhk   1/1     Running   0          2m56s   10.244.2.27   k8s-node-2
apache-clusterip-7c5f85d878-tpzkh   1/1     Running   0          3m21s   10.244.1.28   k8s-node-1

Maintenant, nous pouvons tester l’accès au Service en appelant son adresse IP. On peut atteindre cette adresse si on fait l’appel depuis un des noeuds du cluster, par exemple celui du control plane :

ubuntu@k8s-controlplane:~$ curl http://10.110.74.224:8080
<html><body><h1>It works!</h1></body></html>

Conclusion

Nous avons vu comment exposer un déploiement à l’intérieur du cluster à l’aide d’un Service de type ClusterIP.
Ce type de Service permet aux Pods de communiquer entre eux de manière stable, indépendamment des adresses IP dynamiques des Pods backend.

Cependant, un Service de type ClusterIP n’est accessible qu’à l’intérieur du cluster.
Pour exposer une application à des utilisateurs externes, Kubernetes propose d’autres types de Services, tels que NodePort ou LoadBalancer, qui permettent d’ouvrir l’accès au-delà du réseau interne du cluster.