🇫🇷 Kit de survie Kubernetes pour les développeurs avec K3S - Partie 2: Le déploiement

Normalement, vous avez lu la première partie de ce kit de survie Kubernetes pour les devs. Vous avez donc un cluster qui tourne sur votre machine. Vous êtes prêt à jouer avec et à déployer votre 1ère webapp.

Déployer (et redéployer) facilement une webapp NodeJS sur K3S

J'ai une grosse appétence pour le JavaScript, je trouve que c'est simple à mettre en oeuvre. Nous allons donc commencer par une application Express, mais ne vous inquietez pas, dans le futur, nous ferons du Kotlin 🙂

Alors pas besoin de vous taper tous le code, je vous ai préparé un projet ici: https://gitlab.com/learn-k3s/amazing-web-app

Donc il vous suffit de le cloner: git@gitlab.com:learn-k3s/amazing-web-app.git

Cette application est fournie avec un Dockerfile très simple qui va nous permettre de construire une image et de déployer un container sur notre cluster.

Pré-requis: il vous faut un compte sur le Docker Hub et un client Docker installé

le Dockerfile

FROM node:12.0-slim
COPY . .
RUN npm install
CMD [ "node", "index.js" ]

1er déploiement pas à pas

Nous allons tout d'abord créer un fichier yaml qui va expliquer à kubectl comment déployer notre application. Vous pouvez mettre ce fichier n'importe où dans votre projet, mais par convention, on le trouve le plus souvent dans un sous-répertoire kube. Je vais appeler ce fichier deploy.yaml (je n'ai pas vérifié si il y avait une convention).

Ce fichier comporte 3 parties (vous pouvez même le splitter en 3 fichiers, mais je préfère avoir tout au même endroit)

Notre application est "représentée" dans Kubernetes par:

  • un service
  • un déploiement
  • un ingress

✋ pas de panique, en général vous ne serez pas seul pour écrire ce type de fichier

1ère partie: "Service"

---
# Service
apiVersion: v1
kind: Service
metadata:
  name: amazing-web-app
spec:
  selector:
    app: amazing-web-app
  ports:
    - port: 80
      targetPort: 8080
  • Service: "Une manière abstraite d’exposer une application s’exécutant sur un ensemble de Pods en tant que service réseau."
  • 📘 Référence: https://kubernetes.io/fr/docs/concepts/services-networking/service/
  • Pod: "Un Pod est l’unité d’exécution de base d’une application Kubernetes–l’unité la plus petite et la plus simple dans le modèle d’objets de Kubernetes–que vous créez ou déployez. Un Pod représente des process en cours d’exécution dans votre Cluster."
  • 📘 Référence: https://kubernetes.io/fr/docs/concepts/workloads/pods/pod-overview/

🖐 dans notre cas nous aurons un pod qui va encapsuler notre container

Les données importantes à connaître sont:

  • le nom de l'application (et du service, car j'ai donné le même au deux): amazing-web-app
  • le port http (dans notre cas) de notre application: 8080
  • le port exposé: 80

2ème partie: "Deployment"

---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: amazing-web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: amazing-web-app
  template:
    metadata:
      labels:
        app: amazing-web-app
    spec:
      containers:
        - name: amazing-web-app
          image: k33g/amazing-img:0.0.0
          ports:
            - containerPort: 8080
          imagePullPolicy: Always
  • Déploiement: "Le déploiement instruit Kubernetes de comment créer et mettre à jour des instances de votre application"
  • 📘 Références:
    • https://kubernetes.io/fr/docs/tutorials/kubernetes-basics/deploy-app/deploy-intro/
    • https://kubernetes.io/fr/docs/concepts/workloads/controllers/deployment/
  • Réplicas: "Un ReplicaSet (ensemble de réplicas en français) a pour but de maintenir un ensemble stable de Pods à un moment donné. Cet objet est souvent utilisé pour garantir la disponibilité d’un certain nombre identique de Pods."
  • 📘 Référence: https://kubernetes.io/fr/docs/concepts/workloads/controllers/replicaset/

🖐 dans notre cas nous aurons un ensemble de 3 réplicas de notre pod (et nous aurns donc un système "automatique" de loadbalancing pour notre application sur ces 3 pods)

Les données importantes à connaître sont:

  • le nom de l'application: amazing-web-app
  • le port http (dans notre cas) de notre application: 8080
  • le nom de l'image Docker: k33g/amazing-img:0.0.0

🖐 k33g c'est mon nom(handle) de user sur le docker hub, donc si le votre c'est bob, pensez à changer 😉

3ème partie: "Ingress"

---
# Ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: amazing-web-app
spec:
  rules:
    - host: amazing-web-app.192.168.64.17.nip.io
      http:
        paths:
          - backend:
              serviceName: amazing-web-app
              servicePort: 80
  • Ingress: "Un Ingress est un objet Kubernetes qui gère l’accès externe aux services dans un cluster, généralement du trafic HTTP. Un Ingress peut fournir un équilibrage de charge, une terminaison TLS et un hébergement virtuel basé sur un nom."
  • 📘 Référence: https://kubernetes.io/fr/docs/concepts/services-networking/ingress/

Les données importantes à connaître sont:

  • le nom du service (et application): amazing-web-app
  • le port http exposé: 80
  • l'url de notre application: amazing-web-app.192.168.64.17.nip.io

🖐 🤔 Mais c'est quoi cette URL?amazing-web-app.192.168.64.17.nip.io: nip.io est un service de "DNS wildcard" bien pratique pour du test. Par exemple, un nom comme hello.127.0.0.1.nip.io vas résoudre/pointer sur 127.0.0.1. Et nous nous utiliserons l'adresse ip de notre cluster (enfin du noeud master).

Nous avions appelé notre noeud principal basesar, donc pour récupérer son adresse IP, vous pouvez utiliser la commande multipass list ou encore mieux (car très pratique dans un script):

CLUSTER_IP=$(multipass info basestar | grep IPv4 | awk '{print $2}')
echo $CLUSTER_IP 

🖐🖐🖐 Pensez bien à changer l'adresse IP dans le fichier YAML avec l'IP de votre cluster

Enregistrez votre fichier deploy.yaml dans le répertoire kube de votre projet.

Donc maintenant vous êtes prêt à déployer votre application, voici les étapes:

  • lancez K9S dans un terminal pour suivre le déploiement:
    export KUBECONFIG=$PWD/k3s.yaml
    k9s --all-namespaces 
    
  • ouvrez un autre terminal

Partie "Docker":

  • positionnez vous dans votre projet: cd amazing-web-app
  • connectez vous au Docker hub: docker login
  • construisez votre image: docker build -t amazing-img . (🖐 n'oubliez pas le . à la fin)
  • taguez votre image: docker tag amazing-img k33g/amazing-img:0.0.0
  • "poussez" votre image sur le Docker Hub: docker push k33g/amazing-img:0.0.0

🖐 mon repository docker est publique sur Docker Hub, donc accessible par mon cluster sans avoir besoin de credentials pour accéder à l'image

Partie "Kube":

  • expliquez à kubectl où est le fichier de configuration du cluster: export KUBECONFIG=../create-cluster/k3s.yaml (🖐 adaptez en fonction de vos besoins)
  • lancez la commande kubectl apply -f ./kube/deploy.yaml

Vous devriez obtenir:

service/amazing-web-app created
deployment.apps/amazing-web-app created
ingress.extensions/amazing-web-app created

Et vous pouvez suivre dans K9S le déploiement de vos pods:

alt k9s

alt k9s

Et enfin, allez à http://amazing-web-app.192.168.64.17.nip.io avec votre navigateur:

alt k9s

🎉 vous avez déployé votre première webapp dans un cluster kube (et en local!!!)

Maintenant toujours avec votre navigateur, appelez la "route" http://amazing-web-app.192.168.64.17.nip.io/api/hello et raffraîchissez votre page plusieurs fois:

alt k9s

alt k9s

alt k9s

Vous notez qu'à chaque refresh le message json change. Si vous allez faire un tour dans le code source de l'application (dans index.js), à chaque lancement de l'application je crée un nom let fancy_name = fancyName() et je fournis ce nom lors de l'appel de /api/hello:

app.get('/api/hello', (req, res) => {
	res.send({
		message: `👋 hello world 🌍`,
		pod: fancy_name
	})
})

Et comme nous avons 3 pods, que Kube fait du "loadbalancing" sur ces 3 pods, et bien nous avons 3 noms.

👋 Remarque: vous trouverez le fichier deploy.yaml sur la branche first-steps du projet https://gitlab.com/learn-k3s/amazing-web-app/-/tree/first-steps

Allez, un dernier petit effort, nous allons voir comment re-déployer après une modification.

Redéploiement

Si vous faites des modifications dans votre code, par exemple, ajoutez une "route" à votre web app:

app.get('/api/vulcan_salute', (req, res) => {
	res.send({
		message: `🖖dif-tor heh smusma (live long and prosper)`,
		pod: fancy_name
	})
})

Il va falloir penser à "re-taguer" votre image et modifier le fichier deploy.yaml en fonction:

docker build -t amazing-img .
docker tag amazing-img k33g/amazing-img:0.0.1 # 👋 on change le tag
docker push k33g/amazing-img:0.0.1 # 👋 on change le tag

Dans le fichier deploy.yaml, allez modifier le tag: image: k33g/amazing-img:0.0.1

et enfin:

kubectl apply -f ./kube/deploy.yaml

Vous devriez obtenir:

service/amazing-web-app unchanged
deployment.apps/amazing-web-app configured
ingress.extensions/amazing-web-app unchanged

👋 vous pouvez noter que seul la partie déploiement a été changée

Faites un curl:

curl http://amazing-web-app.192.168.64.17.nip.io/api/vulcan_salute

Vous devriez obtenir un résultat comme celui ci:

{"message":"🖖dif-tor heh smusma (live long and prosper)","pod":"lingering_field"}

👋 bien-sûr, à chaque fois que vous allez relancer un curl, le nom du pod va changer.

🎉 C'est terminé pour ce premier déploiement. Je vous laisse expérimenter dans vore coin. Pendant ce temps, je vous écris une suite.

Ce que nous avons fait s'appelle du "Rolling Update": https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/

Dans les prochains épisodes nous allons voir comment:

  • automatiser un peu son déploiement (et écrire un plugin pour kubectl)
  • déployer un/des microservice(s) Vert-x (en Kotlin)
  • créer un service discovery grâce à Redis pour nos microservices
  • ... et d'autres petites choses sympas à venir

🖖 J'espère que cela vous a plu. Bien sûr toutes les remarques sont les bienvenues

Last Articles

Last Updated: 23/02/2020 à 06:35:11