Skip to content

Jouer à 2048 avec Kubernetes – Partie 1 : On s’fait la main

Depuis qu’il a été lancé en 2014, Kubernetes est devenu le leader dans les outils d’orchestration de conteneurs. Il permet entre autres, de faciliter le déploiement, la résilience et l’évolutivité de nos applications.

Cela fait maintenant quelques mois que je me forme à cet outil,  et il est l’heure de mettre en pratique ces nouvelles connaissances. Vous êtes de la partie ? 🎮

Super, du coup, dans cet article, nous allons apprendre pas à pas comment déployer une application sur Kubernetes. Mais pas n’importe quelle app’, pour les plus gamers d’entre nous, il s’agit du jeu 2048.

Pour cela nous allons créer plusieurs objets au sein de notre cluster : 

  • un déploiement
  • un service
  • un volume persistent
  • un ingress

Nous reviendrons en détail sur le rôle de chacun de ces composants au fil des étapes.

Pret pour une ptite’ game ? 🤗

Vous retrouverez les ressources de cet article sur mon gitlab.

Level 1 – Mettre en place son cluster Kube’

Pour des raisons de simplicité je vais lancer un cluster K3s en local sur mon poste. Pour ceux qui ne connaissent pas, c’est une solution Kubernetes légère, très facile à déployer et adapter aux environnements avec des ressources limitées. Perso, ça me convient très bien pour faire pop un cluster rapidement lorsque je souhaite tester des trucs.

Pour l’installer, rien de plus simple, il suffit de faire la commande suivante :

$ curl -sfL https://get.k3s.io | K3S_KUBECONFIG_MODE="644" sh

$ export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

L’installation d’un cluster n’étant pas le sujet principal de cet article, je vous laisse avec la documentation officielle 🙂

Level 2 – Namespace

Avant de foncer tête baissée dans notre projet, faisons les choses dans l’ordre. Qu’est-ce qu’un namespace ?

Dans le monde de Kubernetes c’est un moyen de ranger toutes les ressources d’une appli ou d’un projet dans un même endroit. Cela nous offre une gestion plus efficace, une isolation, une meilleure organisation et un contrôle d’accès plus précis.

Du coup, toutes les ressources créées seront dans le namespace que l’on va nommer game2048.

Pour la création de ce dernier il suffit de faire la commande suivante :

$ kubectl create namespace game2048

Les pros de k8s je vous vois venir 👀, je sais que “ce n’est pas bien” de créer nos ressources directement avec la commande kubectl. On va s’améliorer dans les prochaines parties, restez connectés. 😉

On peut vérifier que notre namespace est bien présent sur notre cluster avec la commande kubectl get namespace

Si vous avez game2048 dans la liste, bravo, vous pouvez passer au level 2️⃣ !

Level 3 – Déploiement

Petite définition ? 🤓

Bien sûûûûûr. Les déploiements sont des ressources Kubernetes. Ils offrent plusieurs avantages pour la gestion des applications.

En gros, les déploiements vont nous permettre de gérer le cycle de vie de notre app’. Et ce, de manière fiable et déclarative, en facilitant les mises à jour, les rollbacks, la scalabilité et la disponibilité. Oui oui, rien que ça 😂😂

Hop, on prend sa plus belle plume 🖋️(ou éditeur de texte) et on écrit notre premier déploiement :

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: game2048
  name: deployment2048
spec:
  selector:
	matchLabels:
  	app: app2048
  replicas: 2
  template:
	metadata:
  	labels:
    	app: app2048
	spec:
  	containers:
  	- image: public.ecr.aws/l6m2t8p7/docker-2048:latest
    	name: app2048
    	ports:
    	- containerPort: 80

On ne panique pas, j’ai la situation sous contrôle. Je vais revenir sur le contenu de ce fichier.

  • apiVersion: version de l’API Kubernetes utilisée.
  • kind: Le type de ressource, dans notre cas, un déploiement.
  • metadata: Les métadonnées associées au déploiement, comme le nom et dans quel namespace on souhaite déployer.
  • spec: Les spécifications du déploiement.
    • replicas: Le nombre de pods à maintenir.
    • selector: Les labels qui identifient les pods cibles pour ce déploiement.
    • template: La définition du pod.
      • metadata: Les labels associés au pod.
      • spec: Les spécifications du pod.
        • containers: Les conteneurs qui seront exécutés dans le pod.
          • name: Le nom du conteneur.
          • image: L’image Docker utilisée pour le conteneur.
      • ports: Les ports exposés par le conteneur.

Enregistrons tout ça dans un fichier. Ici je l’ai nommé 2048-deployment.yaml.

Pour déployer notre petit jeu on va utiliser la commande kubectl apply -f 2048-deployment.yaml.

On peut vérifier que tout est bien déployé avec la commande kubectl get deployment -n game2048. Si vous avez ce retour, je vous autorise à aller au level 4️⃣ :

NAME         	  READY   UP-TO-DATE       AVAILABLE       AGE
deployment2048    2/2 	  2        	   2       	   42s

Level 4 – Service

C’est cool tout ce que l’on a fait, mais comment on accède à notre jeu ? 🎮

C’est maintenant que les services vont nous être utiles ! Ils fournissent un mécanisme plutôt abstrait pour exposer des applications soit juste en interne dans le cluster, soit à l’extérieur où l’on pourra accéder à notre application depuis notre poste par exemple.

Pour déployer notre service, enregistrez ce fichier, que l’on nommera 2048-service.yaml :

apiVersion: v1
kind: Service
metadata:
  namespace: game2048
  name: service2048
  labels:
    app: app2048
spec:
  type: NodePort
  selector:
    app: app2048
  ports:
    - port: 80
      targetPort: 80

Petite explication 🤓

  • type: NodePort: Indique le type de service. Dans notre cas, il s’agit d’un service NodePort, ce qui signifie que le service sera accessible via un port spécifique sur tous les nœuds du cluster depuis l’extérieur.
  • selector: Spécifie les pods auxquels le service est associé. Ici, le service sélectionne les pods qui ont le label app: app2048.
  • ports: Définit les ports à travers lesquels le service sera accessible.
    • port: 80: Indique le port sur lequel le service sera accessible en interne
    • targetPort: 80: Indique le port sur lequel les paquets seront dirigés vers les pods associés. Dans ce cas, les paquets seront dirigés vers le port 80 des pods sélectionnés.

Pour déployer notre service on va utiliser la commande kubectl apply -f 2048-service.yaml.

Vérifions que tout est ok avec la commande kubectl get service -n game2048. Vous obtenez alors le port exposé à l’extérieur de votre cluster :

NAME      	TYPE   	CLUSTER-IP  	EXTERNAL-IP   PORT(S)    	AGE
service2048   NodePort   10.104.174.30   <none>    	80:32423/TCP   11s

Pour ma part c’est le port 32423. À présent on peut accéder à notre jeu avec l’ip de notre cluster et le port exposé par le service.

Si vous arrivez à voir cette page depuis votre navigateur, allez au level 5️⃣ !

screenshot 2048

Level 5 – Volumes

C’est top, je viens de faire une petite partie. J’ai fermé mon navigateur, et quand je suis revenu, mon score était à 0 😱😱. ça peut être dû à plusieurs choses. La première, nous avons 2 replicas (pour plus de résilience). Cependant si nous avons fait notre superbe score sur le premier pod et que l’on revient une heure plus tard, mais que l’on est routé sur notre deuxième pod nous n’aurons pas notre score. La deuxième possibilité, c’est que le pod a été supprimé pour une raison mystérieuse.

On est d’accord que l’on est dans une situation pas foufou… Surtout que je n’avais jamais fait un score aussi bien ? 😭😭

Heureusement il y a les Persistent Volume (PV) et Persistent Volume Claim (PVC) qui permettent la gestion d’un stockage persistant pour nos applications.

Eh bien, go, mettons ça en place ! 🏃‍♂️

Commençons par déclarer notre PV dans le fichier 2048-pv.yaml :

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-game2048
  namespace: game2048
spec:
  accessModes:
	- ReadWriteMany
  storageClassName: hostpath
  capacity:
	storage: 10Mi
  hostPath:
	path: /data/pv-game2048

Ce PersistentVolume utilise le stockage basé sur mon système de fichier local (hostPath). Cela signifie que le stockage est directement disponible dans le répertoire /data/pv-game2048 sur le nœud où est lancé mon pod 2048.

On peut à présent utiliser ce PersistentVolume avec un PersistentVolumeClaim (PVC). Puis dans notre déploiement nous allons demander l’accès à ce stockage et l’attacher à nos pods.

Créons notre Persistent Volume Claim dans un fichier nommé 2048-pvc.yaml :

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-game2048
  namespace: game2048
spec:
  accessModes:
	- ReadWriteMany
  storageClassName: hostpath
  resources:
	requests:
  	storage: 10Mi
  volumeName: pv-game2048

Appliquons ces fichiers à notre cluster avec kubectl apply -f 2048-pv.yaml -f 2048-pvc.yaml, le PersistentVolume et le PersistentVolumeClaim seront créés.

Vous pouvez checker que la création s’est bien effectuée avec la commande suivante : kubectl get pv,pvc -n game2048. Si les champs statuts sont marqués comme Bound , c’est que tout est ok. 🥳

On va ensuite référencer le PVC dans la définition de notre déploiement pour attacher le stockage persistant à nos pods. ça devrait ressembler à ceci :

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: game2048
  name: deployment2048
spec:
  selector:
	matchLabels:
  	app: app2048
  replicas: 2
  template:
	metadata:
  	labels:
    	app: app2048
	spec:
  	volumes:
    	- name: 2048game
      	persistentVolumeClaim: { claimName: pvc-game2048 }
  	containers:
  	- image: public.ecr.aws/l6m2t8p7/docker-2048:latest
    	imagePullPolicy: IfNotPresent
    	name: app2048
    	volumeMounts:
      	- mountPath: /usr/share/nginx/html
        	name: 2048game
    	ports:
    	- containerPort: 80

N’oubliez pas que dans un environnement de production, vous voudrez probablement utiliser des solutions de stockage persistant plus robustes que le stockage basé sur le système de fichier local. 😉

On peut tester la persistance en supprimant nos pods avec la commande kubectl delete pod <pod name> <namespace>

Si à la recréation vous avez toujours votre score, bravo, on passe au level 6️⃣ !

Level 6 – Ingress

Un ingress ?? 🤔🤔 Mais, qu’est-ce que c’est ça encore ?

J’avoue, si vous êtes novice du Kube’ ça peut faire peur alors mettons quelques mots simples dessus ! 🙂

L’utilisation d’un Ingress permet la gestion des accès à notre application, en fournissant des fonctionnalités de routage, de terminaison SSL/TLS ou encore de load balancing.

Sur le marché, il existe 2 resta 😎

Pour ma part, j’aime utiliser Traefik et je vais utiliser Helm pour l’installation. Pas de panique, si vous n’êtes pas à l’aise avec Helm. On reviendra en détails sur ce sujet dans le prochain article. 😉

Donc, pour l’install’ il suffit d’exécuter ces quelques commandes :

#Ajout du chart dans Helm
$ helm repo add traefik https://traefik.github.io/charts
$ helm repo update

#Installation avec helm dans un namespace dédié
$ kubectl create ns traefik
$ helm install --namespace=traefik traefik traefik/traefik

Et c’est touuuuut.

Si vous avez le message suivant, Traefik Proxy v2.10.6 has been deployed successfully on traefik namespace ! faisons notre fichier de conf ingress.yaml afin de rediriger le trafic vers le service de notre petit jeu :

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress2048
  namespace: game2048
spec:
  rules:
  - host: "game.127.0.0.1.nip.io"
	http:
  	paths:
  	- path: /
    	pathType: Prefix
    	backend:
      	service:
        	name: service2048
        	port:
          	number: 80
        	path: /

Une fois appliquée avec la commande kubectl apply -f ingress.yaml, cet Ingress va rediriger le trafic destiné à “game.127.0.0.1.nip.io” vers notre service.

⚠️ Il faudra changer le type de notre service en commantant la ligne type: NodePort

Si en vous rendant sur l’adresse renseignée dans l’attribut host de votre ingress, vous arrivez sur la page du jeu alors je vous invite à passer au level 8️⃣ !

Level 8 – Final Boss

Bravo vous êtes arrivé jusqu’au boss de fin ! 👏👏

Cependant, vous avez certainement remarqué que l’on a fait beaucoup de fichiers yaml et on a dû les appliquer manuellement… Vous vous doutez bien que ce n’est pas trop optimisé et que l’on peut s’améliorer non ? 🤔

Effectivement c’est le cas, alors si cette première partie vous a plu, que vous avez envie d’aller plus loin,  je vous invite à relancer une partie dans mon prochain article qui sortira courant février. 😉

Sur ce, j’espère que cette première partie vous aura plu. On se retrouve rapidement pour la suite !

Published inNon classé