Chez True Engineering, nous avons mis en place un processus de livraison continue pour les mises à jour sur les serveurs de nos clients et nous souhaitons partager cette expérience.
Tout d'abord, nous avons développé un système en ligne pour le client et l'avons déployé dans notre propre cluster Kubernetes. Maintenant, notre solution à fort trafic a été transférée sur la plateforme du client, pour laquelle nous avons configuré un processus entièrement automatisé de Continuous Deployment. Grâce à cela, nous avons accéléré le time-to-market – la livraison des modifications dans l'environnement de production.
Dans cet article, nous allons vous parler de toutes les étapes du processus de Continuous Deployment (CD) ou de livraison de mises à jour sur la plateforme du client :
- comment ce processus démarre,
- synchronisation avec le dépôt Git du client,
- compilation du backend et du frontend,
- déploiement automatique de l'application dans l'environnement de test,
- déploiement automatique en production.
Au cours du processus, nous partagerons les détails de la configuration.

1. Démarrage du CD
Le Continuous Deployment commence lorsque le développeur pousse les modifications dans la branche de release de notre dépôt Git.
Notre application fonctionne sur une architecture microservices et tous ses composants sont stockés dans un seul dépôt. Grâce à cela, tous les microservices sont compilés et installés, même si l'un d'eux a été modifié.
Nous avons organisé le travail via un seul dépôt pour plusieurs raisons :
- Convivialité du développement – l'application est en constante évolution, ce qui permet de travailler directement sur tout le code.
- Un pipeline CI/CD unique, qui garantit que l'application comme un système unique passe tous les tests et est livrée dans l'environnement de production du client.
- Élimination de la confusion dans les versions – nous n'avons pas besoin de conserver une carte des versions des microservices ni de décrire la configuration de chaque microservice dans les scripts Helm.
2. Synchronisation avec le dépôt Git du code source du client
Les modifications apportées sont automatiquement synchronisées avec le dépôt Git du client. Une compilation de l'application est configurée, qui se lance après la mise à jour de la branche, ainsi que le déploiement en production. Les deux processus se déroulent dans leur environnement à partir du dépôt Git.
Nous ne pouvons pas travailler directement avec le dépôt du client, car nous avons besoin de nos propres environnements de développement et de test. Pour cela, nous utilisons notre propre dépôt Git, qui est synchronisé avec leur dépôt Git. Dès qu'un développeur pousse des modifications dans la branche correspondante de notre dépôt, GitLab envoie immédiatement ces modifications au client.

Ensuite, il faut procéder à la construction. Cela se compose de plusieurs étapes : la construction du backend et du frontend, des tests et la livraison en production.
3. Construction du backend et du frontend
La construction du backend et du frontend est une tâche parallèle qui s'exécute dans le système GitLab Runner. Sa configuration de construction source réside dans ce même dépôt.
.
GitLab Runner récupère le code du dépôt requis, construit l'application Java à l'aide de la commande et l'envoie au registre Docker. Ici, nous construisons le backend et le frontend, obtenons des images Docker que nous rangeons dans le dépôt du côté du client. Pour gérer les images Docker, nous utilisons .
Nous synchronisons les versions de nos images avec la version de la release qui sera publiée dans Docker. Pour un fonctionnement sans accroc, nous avons effectué plusieurs configurations :
1. Entre l'environnement de test et le conteneur de production, les conteneurs ne sont pas reconstruits. Nous avons effectué des paramétrages afin qu'un même conteneur puisse fonctionner sans reconstruction avec tous les paramètres, variables d'environnement et services tant dans l'environnement de test que dans la production.
2. Pour mettre à jour l'application via Helm, il est nécessaire d'indiquer sa version. Pour nous, la construction du backend, du frontend et la mise à jour de l'application sont trois tâches distinctes, donc il est important d'utiliser partout la même version de l'application. Pour cela, nous utilisons des données de l'historique Git, puisque notre configuration du cluster K8S et de l'application se trouve dans un même dépôt Git.
Nous obtenons la version de l'application à partir des résultats de l'exécution de la commande
git describe --tags --abbrev=7.
4. Déploiement automatique de toutes les modifications dans l'environnement de test (UAT)
L'étape suivante dans ce script de construction consiste à mettre à jour automatiquement le cluster K8S. Cela se produit à condition que toute l'application ait été construite et que tous les artefacts aient été publiés dans le Docker Registry. Ensuite, le mise à jour de l'environnement de test commence.
La mise à jour du cluster est lancée à l'aide de . En cas de problème, Helm annulera automatiquement toutes ses modifications. Aucune surveillance de son fonctionnement n'est nécessaire.
Nous fournissons avec l'assemblage la configuration du cluster K8S. L'étape suivante consiste donc à la mettre à jour : configMaps, déploiements, services, secrets et toute autre configuration K8S que nous avons modifiée.
Après cela, Helm lance une mise à jour RollOut de l'application dans l'environnement de test. Avant que l'application ne soit déployée en production. Cela permet aux utilisateurs de vérifier manuellement les fonctionnalités business que nous avons mises à disposition dans l'environnement de test.
5. Déploiement automatique de toutes les modifications en Prod
Pour déployer la mise à jour dans l'environnement de production, il suffit d'appuyer sur un bouton dans GitLab — et les conteneurs sont immédiatement livrés dans l'environnement de production.
La même application peut fonctionner sans recompilation dans différents environnements — test et production. Nous utilisons les mêmes artefacts, sans modifier quoi que ce soit dans l'application, et les paramètres sont définis de l'extérieur.
La paramétrisation flexible des paramètres de l'application dépend de l'environnement dans lequel cette application sera exécutée. Nous avons externalisé tous les paramètres des environnements : tout est paramétré via la configuration K8S et les paramètres Helm. Lorsque Helm déploie l'assemblage dans l'environnement de test, il applique des paramètres de test, tandis que dans l'environnement de production — des paramètres de production.
Le plus difficile a été de paramétrer tous les services utilisés et les variables qui dépendent de l'environnement, puis de les convertir en variables d'environnement et en descriptions de configuration de paramètres d'environnement pour Helm.
Dans les paramètres de l'application, des variables d'environnement sont utilisées. Leurs valeurs sont définies dans des conteneurs à l'aide de K8S configmap, qui est modélisé à l'aide de modèles Go. Par exemple, pour définir une variable d'environnement pour le nom de domaine, cela peut se faire ainsi :
APP_EXTERNAL_DOMAIN: {{ (pluck .Values.global.env .Values.app.properties.app_external_domain | first) }}
.Values.global.env – cette variable contient le nom de l'environnement (prod, stage, UAT).
.Values.app.properties.app_external_domain – dans cette variable, nous définissons le domaine approprié dans le fichier .Values.yaml
Lors de la mise à jour de l'application, Helm crée à partir des modèles le fichier configmap.yaml et remplit la valeur APP_EXTERNAL_DOMAIN avec la valeur appropriée en fonction de l'environnement dans lequel la mise à jour de l'application commence. Cette variable est définie directement dans le conteneur. L'application y a accès ; par conséquent, la valeur de cette variable sera différente dans chaque environnement de l'application.
Récemment, Spring Cloud a ajouté la prise en charge de K8S, y compris le travail avec les configMaps : . Tant que le projet évolue activement et change radicalement, nous ne pouvons pas l'utiliser en production. Cependant, nous surveillons son état de près et l'utilisons dans les configurations DEV. Dès qu'il se stabilisera, nous passerons de l'utilisation des variables d'environnement à son utilisation.
Au total
Ainsi, le Continuous Deployment est configuré et fonctionne. Toutes les mises à jour se font d'un simple clic. La livraison des modifications vers l'environnement de production est automatique. Et, ce qui est important, les mises à jour ne stoppent pas le fonctionnement du système.

Plans pour l'avenir : migration automatique de la base de données
Nous avons envisagé de mettre à niveau la base de données et de pouvoir annuler ces modifications. En effet, deux versions différentes de l'application fonctionnent simultanément : l'ancienne est en fonctionnement, tandis que la nouvelle est en cours de déploiement. Nous n'éteindrons l'ancienne que lorsque nous aurons vérifié que la nouvelle version fonctionne. La migration de la base de données doit permettre de faire fonctionner les deux versions de l'application.
Par conséquent, nous ne pouvons pas simplement changer le nom d'une colonne ou d'autres données. Mais nous pouvons créer une nouvelle colonne, copier les données de l'ancienne colonne et écrire des triggers qui, lors de la mise à jour des données, copieront et mettront simultanément à jour celles de l'autre colonne. Une fois le déploiement de la nouvelle version de l'application réussi, après la période de support post-lancement, nous pourrons supprimer l'ancienne colonne et le trigger devenu inutile.
Si la nouvelle version de l'application ne fonctionne pas correctement, nous pouvons revenir à la version précédente, y compris à la version antérieure de la base de données. En résumé, nos modifications permettront de faire fonctionner simultanément plusieurs versions de l'application.
Nous prévoyons d'automatiser la migration de la base de données via un job K8S, intégrant cela dans le processus de CD. Et nous partagerons certainement cette expérience sur Habr.
Source : habr.com
