Déploiement sur le cloud de Google d’une application d’entreprise

Par : Mohamed Ali Amdouni

I. Introduction

Dans cet article, nous vous présentons les problèmes auxquels nous avons été confrontés (et les solutions trouvées) lors du déploiement d’une application sur le cloud de Google en utilisant la technologie Kubernetes. Avoir plusieurs services déployés sous un même nom de domaine est une pratique courante pour les applications les plus volumineuses. Avec Kubernetes, vous pouvez exposer une application sur internet en tant que service indépendant en utilisant par exemple un LoadBalancer.

Vous pouvez également exposer plusieurs services en une seule entité virtuelle à l’aide des ressources Ingress.

II. Résultat final

Nous avons mis sur le cloud la plateforme de gestion des absences réglementées de Salto Consulting. Cette plateforme est constituée de trois composants :

  1. IHM : c’est une application frontend développée en React et qui permet aux collaborateurs Salto de gérer leurs absences,
  2. Backend : Il s’agit de ProjeQtor, un logiciel de gestion de projet Open Source qui offre plusieurs fonctionnalités telles que la gestion du planning, la gestion des ressources, la gestion des risques etc. C’est une application développée en PHP et qui expose une partie de ses fonctionnalités en tant que des services Rest. Dans notre cas, nous n’utiliserons que les services CRUD qui concernent la gestion des absences.
  3. Middleware : c’est une application Node.JS qui offre certaines services Rest spécifiques à l’IHM tel que le mode d’authentification SSO, LDAP

Vous trouverez ci-après le diagramme qui présente le scénario d’authentification SSO et de récupération des congés :

L’architecture finale de la répartition de ces composants sur le Cloud que nous avons déployé est la suivante :

III. Kubernetes

Dans cette partie, nous présentons les composants Kubernetes que nous allons utiliser dans notre projet.

  • Pod : Un pod est un groupe d’un ou plusieurs conteneurs (comme des conteneurs Docker), qui partagent une adresse IP et un espace de ports et peuvent communiquer via localhost. Bien que Kubernetes prenne en charge d’autres runtimes de conteneurs que Docker, Docker est le runtime le plus connu, et cela aide à décrire des pods en termes Docker.

    ]14 Exemple de déclaration d’un Pod qui utilise l’image docker nginx

  • Deployment : Dans le contexte de Kubernetes, un déploiement est une description de l’état souhaité du système. Grâce à un déploiement, vous informez votre cluster Kubernetes du nombre de pods d’une application particulière que vous souhaitez exécuter. Dans le cas ci-après, vous spécifiez que vous souhaitez trois pods :

    ]15 Exemple de déclaration d’un déploiement

  • Node Port : Un service NodePort est le moyen le plus basique pour acheminer le trafic externe directement vers votre service. NodePort, comme son nom l’indique, ouvre un port spécifique sur tous les nœuds (les machines virtuelles) et tout trafic envoyé à ce port est transféré au service.

  • Ingress : Contrairement à l’exemple ci-dessus, Ingress n’est pas un service. C’est une couche qui se trouve en amont de vos services et agit comme un « routeur intelligent » ou un point d’entrée dans votre cluster. Vous pouvez effectuer différentes tâches avec un Ingress, et il existe de nombreux types de contrôleurs Ingress qui ont des capacités différentes (parmi lesquels nous citons Google Cloud Load Balancer, Nginx, Contour, Istio …). Le contrôleur Ingress GKE (Google Kubernetes Engine), par défaut, fera tourner un équilibreur de charge HTTP(S) pour vous. Cela vous permettra de faire à la fois un routage basé sur un chemin et un sous-domaine vers des services backend. Par exemple, vous pouvez tout envoyer à partir de foo.votredomaine.com au service foo, et tout ce qui se trouve sous le chemin yourdomain.com/bar/ vers le service de bar.

IV. Contexte des APIS

En faisant des exemples simples avec l’Ingress Google, tout semble fonctionnel. Toutefois, lors du déploiement de notre première application nous rencontrons des 404 Not Found. Le problème est lié au contexte de nos applications. Lors de la phase de développement, nous utilisons souvent le chemin root «localhost:port/» comme contexte par défaut. Exemple simple avec notre application middleware :

C’est une application qui expose un service «hello world» en https :

$curl –insecure https://localhost:4430
Hello Node !!!

Mais en déployant cette application sur l’Ingress Google en faisant cet appel :

$ curl https://salto-consulting.fr/conges/middleware
Nous aurons une erreur 404.

En effet, le problème n’est pas lié à l’application mais à l’Ingress Google qui ne supporte pas la réécriture des URLs (https://github.com/kubernetes/ingress-gce/issues/109). C’est à dire qu’après le routage, la requête transmise à notre application (middleware) est https://salto-consulting.fr/conges/middleware au lieu de https://salto-consulting.fr/ et puisque nous n’avons pas déclaré cette route dans notre application on a une erreur 404. D’autres Ingress comme nginx supportent la réécriture de l’URL.

Pour régler ce problème, nous avons rajouté un contexte à toutes les applications :

Node :

React :

Projeqtor: Projeqtor sera déployé sur un serveur apache. Ainsi, lors de la création de l’image docker, nous avons déplacé toutes les sources sous le répertoire projeqtor et ce dernier sert comme contexte de l’application.

V. Health Check

Un deuxième souci que nous avons rencontré était la vérification de l’état de l’application par l’Ingress. En effet, l’Ingress fait un appel http à la route principale de chaque application (root /) chaque x secondes. S’il reçoit une réponse 200, il considère que l’application est fonctionnelle sinon il va la supprimer et créer une nouvelle (un nouveau pod). Le problème se situait au niveau du middleware puisque ce dernier expose ses services en HTTPS et donc, pour tout appel HTTP il retournera une erreur 404.

La solution est d’ouvrir un port pour répondre aux requêtes HTTP.

Et puisque c’est une bonne pratique, nous avons créé nos propres routes de Health Check. Au lieu d’attaquer la route principale, Ingress Google va vérifier l’état des applications en utilisant les routes que nous avons créées.

  • Middleware :

  • React : Puisque l’application React est déployée sur un serveur nginx, nous allons définir la route de health check dans le fichier de configuration de ce dernier.

  • Projeqtor: De même pour projeqtor, nous allons définir cette route dans le fichier de configuration de apache.

VI. Fichiers de déploiement

Deployment FrontEnd :

  • readinessProbe : Les readinessProbe (sondes de préparation) sont conçues pour informer Kubernetes lorsque votre application est prête à accepter du trafic. Kubernetes s’assure que la readinessProbe passe avant d’autoriser un service à envoyer du trafic vers le pod.
  • livenessProbe : Les livenessProbe (sondes de vitalité) permettent à Kubernetes de savoir si votre application est vivante ou morte. Si votre application est vivante, Kubernetes la laisse sinon il va la supprimer et en démarrer une nouvelle pour la remplacer.

Service FrontEnd :

Deployment Middleware :

Service Middleware :

Le fichier manifeste du service spécifie deux ports. L’annotation cloud.google.com/app-protocols indique que lorsqu’un équilibreur de charge HTTP(S) cible le port 80 du service, il doit utiliser le protocole HTTP (pour communiquer avec le pod). Lorsque l’équilibreur de charge cible le port 443 du service, il doit utiliser le protocole HTTPS.

Deployment Projeqtor :

Service Projeqtor :

Et enfin : Ingress :

  • Nous avons utilisé une adresse IP statique fournie par Google pour déployer notre Ingress. Donc, à chaque fois que notre Ingress est supprimé et que nous créons un nouveau, nous aurons la même adresse IP accessible depuis Internet.

  • Le mapping des URLs avec l’Ingress Google est explicite et donc /hello et /hello/* sont deux routes différentes (ce qui n’est pas le cas pour l’Ingress nginx). C’est la raison pour laquelle nous avons rajouté deux routes par application :

Exemple : sans faire ce mapping, cette requête: https://salto-consulting.com/conges/middleware Pourra être acheminé vers l’application FrontEnd au lieu du Middleware.

VII. Mise en place de HTTPS

Dans cette partie, nous expliquons comment améliorer la sécurité de notre application en mettant en place le SSL. Le moyen le plus simple est de créer un certificat Let’s Encrypt. Cette autorité, lancée le 3 décembre 2015 vise à généraliser l’usage de connexions sécurisées sur internet et fournit des certificats, certes gratuits, mais avec une validité de 3 mois d’où la nécessité d’utiliser le Cert Manager.  Cert Manager : cert-manager est un outil Kubernetes qui émet des certificats de divers fournisseurs de certificats, y compris Let’s Encrypt. Il s’assurera que les certificats sont valides et à jour, et tentera de renouveler ces certificats avant leur expiration.

Pour mettre en place le SSL, il faut avoir un nom de domaine. Dans notre cas, c’est salto-consulting.fr. De plus, il faut le configurer pour pointer vers l’adresse IP statique fournie par Google et mentionnée précédemment. Nous avons utilisé Google domains pour créer le nom de domaine et ça coûte 12 euros environ par an, cependant vous pouvez choisir un autre fournisseur de service.

Installation de Cert Manager : Il faut installer Cert Manager sur le cluster en utilisant le lien ci-après ; https://cert-manager.io/docs/installation/kubernetes/

Le Cluster Issuer :

Les ClusterIssuers sont des ressources Kubernetes qui représentent des autorités de certification (AC) capables de générer des certificats signés. Tous les certificats cert-manager nécessitent un émetteur référencé qui est prêt à traiter la demande. Dans notre cas, l’autorité de certification est let’s encrypt et nous utilisons leur serveur de production. Le nom du solveur est le nom de l’Ingress que nous avons déployé. http01 est la méthode utilisée pour prouver que le domain salto-consulting nous appartient. Pour plus de détails voir : https://cert-manager.io/docs/tutorials/acme/http-validation/

Notre Certificat :

La ressource Certificat décrit les méthodes possibles qui peuvent être utilisées pour obtenir notre certificat. Dans notre cas, nous l’obtenons en utilisant le cluster issuer déclaré précédemment. Si le certificat est obtenu avec succès, il sera stocké dans un secret kubernetes appelé salto-fr-tls dans le même espace de noms que le certificat.

Après le déploiement de ces deux fichiers, nous attendons entre 8 à 15 minutes pour que le certificat soit prêt. Vous pouvez utiliser cette commande pour voir l’état d’avancement :

kubectl get certificate salto-fr-certificate -o wide

Une fois le certificat prêt, nous le rajoutons à notre Ingress en utilisant le secret :

sercretName est le secret dans lequel le certificat a été stocké.

Kubernetes.io/ingress.allow-http : cette annotation permet de bloquer toutes les requêtes HTTP (et de forcer l’utilisation de l’HTTPS ainsi).

Il faut attendre quelques minutes pour que les changements soient prises en compte et ce n’est qu’après que nous pouvons faire ce test :

$ curl https://salto-consulting.fr/conges/middleware/healthz {« status »: »UP »}