
Introduction
đ Dans un monde oĂč les utilisateurs exigent une disponibilitĂ© constante, mĂȘme de courts arrĂȘts de service peuvent entraĂźner des pertes significatives et compromettre l’expĂ©rience client. Ce guide propose une approche moderne et pratique pour garantir des dĂ©ploiements sans interruption, en utilisant une stratĂ©gie de dĂ©ploiement blue-green sur Google Cloud Platform (GCP). đ
L’objectif est de vous guider dans la crĂ©ation dâune application Ă Ă©tat, hĂ©bergĂ©e sur Compute Engine, qui puisse ĂȘtre mise Ă jour sans aucun temps d’arrĂȘt. Cette mĂ©thode est idĂ©ale pour les entreprises nĂ©cessitant un accĂšs ininterrompu, notamment celles oĂč la continuitĂ© de service est cruciale. đŒâš
Dans ce guide, nous allons dĂ©tailler Ă©tape par Ă©tape l’implĂ©mentation complĂšte de ce projet. đđ ïž
Pour commencer, si vous souhaitez suivre cette implĂ©mentation, veuillez cloner le dĂ©pĂŽt suivant https://github.com/DJTJ21/Blue-Green-deployment-application-code.git qui contient uniquement le code de l’application. Nous allons le complĂ©ter ensemble progressivement. đ»đ§
Si vous prĂ©fĂ©rez avoir le code final prĂȘt Ă l’emploi, vous pouvez cloner le dĂ©pĂŽt suivant https://github.com/DJTJ21/Blue-Green-terraform-GCP-packer-github-Action-project.git . Pour ceux qui aimeraient directement dĂ©ployer la solution, je prĂ©parerai un petit article dĂ©diĂ©. đâš
Sans plus tarder, lançons-nous dans l’implĂ©mentation du projet ! đđ§
Ătape 1 : CrĂ©ation dâun projet sur GCP đ
Pour créer un projet sur Google Cloud Platform (GCP) et établir une infrastructure robuste, suivez ces étapes :
- Création du projet :
- Une fois connectĂ© Ă la console , cliquez sur « SĂ©lectionner un projet » et ensuite sur « Nouveau projet« . đ ïž
- Donnez un nom au projet et sĂ©lectionnez l’organisation (facultatif) et l’emplacement. Cliquez sur « CrĂ©er« . đâš
- Activation de la facturation :
- Allez dans « Facturation » et associez une mĂ©thode de paiement au projet. đłđ§Ÿ
Configuration du Compte de Service et Activation des APIs
1. Création et Configuration du Compte de Service
AprÚs avoir configuré votre projet GCP, créez un compte de service ou utilisez le compte de service par défaut créé lors de la création du projet et assignez les rÎles suivants :
- roles/secretmanager.secretAccessor : accĂšs aux secrets pour la gestion sĂ©curisĂ©e des informations. đ
- roles/storage.objectAdmin : administration des objets dans Cloud Storage. đŠ
- roles/compute.imageUser : utilisation dâimages pour les VM Compute Engine. đ„ïž
- roles/cloudsql.editor : gestion des bases de donnĂ©es Cloud SQL. đïž
- roles/logging.logWriter : accĂšs pour Ă©crire des journaux. đ
Pour ce faire, cliquez sur la barre de menu et naviguez dans IAM et Administration :

Vous verrez le compte de service par défaut créé lors de la mise en place du projet.

Cliquez ensuite sur le crayon tout Ă droite pour effectuer lâattribution de rĂŽle. đïžđ§

Cliquez ensuite sur ajouter un autre rĂŽle. âđ§

Faite un filtre sur le role que vous aimeriez ajouter par exemple pour le role roles/logging.logWriter on a ceci :

Ensuite vous cliquez sur le rÎle puis sur enregistrer. Vous répÚteriez la action pour tous les autres rÎles.
2. Activation des APIs Requises
Pour permettre la création de certaines ressources, vous devez activer plusieurs APIs dans votre projet, notamment les suivantes :
- Compute Engine API (compute.googleapis.com) : pour la gestion des VM et autres ressources de calcul. đ„ïž
- IAM Service Account API (iam.googleapis.com) : pour gĂ©rer les comptes de service. đ€
- Cloud Resource Manager API (cloudresourcemanager.googleapis.com) : pour la gestion des ressources et des permissions. đ§
- SQL Admin API (sqladmin.googleapis.com) : pour la gestion des bases de donnĂ©es Cloud SQL. đïž
- Secret Manager API (secretmanager.googleapis.com) : pour stocker et accĂ©der aux secrets. đ
- Service Networking API (servicenetworking.googleapis.com) : pour la configuration des rĂ©seaux et des connexions entre services. đ
Pour activer les APIs :
Cliquez sur la barre de menu et naviguez dans API et services puis dans bibliothĂšque.

Recherchez lâAPI que vous souhaitez activer dans la barre de recherche, par exemple pour lâAPI SQL Admin API, puis cliquez sur « Activer »

Ensuite vous verrez un bouton Activer sâil nâest pas encore activĂ© dans mon cas je lâai dĂ©jĂ activĂ©e.
Bravo! Vous avez maintenant créé un projet, activĂ© les APIs et configurĂ© les rĂŽles nĂ©cessaires. đđ
Installation et Configuration de Google CLI (gcloud)
Pour installer Google Cloud CLI (gcloud), suivez ces étapes :
- Télécharger et Installer le Google Cloud SDK :
- đ Rendez-vous sur le site du Google Cloud SDK et tĂ©lĂ©chargez le package adaptĂ© Ă votre systĂšme d’exploitation (Windows đȘ, macOS đ ou Linux đ§). Les instructions d’installation y sont trĂšs bien dĂ©taillĂ©es, donc je n’y reviendrai pas ici. đ
- Initialiser le SDK :
- AprĂšs l’installation, lancez la commande suivante pour configurer le CLI:

- Suivez les instructions pour vous connecter, configurer le projet creer ci dessus.
- AprĂšs l’installation, lancez la commande suivante pour configurer le CLI:
Ătape 2 : CrĂ©ation des images Docker đł et de lâimage VM packer đ» avec Cloud Build
Nous allons maintenant plonger dans l’implĂ©mentation ! Si vous n’avez pas encore clonĂ© le dĂ©pĂŽt, c’est le moment idĂ©al pour le faire : https://github.com/DJTJ21/Blue-Green-deployment-application-code.git il est pour l’instant constituer uniquement des repertoires de l’application 
Vous remarquerez sans doute que dans le rĂ©pertoire du frontend, il y a un Dockerfile, mais pas dans celui de l’API. đ§ Nous aurions pu utiliser un Dockerfile pour construire l’image, mais dans ce cas, nous allons utiliser le build pack. đđ§
Construction des images docker avec CloudBuild:
dans la racine des repertoires api et frontend creez un fichier cloudbuild.yml dans le fichier cloudbuild de l’api ajouter le contenue suivant :
steps:
- id: configure-docker-authentication
name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: 'gcloud'
args: ['auth', 'configure-docker', '-q']
- id: mvn-install
volumes:
- name: 'm2'
path: /root/.m2
name: 'maven:latest'
entrypoint: 'mvn'
args: ['install', '-DskipTests']
- id: build-and-push-api-docker-image
volumes:
- name: 'm2'
path: /root/.m2
name: 'maven'
entrypoint: 'mvn'
env: ['LDS_IMAGE_NAME=gcr.io/${PROJECT_ID}/${_BACKEND_IMAGE_REPO_NAME}:${_BACKEND_IMAGE_TAG}']
args: ['compile', 'com.google.cloud.tools:jib-maven-plugin:3.3.1:build']
substitutions:
_BACKEND_IMAGE_REPO_NAME: 'techworld-backend'
_BACKEND_IMAGE_TAG: 'latest'
PROJECT_ID: 'votre-id-project'
options:
machineType: 'E2_MEDIUM'
logging: 'CLOUD_LOGGING_ONLY'
Nous allons clarifier les différentes étapes de ce fichier :
Configuration de l’authentification Docker :

Cette Ă©tape configure l’authentification Docker pour permettre Ă Google Cloud Build de pousser des images Docker vers le registre de conteneurs Google. đđ
Installation des dependances Maven :

Cette Ă©tape installe les dĂ©pendances Maven sans exĂ©cuter les tests, en utilisant le volume /root/.m2 pour stocker les artefacts Maven en cache. đŠ
Construire et Pousser l’Image Docker de l’API :

Cette Ă©tape compile l’application API et utilise le plugin Jib de Maven pour construire et pousser l’image Docker vers Google Container Registry (GCR) en utilisant l’authentification configurĂ©e prĂ©cĂ©demment. đ ïžđą
Substitutions:

Ces substitutions permettent de dĂ©finir des variables personnalisĂ©es utilisĂ©es dans le fichier. Ici, le nom du dĂ©pĂŽt d’images backend, la balise d’image et l’ID du projet GCP. đđ„ïž
Options :

machineType: SpĂ©cifie le type de machine utilisĂ©e pour le build. Ici, une machine de type E2_MEDIUM est utilisĂ©e. đ»âïž
logging: DĂ©finit les options de journalisation, ici configurĂ©es pour n’utiliser que Cloud Logging. đđ
Maintenant que nous avons bien compris le contenu du fichier cloudbuild, nous allons construire l’image Ă partir de ce fichier. Pour cela, naviguez dans le rĂ©pertoire api/ et exĂ©cutez la commande suivante :
gcloud builds submit --config cloudbuild.yamlVous ne verrez aucun log pendant le processus; c’est tout Ă fait normal. đ ïž En revanche, un bucket sera créé dans votre projet pour y stocker tous les logs. đđ
Une fois l’exĂ©cution terminĂ©e, si tout s’est bien passĂ©, vous obtiendrez une sortie similaire Ă celle-ci. â âš

Vous pouvez tout de mĂȘme consulter les logs sur votre terminal grĂące Ă cette commande : đđ»

Si nous naviguons dans notre projet GCP dans Artifact Registry, nous pouvons voir que l’image du backend a Ă©tĂ© mise Ă jour.

A present passons au fichier cloudbuild du frontend ajoutez y le contenue suivant :
steps:
- id: build-frontend-blue-docker-image
name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/${PROJECT_ID}/${_FRONTEND_IMAGE_REPO_NAME}:blue', '.']
- id: push-frontend-blue-docker-image
name: 'gcr.io/cloud-builders/docker'
args: ['push', 'gcr.io/${PROJECT_ID}/${_FRONTEND_IMAGE_REPO_NAME}:blue']
- id: prepare-frontend-green-styles
name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: 'cp'
args: ['src/green.index.html', 'src/index.html']
- id: build-frontend-green-docker-image
name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/${PROJECT_ID}/${_FRONTEND_IMAGE_REPO_NAME}:green', '.']
- id: push-frontend-green-docker-image
name: 'gcr.io/cloud-builders/docker'
args: ['push', 'gcr.io/${PROJECT_ID}/${_FRONTEND_IMAGE_REPO_NAME}:green']
substitutions:
_FRONTEND_IMAGE_REPO_NAME: 'techworld-frontend'
options:
machineType: 'E2_MEDIUM'
logging: 'CLOUD_LOGGING_ONLY'
Nous allons clarifier les différentes étapes de ce fichier :
Build Frontend Blue Docker Image :

Cette Ă©tape construit une image Docker pour le frontend en utilisant le Dockerfile et la tagge avec le tag blue. đ ïžđ”
Push Frontend Blue Docker Image :

Cette Ă©tape pousse l’image Docker construite prĂ©cĂ©demment vers Google Container Registry (GCR) . đđ”
Prepare Frontend Green Styles :

Cette Ă©tape copie le fichier green.index.html vers index.html pour prĂ©parer les styles du frontend pour l’image :greenđżđ„ïž.
Build Frontend Green Docker Image :

Cette Ă©tape construit une image Docker pour le frontend en utilisant les styles prĂ©parĂ©s pour :green et la tagge avec :green. đ ïžđż
Push Frontend Green Docker Image :

Cette Ă©tape pousse l’image Docker :green construite prĂ©cĂ©demment vers GCR. đđż
Les deux derniĂšres sections ayant dĂ©jĂ Ă©tĂ© abordĂ©es prĂ©cĂ©demment, passons maintenant Ă la construction des images du frontend. đ Pour cela, naviguez dans le rĂ©pertoire frontend oĂč vous avez créé le fichier cloudbuild et exĂ©cutez la commande suivante :
gcloud builds submit --config=cloudbuild.ymlLe processus prendra un peu de temps. Si l’opĂ©ration rĂ©ussit, vous obtiendrez une sortie semblable Ă celle-ci. â â

Une fois terminĂ©, si nous naviguons dans notre projet GCP, dans Artifact Registry, nous pouvons voir que les images du frontend ont Ă©tĂ© mises Ă jour. đđ

Maintenant que nous avons fini de construire les images, passons Ă l’Ă©tape suivante : utiliser ces images pour crĂ©er une VM personnalisĂ©e avec Packer. đâš
Construction de l’image VM Packer avec cloudbuild
đ„ Packer, dĂ©veloppĂ© par HashiCorp, est un outil open-source puissant conçu pour simplifier la crĂ©ation d’images de machines. Avec Packer, vous pouvez crĂ©er des images identiques pour diverses plateformes (AWS, Azure, GCP, VMware, etc.) Ă partir d’une seule configuration source
Points clés de Packer :
- Automatisation : Packer automatise la crĂ©ation d’images, rĂ©duisant ainsi les erreurs humaines et augmentant la consistance des dĂ©ploiements.
- Multiplateforme : Il supporte de multiples environnements, vous permettant de déployer des images sur plusieurs infrastructures avec une seule configuration.
- Rapide et LĂ©ger : Packer est conçu pour ĂȘtre rapide et lĂ©ger, facilitant des cycles de build rapides et une intĂ©gration fluide dans vos pipelines CI/CD.
- CompatibilitĂ© avec les outils de gestion de configuration : Packer s’intĂšgre avec des outils comme Chef, Puppet et Ansible pour configurer vos images pendant le processus de crĂ©ation.
Maintenant que nous avons dĂ©couvert Packer đ, continuons avec notre mise en Ćuvre ! đ Ă la racine du projet, crĂ©ez un rĂ©pertoire nommĂ© cloudbuild-packer-image/ đïž. Ensuite, naviguez Ă l’intĂ©rieur et crĂ©ez deux autres rĂ©pertoires : config/ et tools/ đ ïž. (Je suppose que tout le monde sait crĂ©er un rĂ©pertoire, non ? đ). Le rĂ©pertoire config/ contiendra notre code Packer, tandis que le rĂ©pertoire tools/ renfermera les Ă©lĂ©ments qui seront provisionnĂ©s lors de la crĂ©ation de l’image de notre VM đ„ïž.
Dans le rĂ©pertoire tools/, crĂ©ez un fichier install_package.sh qui contiendra le script d’installation des prĂ©requis nĂ©cessaires pour faire fonctionner notre application dans la VM Ă crĂ©er. đ„ïž N’oubliez pas d’y ajouter le contenu ci-dessous ! đđ
cloud-init status -w
# Install docker, docker-compose, and mysql client
sudo apt-get update
sudo apt-get install -y docker.io docker docker-compose
sudo apt install -y mysql-client-core-8.0
gcloud auth configure-docker -q
sudo usermod -aG docker "$USER"
Le contenu est assez accessible pour tout le monde, donc je ne vais pas m’attarder sur les explications. đ Maintenant, dans le mĂȘme rĂ©pertoire, crĂ©ez un fichier docker-compose.yml qui utilisera les images construites prĂ©cĂ©demment pour dĂ©marrer les conteneurs applicatifs lors de l’initialisation de la VM. Ajoutez y le contenue suivant :
version: '3.9'
services:
api:
image: gcr.io/techworld-438822/techworld-backend:latest
container_name: lds-api
environment:
- LDS_BUCKET=${LDS_BUCKET}
- LDS_RESOURCE_PATH=${LDS_RESOURCE_PATH}
- LDS_REST_PORT=${LDS_REST_PORT}
- LDS_MYSQL_INSTANCE=${LDS_MYSQL_INSTANCE}
- LDS_MYSQL_DATABASE=${LDS_MYSQL_DATABASE}
- LDS_MYSQL_USERNAME=${LDS_MYSQL_USERNAME}
- LDS_MYSQL_PASSWORD=${LDS_MYSQL_PASSWORD}
- DATA_INIT_BUCKET_NAME=${DATA_INIT_BUCKET_NAME}
- DATA_INIT_FILE_NAME=${DATA_INIT_FILE_NAME}
ports:
- ${LDS_REST_PORT}:${LDS_REST_PORT}
networks:
- lds
logging:
driver: gcplogs
web:
image: gcr.io/techworld-438822/techworld-frontend:${FRONTEND_VERSION_TAG}
container_name: lds-web
environment:
- LDS_REST_URL=${LDS_REST_URL}
ports:
- '80:80'
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
- lds
logging:
driver: gcplogs
networks:
lds:
driver: bridge
N’oubliez pas de modifier les noms des images avec les vĂŽtres !
Ă prĂ©sent, dans le rĂ©pertoire config/, crĂ©ez deux fichiers : jss_cd.pkr.hcl et variable.pkr.hcl, qui contiendront le code Packer chargĂ© de crĂ©er notre image VM. đ„ïž . Ajoutez le contenu suivant dans le fichier variable.pkr.hcl. đđ§
variable "project_id" {
type = string
}
variable "region" {
type = string
default = "us-central1"
}
variable "zone" {
type = string
default = "us-central1-a"
}
variable "img_name" {
type = string
}
variable "img_desc" {
type = string
}
variable "file_install_package_sh" {
type = string
default = "../tools/install_package.sh"
}
variable "file_docker_compose_yml" {
type = string
default = "../tools/docker-compose.yml"
}
C’est un simple fichier de variables, similaire Ă celui de Terraform, donc rien de trĂšs nouveau Ă ce niveau (si tu es familier avec Terraform đ ).
dans le fichier jss_cd.pkr.hcl ajoutez le contenue ci-dessous :
packer {
required_plugins {
googlecompute = {
version = "1.0.16"
source = "github.com/hashicorp/googlecompute"
}
}
}
source "googlecompute" "main" {
project_id = "${var.project_id}"
image_storage_locations = [
"${var.region}",
]
zone = "${var.zone}"
image_name = "${var.img_name}"
image_description = "${var.img_desc}"
image_labels = {
devops = "techworld-with-romuald"
}
image_family = "jss-cd"
source_image_family = "ubuntu-2204-lts"
ssh_username = "root"
network = "default"
machine_type = "n2-standard-2"
}
build {
sources = ["sources.googlecompute.main"]
provisioner "file" {
sources = [
"${var.file_docker_compose_yml}",
]
destination = "/"
}
provisioner "shell" {
script = "${var.file_install_package_sh}"
}
}
Configuration de Packer:

Ce bloc configure Packer pour utiliser le plugin googlecompute (version 1.0.16) depuis le registre HashiCorp, qui permet de créer des images VM sur Google Cloud Platform (GCP).
Definition de la source :

Ce bloc dĂ©finit les paramĂštres de l’image sur GCP :
- project_id : identifiant du projet GCP.
- image_storage_locations et zone : emplacement et zone de stockage de l’image.
- image_name, image_description, image_labels : informations sur l’image (nom, description, Ă©tiquettes).
- image_family et source_image_family : famille de l’image cible et source (basĂ©e sur Ubuntu 22.04 LTS).
- ssh_username : identifiant SSH pour accéder à la VM.
- network et machine_type : réseau par défaut et type de machine (ici,
n2-standard-2).
Bloc de construction :

Ce bloc build exĂ©cute la configuration sur l’image cible :
sources: utilise la sourcegooglecompute.maindéfinie plus haut.- Provisioners :
file: transfĂšre le fichier Docker Compose (spĂ©cifiĂ© dansfile_docker_compose_yml) vers la VM.shell: exĂ©cute un script d’installation des prĂ©requis (dĂ©fini dansfile_install_package_sh).
En definitif ce fichier packer configure un plugin pour Google Compute, dĂ©finit la source d’image avec des paramĂštres spĂ©cifiques (comme le nom, la zone, et les Ă©tiquettes), et utilise des provisioners pour copier des fichiers et exĂ©cuter des scripts shell dans l’image créée. đđ ïž
âš Maintenant, Ă la racine de votre rĂ©pertoire cloudbuild-packer-image/, crĂ©ez le fichier cloudbuild_packer_vm_img.yaml đ et ajoutez-y le contenu suivant ! đ
steps:
- name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
id: "Remove app image"
env:
- VM_IMAGE_NAME=$_VM_IMAGE_NAME
script: |
if [ -z $(gcloud compute images describe ${VM_IMAGE_NAME} --verbosity=none --format=text) ]; then
echo "The image does not exist. Proceeding with next build step."
else
echo "Deleting the image."
gcloud compute images delete ${VM_IMAGE_NAME} -q --verbosity=none
fi
- name: 'gcr.io/${_PACKER_BUILDER_PROJECT}/packer'
dir: 'config'
args:
- init
- .
- name: 'gcr.io/${_PACKER_BUILDER_PROJECT}/packer'
dir: 'config'
args:
- build
- -var
- project_id=$PROJECT_ID
- -var
- region=$_LOCATION
- -var
- zone=$_ZONE
- -var
- img_name=$_VM_IMAGE_NAME
- -var
- img_desc=Jss cd image from Packer which is triggered by Cloud Build
- -var
- file_install_package_sh=../tools/install_package.sh
- -var
- file_docker_compose_yml=../tools/docker-compose.yml
- .
- name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
id: "Public app image"
env:
- VM_IMAGE_NAME=$_VM_IMAGE_NAME
script: |
gcloud compute images add-iam-policy-binding ${VM_IMAGE_NAME} --member='allAuthenticatedUsers' --role='roles/compute.imageUser'
substitutions:
_LOCATION: "us-central1"
_ZONE: "us-central1-a"
_VM_IMAGE_NAME: "techworld-vm-image"
_PACKER_BUILDER_PROJECT: "hsa-public"
options:
logging: CLOUD_LOGGING_ONLY
Voici les principales etapes de notre fichier :
Suppression de l’Image Existante đ
- Cette étape vérifie si une image VM existe déjà et la supprime si elle est présente. Cela garantit que nous travaillons toujours avec les images les plus récentes.
Initialisation de Packer đ
- Packer est utilisĂ© pour initialiser la configuration. Cela prĂ©pare l’environnement pour crĂ©er de nouvelles images VM.
Construction de l’Image VM avec Packer đ ïž
- Packer construit l’image VM en utilisant les variables fournies (comme l’ID du projet, la rĂ©gion, la zone, etc.) et les fichiers/scripts spĂ©cifiĂ©s. Cette Ă©tape assure que notre image est correctement configurĂ©e et prĂȘte Ă l’emploi.
Publication de l’Image đ
- Une fois l’image construite, des rĂšgles de politique IAM sont ajoutĂ©es pour rendre l’image publique, permettant Ă tous les utilisateurs authentifiĂ©s d’y accĂ©der.
Ă ce stade, l’arborescence de notre projet devrait ĂȘtre la suivante : đłđ

Passons maintenant Ă la crĂ©ation de l’image ! Pour cela, rendez-vous dans le rĂ©pertoire cloudbuild-packer-image đ et exĂ©cutez la commande suivante (que vous connaissez dĂ©jĂ trĂšs bien đ) :
gcloud builds submit --config=cloudbuild_packer_vm_img.yamlđ Une fois la construction terminĂ©e avec succĂšs, la sortie affichĂ©e sera identique Ă celle des Ă©tapes prĂ©cĂ©dentes.

Pour voir votre magnifique image, rendez-vous sur la console GCP đ„ïž ! Dans la barre de recherche, tapez Compute Image, puis cliquez sur Images.

Assurez-vous que le message sous Images affiche bien Compute Engine â câest lĂ que votre crĂ©ation vous attend ! âš

Etape 3 : Insfrastructure as code avec terraform
Maintenant, on attaque la partie que je considĂšre sĂ»rement comme la plus corsĂ©e⊠du moins pour moi ! đ (Jâavoue, jâai du mal Ă la rĂ©diger, elle est un peu longue, mais bon, on va y arriver ! đȘ). Allez, courage, on en sortira plus forts ! đ
afin de mieux vous situez sur l’infrastructure que nous allons mettre en place voici un shema de l’infrastructure question de voir un peu plus clair:

đ Allez, câest parti pour le code Terraform de notre super infrastructure ! đ
Avant tout, mettons un peu d’ordre : crĂ©ez un rĂ©pertoire appelĂ© terraform-deployment. Ă l’intĂ©rieur, ajoutez un rĂ©pertoire modules oĂč on va ranger tous nos modules (oui, oui, nos prĂ©cieux composants rĂ©utilisables đ§©).
Ensuite, dans ce répertoire modules, on va organiser un peu notre monde :
database: pour tout ce qui est stockage et base de donnĂ©es đload-balancer: pour le module qui gĂ©rera notre Ă©quilibrage de charge đ§ââïžnetworking: pour le rĂ©seau, parce que la connexion, câest la clĂ© đvm-container: pour tout ce qui est crĂ©ation de nos Compute Engines âïž
VoilĂ , avec tout ça en place, on est prĂȘt Ă lancer notre infrastructure vers les Ă©toiles ! đ
đ© Et maintenant, place au module database ! đâš
Allez, préparez vos claviers, et lançons ce module database pour que vos données se sentent comme chez elles.
Débutons avec le fichier main.tf :
resource "google_sql_database_instance" "main" {
name = "cloud-deployment-gce-java-db"
database_version = "MYSQL_8_0"
region = var.region
settings {
availability_type = var.availability_type
backup_configuration {
enabled = true
binary_log_enabled = true
}
location_preference {
zone = var.zone
}
tier = "db-custom-2-4096"
disk_type = "PD_SSD"
disk_size = 20
ip_configuration {
private_network = var.vpc_network_self_link
ipv4_enabled = false
}
}
deletion_protection = false
depends_on = [google_service_networking_connection.private_vpc]
}
resource "google_sql_database" "main" {
name = "cloud_deployment_gce_db" # equal to api/src/main/resources/db/schema.sql
charset = "utf8"
collation = "utf8_general_ci"
instance = google_sql_database_instance.main.name
}
resource "google_sql_user" "main" {
name = "cloud-deployment-gce-java"
instance = google_sql_database_instance.main.name
password = random_password.sql_password.result
host = "%"
}
resource "google_compute_global_address" "sql" {
name = "cloud-deployment-gce-java-db-address"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = 20
network = var.vpc_network_self_link
}
resource "random_password" "sql_password" {
length = 20
min_lower = 4
min_numeric = 4
min_special = 4
min_upper = 4
override_special = "!@#*()-_=+[]{}:?"
}
resource "google_secret_manager_secret" "sql_password" {
secret_id = "cloud-deployment-gce-java-db-password"
project = var.project_id
replication {
automatic = true
}
}
resource "google_secret_manager_secret_version" "sql_password" {
secret = google_secret_manager_secret.sql_password.id
enabled = true
secret_data = random_password.sql_password.result
}
resource "google_secret_manager_secret_iam_member" "sql_password" {
project = google_secret_manager_secret.sql_password.project
secret_id = google_secret_manager_secret.sql_password.secret_id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${var.service_account}"
}
resource "google_service_networking_connection" "private_vpc" {
network = var.vpc_network_self_link
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [
google_compute_global_address.sql.name,
]
}
Allons-y, tentons vaillamment de dĂ©cortiquer ce fichier ! đđ ïž
đïž CrĂ©ation de l’Instance de Base de DonnĂ©es MySQL :

google_sql_database_instance : Crée une instance MySQL avec les paramÚtres spécifiés.
settings : Configuration de l’instance, incluant le type de disponibilitĂ©, la sauvegarde, la prĂ©fĂ©rence de localisation, le type et la taille du disque, et la configuration IP.
đ CrĂ©ation de la Base de DonnĂ©es :

google_sql_database : CrĂ©e la base de donnĂ©es avec le nom, charset, collation, et l’instance associĂ©e.
đ CrĂ©ation de l’Utilisateur de la Base de DonnĂ©es :

google_sql_user : CrĂ©e un utilisateur pour la base de donnĂ©es avec le nom, l’instance associĂ©e, et le mot de passe gĂ©nĂ©rĂ©.
đ Adresse Globale de Peering VPC :

google_compute_global_address : Crée une adresse globale pour le peering VPC avec les paramÚtres spécifiés.
đ GĂ©nĂ©ration de Mot de Passe AlĂ©atoire :

random_password : GénÚre un mot de passe aléatoire avec les critÚres spécifiés.
Secret Manager pour le Mot de Passe de la Base de Données :

google_secret_manager_secret et google_secret_manager_secret_version : Créent un secret et une version du secret contenant le mot de passe de la base de données.
google_secret_manager_secret_iam_member : Attribue le rĂŽle d’accĂšs au secret Ă un compte de service.
Connexion de Peering de Service :

google_service_networking_connection : Configure une connexion de peering de service avec les paramÚtres spécifiés.
Poursuivons avec les fichiers outputs et variables de ce module :
Voici le contenu du fichier variables.tf :
variable "project_id" {
description = "The project ID to manage the Cloud SQL resources."
type = string
}
variable "region" {
description = "The region of the Cloud SQL resource."
type = string
}
variable "zone" {
description = "Google cloud zone where the resources will be created."
type = string
default = "us-central1-a"
}
variable "vpc_network_self_link" {
description = "network_self_link is the self link of the VPC network to which the Cloud SQL instance is connected."
type = string
}
variable "availability_type" {
description = "The availability type of the Cloud SQL instance, high availability (REGIONAL) or single zone (ZONAL)."
type = string
validation {
condition = contains(["REGIONAL", "ZONAL"], var.availability_type)
error_message = "Allowed values for type are \"REGIONAL\", \"ZONAL\"."
}
}
variable "service_account" {
description = "Service Account which should have read permission to access the database password."
type = string
}
Voici le contenu du fichier outputs.tf :
output "db_ip" {
description = "The IPv4 address assigned for the master instance"
value = google_sql_database_instance.main.private_ip_address
}
output "connection_name" {
description = "The connection name of the instance to be used in connection strings. For example, when connecting with Cloud SQL Proxy."
value = google_sql_database_instance.main.connection_name
}
output "database_name" {
description = "Name of the Cloud SQL database"
value = google_sql_database.main.name
}
output "user_name" {
description = "SQL username"
value = google_sql_user.main.name
}
output "password_secret" {
description = "Name of the secret storing the database password"
value = google_secret_manager_secret.sql_password.secret_id
}
output "password" {
description = "SQL password"
value = random_password.sql_password.result
}
Voici le contenu du fichier versions.tf :
terraform {
required_version = ">= 0.13"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.65"
}
random = {
source = "hashicorp/random"
version = "~> 3.4"
}
}
}
Ce fichier dĂ©finit les versions minimales requises de Terraform et des fournisseurs google et random, garantissant ainsi que notre configuration utilise des versions compatibles pour dĂ©ployer l’infrastructure.
Ăa y est, notre module database est prĂȘt Ă faire des Ă©tincelles ! đâš Maintenant, en route pour le module networking ! đ
đžïž Module Networking : Tissons notre toile de connexions ! đ
Ok, dans notre rĂ©pertoire networking, on va crĂ©er un fichier nommĂ© main.tf. Une fois prĂȘt, on y met le contenu suivant :
resource "google_compute_network" "main" {
name = "cloud-deployment-gce-java"
project = var.project_id
auto_create_subnetworks = true
}
resource "google_compute_firewall" "cloud_deployment" {
name = "cloud-deployment-gce-java-health-check"
network = google_compute_network.main.name
direction = "INGRESS"
source_ranges = [
"130.211.0.0/22", //health check ip
"35.191.0.0/16" //health check ip
]
allow {
protocol = "tcp"
ports = var.health_check_allow_ports
}
}
# need to know what is google_compute_router and its nat.
resource "google_compute_router" "main" {
name = "cloud-deployment-gce-java-router"
region = var.region
network = google_compute_network.main.name
}
resource "google_compute_router_nat" "main" {
name = "cloud-deployment-gce-java-router-nat"
router = google_compute_router.main.name
region = google_compute_router.main.region
nat_ip_allocate_option = "AUTO_ONLY"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
}
Analisons de plus prĂšs le contenu de ce fichier :
Ressource google_compute_network:

Ici, nous crĂ©ons un rĂ©seau virtuel nommĂ© « cloud-deployment-gce-java » dans le projet spĂ©cifiĂ©. đïž L’option auto_create_subnetworks est activĂ©e, ce qui signifie que Google Cloud gĂ©nĂ©rera automatiquement des sous-rĂ©seaux pour nous. Pas besoin de se prendre la tĂȘte avec ça !
Ressource google_compute_firewall:

Ce bloc dĂ©finit une rĂšgle de pare-feu pour permettre le trafic entrant (INGRESS) sur le rĂ©seau créé. đ„ Il est configurĂ© pour accepter les connexions provenant de certaines plages IP, principalement pour les vĂ©rifications de santĂ© (health checks). On autorise ici le protocole TCP sur les ports spĂ©cifiĂ©s par la variable health_check_allow_ports. đȘ
Ressource google_compute_router:

Ce bloc crĂ©e un routeur qui permettra de gĂ©rer le trafic entre les diffĂ©rentes ressources du rĂ©seau. đĄ Il est associĂ© au rĂ©seau que nous avons prĂ©cĂ©demment créé et est dĂ©ployĂ© dans la rĂ©gion spĂ©cifiĂ©e par la variable region.
Ressource google_compute_router_nat:

Enfin, nous configurons une NAT (Network Address Translation) pour permettre aux instances du rĂ©seau de se connecter Ă Internet tout en restant invisibles depuis l’extĂ©rieur. đ L’option nat_ip_allocate_option est rĂ©glĂ©e sur « AUTO_ONLY », ce qui signifie que l’IP NAT sera attribuĂ©e automatiquement. đ
En rĂ©sumĂ©, ce fichier met en place l’infrastructure rĂ©seau qui soutiendra le bon fonctionnement de nos services cloud, en s’assurant que tout le trafic est gĂ©rĂ© correctement et en toute sĂ©curitĂ© ! .
Ajoutez les éléments suivants au fichier variables.tf, qui contient les variables de notre module :
variable "project_id" {
description = "Google Cloud project ID."
type = string
}
variable "region" {
description = "The region chosen to be used."
type = string
}
variable "health_check_allow_ports" {
description = "The ports to allow for health check."
type = list(number)
}
Ensuite, ajoutez les éléments suivants au fichier outputs.tf :
output "vpc_network_self_link" {
description = "VPC network"
value = google_compute_network.main.self_link
}Enfin, ajoutez les éléments suivants dans un fichier versions.tf :
terraform {
required_version = ">= 0.13"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.65"
}
}
}
đ Maintenant que nous avons terminĂ© avec ce module, passons au module Load-balancer! đ
Ce module est constitué de deux sous-modules :
- Backend-config đ ïž : ChargĂ© de la configuration des load-balancers des VM.
- Frontend-config đ : Responsable de la configuration des load-balancers pour rediriger le trafic vers nos diffĂ©rentes VM (jette un Ćil au schĂ©ma pour plus de clartĂ©). đ„ïžâš
Nous allons débuter avec le sous-module Frontend-config. Alors crée ce sous-répertoire dans le répertoire Load-balancer et dans son fichier main.tf (que vous allez créer), ajoutez-y le contenu ci-dessous :
resource "google_compute_url_map" "cloud_deployment" {
name = "cloud-deployment-gce-java-${var.name_suffix}"
default_service = var.backend_bucket_id
host_rule {
path_matcher = "app"
hosts = [
"*",
]
}
path_matcher {
name = "app"
default_service = var.backend_bucket_id
path_rule {
paths = [
"/${var.resource_path}/*",
]
service = var.backend_bucket_id
}
path_rule {
paths = [
"*",
]
route_action {
dynamic "weighted_backend_services" {
for_each = var.weighted_backend_services
iterator = entry
content {
backend_service = entry.value["backend_service_id"]
weight = lookup(entry.value, "weight", floor(1000 / length(var.weighted_backend_services)))
}
}
}
}
}
}
resource "google_compute_target_http_proxy" "cloud_deployment" {
name = "cloud-deployment-gce-java-${var.name_suffix}"
url_map = google_compute_url_map.cloud_deployment.self_link
}
resource "google_compute_global_forwarding_rule" "cloud_deployment" {
name = "cloud-deployment-gce-java-${var.name_suffix}"
target = google_compute_target_http_proxy.cloud_deployment.self_link
ip_address = var.global_address
port_range = "80"
load_balancing_scheme = "EXTERNAL_MANAGED"
labels = var.labels
}
Ressource: google_compute_url_map

Je vais dĂ©tailler ce fichier au maximum, car il peut ĂȘtre un peu difficile Ă apprĂ©hender. Du moins, c’est ce que je pense. đđ
Cette ressource dĂ©finit une map d’URL qui gĂšre comment les requĂȘtes HTTP sont dirigĂ©es vers les services backend en fonction des rĂšgles spĂ©cifiĂ©es.
name: Attribue un nom unique Ă la map d’URL, en utilisant un suffixe variable pour l’unicitĂ©.
default_service: Définit le service backend par défaut (identifié par var.backend_bucket_id) qui sera utilisé si aucune autre rÚgle ne correspond.
host_rule:
path_matcher: Définit quel matcher de chemin utiliser, ici « app ».hosts: Indique les hÎtes qui peuvent utiliser cette rÚgle, ici tous les hÎtes ("*").
path_matcher:
name: Identifie ce matcher comme « app ».default_service: Renvoie au service backend par défaut.path_rule(premiÚre rÚgle):paths: Spécifie le chemin qui doit correspondre, avec${var.resource_path}pour une ressource dynamique.service: Indique que le service correspondant est le backend défini parvar.backend_bucket_id.
path_rule(deuxiĂšme rĂšgle):paths: Utilise un wildcard ("*") pour capturer toutes les autres requĂȘtes.route_action: SpĂ©cifie l’action Ă entreprendre pour cette rĂšgle, incluant la configuration de services backend pondĂ©rĂ©s.dynamic "weighted_backend_services":- Permet d’ajouter dynamiquement plusieurs services backend avec des poids.
for_each: ItĂšre sur une variable de services backend pondĂ©rĂ©s,var.weighted_backend_services.entry: ReprĂ©sente l’Ă©lĂ©ment courant dans l’itĂ©ration.content:backend_service: Indique le service backend Ă utiliser.weight: DĂ©finit le poids du service, permettant d’Ă©quilibrer la charge de maniĂšre dynamique.
Ressource: google_compute_target_http_proxy

Cette ressource crĂ©e un proxy HTTP cible qui sert de point d’entrĂ©e pour le trafic HTTP vers les services backend.
name: Comme précédemment, il utilise un nom unique basé sur le suffixe.
url_map: Lie le proxy Ă la carte d’URL dĂ©finie prĂ©cĂ©demment, lui permettant de savoir oĂč diriger le trafic entrant.
Ressource: google_compute_global_forwarding_rule

Cette ressource configure une rĂšgle de redirection globale qui achemine le trafic vers le proxy HTTP cible.
name: Nomme la rĂšgle de redirection de maniĂšre unique.
target: Fait rĂ©fĂ©rence au proxy HTTP cible, indiquant vers oĂč diriger le trafic.
ip_address: SpĂ©cifie l’adresse IP publique Ă utiliser pour la rĂšgle, stockĂ©e dans une variable (var.global_address).
port_range: Indique le port sur lequel la rÚgle écoute, ici le port 80 pour le HTTP.
load_balancing_scheme: Définit que cette rÚgle utilise un équilibrage de charge externe, géré par Google.
labels: Permet d’ajouter des Ă©tiquettes pour une identification et une gestion faciles.
đ J’espĂšre que cette façon de dĂ©tailler est beaucoup plus claire. đâš Maintenant, ajoutez les Ă©lĂ©ments suivants dans le fichier variables.tf :
variable "name_suffix" {
description = "Suffix to append to the name of each resource."
type = string
}
variable "backend_bucket_id" {
description = "backend bucket id"
type = string
}
variable "resource_path" {
description = "Resource folder path"
type = string
}
variable "labels" {
description = "A map of key/value label pairs to assign to the bucket."
type = map(string)
}
variable "weighted_backend_services" {
description = "A list of weighted backend services."
type = list(object(
{
backend_service_id = string
}
))
}
variable "global_address" {
description = "LB global address"
type = string
}
Ensuite, ajoutez les Ă©lĂ©ments suivants dans un fichier outputs.tf : đâš
output "lb_external_ip" {
description = "Frontend IP address of the load balancer"
value = google_compute_global_forwarding_rule.cloud_deployment.ip_address
}
Ajoutez enfin les Ă©lĂ©ments suivants dans un fichier versions.tf : đâš
terraform {
required_version = ">= 0.13"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.65"
}
}
}
đ Maintenant, attaquons-nous au deuxiĂšme sous-module : backend-config ! CrĂ©ez ce sous-rĂ©pertoire đïž et, Ă l’intĂ©rieur, crĂ©ez un fichier main.tf đ en y ajoutant le contenu suivant :
resource "google_compute_health_check" "cloud_deployment" {
name = "cloud-deployment-gce-java-${var.name_suffix}"
timeout_sec = 10
check_interval_sec = 20
unhealthy_threshold = 2
tcp_health_check {
port = var.health_check_port
}
log_config {
enable = true
}
}
resource "google_compute_backend_service" "cloud_deployment" {
name = "cloud-deployment-gce-java-${var.name_suffix}"
load_balancing_scheme = "EXTERNAL_MANAGED"
port_name = var.port_name
dynamic "backend" {
for_each = var.backends
iterator = entry
content {
group = entry.value["group_url"]
}
}
health_checks = [google_compute_health_check.cloud_deployment.self_link]
}
Nous allons briĂšvement expliquer cette ressource : đâš
Ressource google_compute_health_check

google_compute_health_check : Crée un contrÎle de santé pour le déploiement cloud.
- name : Nom du contrÎle de santé, avec un suffixe dynamique basé sur une variable.
- timeout_sec : DĂ©lai d’attente avant qu’un contrĂŽle de santĂ© soit considĂ©rĂ© comme Ă©chouĂ©.
- check_interval_sec : Intervalle de temps entre les contrÎles de santé successifs.
- unhealthy_threshold : Nombre d’Ă©checs consĂ©cutifs nĂ©cessaires avant que l’instance soit considĂ©rĂ©e comme non saine.
- tcp_health_check : Vérifie la connectivité TCP sur un port spécifié.
- port : Le port utilisé pour le contrÎle de santé.
- log_config : Configuration des journaux.
- enable : Active la journalisation pour ce contrÎle de santé.
Ressource google_compute_backend_service

google_compute_backend_service : Définit un service backend pour le load balancer.
- name : Nom du service backend, avec un suffixe dynamique basé sur une variable.
- load_balancing_scheme : Schéma de répartition de charge (EXTERNAL_MANAGED pour un load balancer externe).
- port_name : Nom du port utilisé par le backend.
- dynamic « backend » : ItÚre sur les backends spécifiés dans les variables et configure chaque groupe de backends.
- for_each : Itération sur la liste des backends.
- iterator : Nom de l’itĂ©rateur pour chaque Ă©lĂ©ment.
- content : Contenu pour chaque backend, spécifiant le groupe de backends.
- group : URL du groupe backend.
- health_checks : Associe le contrÎle de santé défini précédemment au service backend.
Maintenant que ce fichier est plus clair, ajoutez les Ă©lĂ©ments suivants dans un fichier variables.tf : đâš
variable "name_suffix" {
description = "Suffix to append to the name of each resource."
type = string
}
variable "backends" {
description = "A map of key/value pairs to assign load balancer group."
type = list(object(
{
group_url = string
}
))
}
variable "port_name" {
description = "port name"
type = string
}
variable "health_check_port" {
description = "health check port"
type = number
}
CrĂ©ez un fichier outputs.tf et ajoutez-y les Ă©lĂ©ments suivants : đâš
output "backend_service_id" {
description = "Backend service ID"
value = google_compute_backend_service.cloud_deployment.id
}
Enfin, dans un fichier versions.tf, ajoutez le contenu suivant : đâš
terraform {
required_version = ">= 0.13"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.65"
}
}
}đ Maintenant que nous avons terminĂ© ce module, passons au module storage. CrĂ©ez un sous-rĂ©pertoire đ storage dans le mĂȘme rĂ©pertoire modules. Allons-y sans plus tarder avec le fichier main.tf que vous allez crĂ©er âïž et ajoutez-y ceci :
resource "google_storage_bucket" "main" {
project = var.project_id
name = var.name
location = var.location
labels = var.labels
force_destroy = true
}
resource "google_storage_default_object_acl" "policy" {
bucket = google_storage_bucket.main.name
role_entity = [
"READER:allUsers",
]
}
google_storage_bucket.main : crĂ©e un bucket Google Storage. Le bucket est configurĂ© avec des variables pour le projet, le nom, la localisation et les labels. L’option force_destroy est dĂ©finie sur true pour garantir que le bucket soit supprimĂ©, mĂȘme s’il contient encore des objets.
google_storage_default_object_acl.policy : attribue une liste de contrĂŽle d’accĂšs (ACL) par dĂ©faut pour les objets du bucket. Dans ce cas, elle donne aux utilisateurs publics le rĂŽle de lecteur (READERS).
Ensuite, crĂ©ez un fichier nommĂ© variables.tf et insĂ©rez-y le contenu suivant. đđ
variable "project_id" {
description = "Google Cloud project ID."
type = string
}
variable "name" {
description = "Bucket name"
type = string
}
variable "location" {
description = "Bucket location"
type = string
}
variable "labels" {
type = map(string)
description = "A map of key/value label pairs to assign to the bucket."
default = {}
}
CrĂ©ez le fichier outputs.tf et insĂ©rez-y le contenu suivant. đđ
output "bucket_name" {
description = "Bucket name"
value = google_storage_bucket.main.name
}CrĂ©ez enfin le fichier versions.tf et insĂ©rez-y le contenu suivant. đđ
terraform {
required_version = ">= 0.13"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.57"
}
}
}
Passons a present au dernier module , creez un repertoire vm-container dans notre repertoire de module . Ce module va crĂ©er đ ïž un modĂšle d’instance Google Cloud đ et un groupe d’instances managĂ©es (MIG) đ Ă l’aide de Terraform. Il configurera aussi les paramĂštres de santĂ© đ©ș et de compte de service đ pour une gestion optimisĂ©e.
CrĂ©ez un fichier main.tf et insĂ©rez-y le contenu suivant. đđ
module "instance_template" {
source = "terraform-google-modules/vm/google//modules/instance_template"
version = "~> 7.9"
name_prefix = "cloud-deployment-gce-java-temp-"
source_image = var.source_image
network = var.vpc_network_self_link
service_account = {
email = var.service_account.email
scopes = var.service_account.scopes
}
tags = var.tags
metadata = var.metadata
startup_script = var.startup_script
labels = var.labels
}
module "mig" {
source = "terraform-google-modules/vm/google//modules/mig"
version = "~> 8.0.1"
project_id = var.project_id
region = var.region
mig_name = var.mig_name
hostname = var.hostname == "" ? var.mig_name : var.hostname
instance_template = module.instance_template.self_link
target_size = 1
named_ports = var.named_ports
health_check = {
type = "tcp"
port = var.health_check_port
proxy_header = "NONE"
request = ""
response = ""
check_interval_sec = 5
timeout_sec = 5
healthy_threshold = 2
unhealthy_threshold = 2
host = ""
initial_delay_sec = 200
request_path = ""
enable_logging = true
}
}
Module Instance Template :

Ce module dĂ©finit un modĂšle d’instance Google Compute Engine avec un prĂ©fixe pour le nom des instances, une image source pour les lancer, un rĂ©seau VPC de dĂ©ploiement, un compte de service (avec email et permissions), des Ă©tiquettes pour la gestion, des mĂ©tadonnĂ©es pour la personnalisation, un script de dĂ©marrage pour automatiser des configurations, et des labels pour une organisation simplifiĂ©e. đđ
Module Managed Instance Group (MIG)

Ce module dĂ©finit un groupe d’instances managĂ©es (MIG) en utilisant le module source spĂ©cifiĂ©. Il configure des paramĂštres tels que l’ID du projet Google Cloud, la rĂ©gion de dĂ©ploiement, et le nom du MIG. Le module utilise un modĂšle d’instance prĂ©alablement dĂ©fini et dĂ©termine la taille cible du groupe d’instances et les ports nommĂ©s. Le contrĂŽle de santĂ© des instances est configurĂ© avec des paramĂštres spĂ©cifiques comme le type de contrĂŽle (TCP), le port Ă vĂ©rifier, les intervalles de vĂ©rification, les dĂ©lais, et les seuils pour considĂ©rer une instance comme saine ou non. La journalisation des vĂ©rifications est Ă©galement activĂ©e. đ
Ensuite, crĂ©ez un fichier nommĂ© variables.tf et insĂ©rez-y le contenu suivant. đđ
variable "source_image" {
description = "The source image used to create the instance."
type = string
}
variable "vpc_network_self_link" {
description = "VPC network self link to deploy the instance in"
type = string
}
variable "service_account" {
description = "Google recommends custom service accounts that have cloud-platform scope and permissions granted via IAM Roles."
type = object({
email = string
scopes = set(string)
})
}
variable "metadata" {
type = map(any)
description = "metadata to attach to the instance"
default = {}
}
variable "startup_script" {
type = string
description = "The startup script to be used on the instance."
default = ""
}
variable "labels" {
type = map(string)
description = "A map of key/value label pairs to assign to the resources."
}
variable "project_id" {
description = "Google Cloud project ID."
type = string
}
variable "region" {
description = "The region chosen to be used."
type = string
}
variable "mig_name" {
description = "managed instance group name"
type = string
}
variable "hostname" {
description = "hostname for the instance"
type = string
default = ""
}
variable "named_ports" {
description = "named ports"
type = list(object(
{
name = string
port = number
}
))
}
variable "health_check_port" {
description = "health check port"
type = number
}
variable "tags" {
description = "network tags for the instance"
type = list(string)
}
CrĂ©ez le fichier outputs.tf et insĂ©rez-y le contenu suivant. đđ
output "instance_group_url" {
description = "The URL of the instance group."
value = module.mig.instance_group
}
output "mig_self_link" {
description = "The self link of the instance group manager."
value = module.mig.self_link
}
CrĂ©ez enfin le fichier versions.tf et insĂ©rez-y le contenu suivant. đđ
terraform {
required_version = ">= 0.13"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.65"
}
}
}đ Bravo Ă nous! Nous avons terminĂ© avec les modules. Ă prĂ©sent, notre arborescence devrait ressembler Ă ceci. đłâš

A present , à la racine du répertoire terraform-deployment, créez un sous-répertoire template. Dans ce nouveau dossier, créez un fichier nommé startup-script.tftpl, et ajoutez-y le contenu suivant :
#! /bin/bash
export LDS_REST_PORT=${LDS_REST_PORT}
export LDS_PROJECT=$(gcloud config get-value project)
export LDS_BUCKET=${LDS_BUCKET}
export LDS_RESOURCE_PATH=/resource
export LDS_REST_URL=http://host.docker.internal:$${LDS_REST_PORT}
export LDS_MYSQL_INSTANCE=${LDS_MYSQL_INSTANCE}
export LDS_MYSQL_DATABASE=${LDS_MYSQL_DATABASE}
export LDS_MYSQL_USERNAME=${LDS_MYSQL_USERNAME}
export FRONTEND_VERSION_TAG=${FRONTEND_VERSION_TAG}
export DATA_INIT_BUCKET_NAME=${DATA_INIT_BUCKET_NAME}
export DATA_INIT_FILE_NAME=${DATA_INIT_FILE_NAME}
MYSQL_PASSWORD="$(gcloud secrets versions access --secret ${MYSQL_DB_PASSWORD_SECRET} latest --project $${LDS_PROJECT})"
export LDS_MYSQL_PASSWORD=$${MYSQL_PASSWORD}
docker-compose up -d api web
C’est un script qui s’exĂ©cute lors du dĂ©marrage de la VM. đ Si le contenu te semble flou, n’hĂ©site pas Ă jeter un Ćil au fichier docker-compose.yml que nous avons intĂ©grĂ© dans l’image de la VM lors de la construction avec Packer. Cela devrait t’Ă©clairer ! đ
Maintenant, passons concrĂštement au dĂ©ploiement Blue/Green ! Avant de plonger dans le vif du sujet, faisons une petite mise au point : d’aprĂšs le schĂ©ma de l’architecture, on pourrait penser qu’il y a un point d’entrĂ©e unique pour le trafic utilisateur, qui redirige ensuite vers les instances compute-engine. Mais en rĂ©alitĂ©, chaque backend (Blue et Green) a son propre point d’entrĂ©e ! đ C’Ă©tait essentiel de le prĂ©ciser pour la clartĂ©. Maintenant, dans le rĂ©pertoire terraform-deployment, crĂ©ez un fichier blue.tf et ajoutez-y le contenu suivant : đ
module "vm_container_blue" {
depends_on = [
module.project_services,
]
source = "./modules/vm-container"
source_image = var.source_image
vpc_network_self_link = module.networking.vpc_network_self_link
service_account = {
email = google_service_account.cloud_deployment.email
scopes = [
"https://www.googleapis.com/auth/cloud-platform",
]
}
project_id = data.google_project.project.project_id
region = var.region
mig_name = "cloud-deployment-gce-java-mig-blue"
metadata = {
google-logging-enabled = "true"
google-monitoring-enabled = "true"
}
startup_script = templatefile(
"${path.module}/templates/startup-script.tftpl",
{
LDS_REST_PORT = lookup(local.named_ports_map, "server")["port"],
LDS_PROJECT = data.google_project.project.project_id,
LDS_BUCKET = module.storage.bucket_name,
LDS_RESOURCE_PATH = "/${local.resource_path}",
LDS_MYSQL_INSTANCE = module.database.connection_name,
LDS_MYSQL_DATABASE = module.database.database_name,
LDS_MYSQL_USERNAME = module.database.user_name,
MYSQL_DB_PASSWORD_SECRET = module.database.password_secret,
FRONTEND_VERSION_TAG = "blue",
DATA_INIT_BUCKET_NAME = var.data_init_bucket_name,
DATA_INIT_FILE_NAME = var.data_init_archive_file_name,
}
)
tags = [
"service",
]
named_ports = local.named_ports
health_check_port = lookup(local.named_ports_map, "server")["port"]
labels = var.labels
}
module "load_balancer_backend_config_blue" {
depends_on = [
module.project_services,
]
source = "./modules/load-balancer/backend-config"
name_suffix = "blue-v1"
backends = [
{
group_url = module.vm_container_blue.instance_group_url,
},
]
port_name = lookup(local.named_ports_map, "client")["name"]
health_check_port = lookup(local.named_ports_map, "client")["port"]
}
module "load_balancer_frontend_config_blue" {
depends_on = [
module.project_services,
]
source = "./modules/load-balancer/frontend-config"
name_suffix = "blue-v1"
backend_bucket_id = google_compute_backend_bucket.cdn.id
resource_path = local.resource_path
global_address = module.global_addresses.addresses[0]
weighted_backend_services = [
{
backend_service_id = module.load_balancer_backend_config_blue.backend_service_id
}
]
labels = var.labels
}
Module vm_container_blue :
Ce module configure l’instance VM de l’environnement âblueâ. Il utilise le module vm-container, spĂ©cifie une image source, des informations de rĂ©seau et le compte de service pour autoriser l’accĂšs aux ressources GCP. Le script de dĂ©marrage, dĂ©fini dans le fichier startup-script.tftpl, initialise les paramĂštres spĂ©cifiques, tels que les dĂ©tails MySQL et les identifiants de stockage. Ce module crĂ©e Ă©galement des balises et des mĂ©tadonnĂ©es qui activent la journalisation et la surveillance pour la VM.
Module load_balancer_backend_config_blue :
Ce module configure le backend du load balancer pour lâenvironnement âblueâ. Il Ă©tablit le service backend et associe un groupe dâinstances, en utilisant le port spĂ©cifiĂ© pour la communication entre le client et le backend. Un contrĂŽle de santĂ© est appliquĂ© pour vĂ©rifier la disponibilitĂ© des instances dans le backend, ce qui permet une redirection dynamique du trafic en cas de panne ou d’indisponibilitĂ©.
Module load_balancer_frontend_config_blue :
Ce module gĂšre la configuration frontend du load balancer pour lâenvironnement âblueâ. Il associe le bucket backend CDN, lâadresse IP globale et le service backend, assurant ainsi que les requĂȘtes clients sont redirigĂ©es correctement vers le bon environnement. GrĂące Ă la pondĂ©ration, il est possible de basculer facilement le trafic entre diffĂ©rents services backend, ce qui est essentiel pour les dĂ©ploiements Blue/Green, permettant des mises Ă jour sans interruption du service.
Ensuite, créez un fichier nommé green.tf et insérez-y le contenu suivant :
module "vm_container_green" {
depends_on = [
module.project_services,
]
source = "./modules/vm-container"
source_image = var.source_image
vpc_network_self_link = module.networking.vpc_network_self_link
service_account = {
email = google_service_account.cloud_deployment.email
scopes = [
"https://www.googleapis.com/auth/cloud-platform",
]
}
project_id = data.google_project.project.project_id
region = var.region
mig_name = "cloud-deployment-gce-java-mig-green"
metadata = {
google-logging-enabled = "true"
google-monitoring-enabled = "true"
}
startup_script = templatefile(
"${path.module}/templates/startup-script.tftpl",
{
LDS_REST_PORT = lookup(local.named_ports_map, "server")["port"]
LDS_PROJECT = data.google_project.project.project_id,
LDS_BUCKET = module.storage.bucket_name,
LDS_RESOURCE_PATH = "/${local.resource_path}",
LDS_MYSQL_INSTANCE = module.database.connection_name,
LDS_MYSQL_DATABASE = module.database.database_name,
LDS_MYSQL_USERNAME = module.database.user_name,
MYSQL_DB_PASSWORD_SECRET = module.database.password_secret,
FRONTEND_VERSION_TAG = "green",
DATA_INIT_BUCKET_NAME = var.data_init_bucket_name,
DATA_INIT_FILE_NAME = var.data_init_archive_file_name,
}
)
tags = [
"service",
]
named_ports = local.named_ports
health_check_port = lookup(local.named_ports_map, "server")["port"]
labels = var.labels
}
module "load_balancer_backend_config_green" {
depends_on = [
module.project_services,
]
source = "./modules/load-balancer/backend-config"
name_suffix = "green-v2"
backends = [
{
group_url = module.vm_container_green.instance_group_url,
},
]
port_name = lookup(local.named_ports_map, "client")["name"]
health_check_port = lookup(local.named_ports_map, "client")["port"]
}
module "load_balancer_frontend_config_green" {
depends_on = [
module.project_services,
]
source = "./modules/load-balancer/frontend-config"
name_suffix = "green-v2"
backend_bucket_id = google_compute_backend_bucket.cdn.id
resource_path = local.resource_path
global_address = module.global_addresses.addresses[1]
weighted_backend_services = [
{
backend_service_id = module.load_balancer_backend_config_green.backend_service_id
}
]
labels = var.labels
}
Ce module fonctionne de la mĂȘme maniĂšre que le prĂ©cĂ©dent, mais il est spĂ©cifiquement destinĂ© Ă l’environnement green.
Créez ensuite un fichier variables.tf pour définir les variables globales du projet, puis ajoutez-y le contenu suivant :
variable "project_id" {
description = "Google Cloud project ID."
type = string
validation {
condition = var.project_id != ""
error_message = "Error: project_id is required"
}
}
variable "region" {
description = "Google Cloud region where the cloud resource will be created."
type = string
default = "us-central1"
}
variable "zone" {
description = "Google cloud zone where the resources will be created."
type = string
default = "us-central1-a"
}
variable "bucket_location" {
description = "Bucket location where the uploaded files will be stored."
type = string
default = "US"
validation {
condition = contains(["ASIA", "EU", "US"], var.bucket_location)
error_message = "Allowed values for type are \"ASIA\", \"EU\", \"US\"."
}
}
variable "source_image" {
description = "The source image used to create the instance."
type = string
default = "projects/your-project-id/global/images/your-image-name"
}
variable "labels" {
type = map(string)
description = "A map of key/value label pairs to assign to the cloud resources."
default = {
app = "cloud-deployment-gce-java"
}
}
variable "data_init_bucket_name" {
description = "Name of the Cloud Storage bucket that store the archive file for initialization."
type = string
default = "jss-resources"
}
variable "data_init_archive_file_name" {
description = "Name of the archive file that contains the initialization data."
type = string
default = "initialization.tar.gz"
}
n’obliez pas de remplacer la variable source_image par la source de votre image packer .
Créez un fichier provider.tf et ajoutez-y le contenu suivant :
terraform {
required_version = ">= 0.13"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.57"
}
}
}
provider "google" {
project = var.project_id
region = var.region
}
Créez maintenant notre fichier principal main.tf et ajoutez-y le contenu suivant :
module "project_services" {
source = "terraform-google-modules/project-factory/google//modules/project_services"
version = "~> 15.0"
project_id = var.project_id
disable_services_on_destroy = false
activate_apis = [
"compute.googleapis.com",
"iam.googleapis.com",
"cloudresourcemanager.googleapis.com",
"sqladmin.googleapis.com",
"secretmanager.googleapis.com",
"servicenetworking.googleapis.com",
]
}
data "google_project" "project" {
depends_on = [
module.project_services
]
project_id = var.project_id
}
locals {
resource_path = "resource"
named_ports_map = {
"client" = {
name = "lds-client-port"
port = 80
},
"server" = {
name = "lds-server-port"
port = 8000
},
}
named_ports = [
for key, value in local.named_ports_map : {
name = value.name
port = value.port
}
]
}
resource "google_service_account" "cloud_deployment" {
depends_on = [
module.project_services,
]
account_id = "cloud-deployment-gce-java"
}
resource "google_project_iam_member" "cloud_deployment" {
for_each = toset([
"roles/storage.objectAdmin",
"roles/cloudsql.editor",
"roles/logging.logWriter",
"roles/resourcemanager.projectIamAdmin",
"roles/secretmanager.secretAccessor",
"roles/secretmanager.admin",
"roles/servicenetworking.networkAdmin",
])
project = data.google_project.project.project_id
role = each.key
member = "serviceAccount:${google_service_account.cloud_deployment.email}"
}
module "storage" {
source = "./modules/storage"
project_id = data.google_project.project.project_id
location = var.bucket_location
labels = var.labels
name = "cloud-deployment-gce-resource-${data.google_project.project.number}"
}
module "networking" {
source = "./modules/networking"
project_id = data.google_project.project.project_id
region = var.region
health_check_allow_ports = [
for key, value in local.named_ports_map : value.port
]
}
module "database" {
depends_on = [
module.project_services,
]
source = "./modules/database"
project_id = data.google_project.project.project_id
region = var.region
zone = var.zone
vpc_network_self_link = module.networking.vpc_network_self_link
availability_type = "ZONAL"
service_account = google_service_account.cloud_deployment.email
}
resource "google_compute_backend_bucket" "cdn" {
project = data.google_project.project.project_id
name = "cloud-deployment-gce-java-cdn"
bucket_name = module.storage.bucket_name
enable_cdn = true
cdn_policy {
cache_mode = "CACHE_ALL_STATIC"
client_ttl = 3600
default_ttl = 3600
max_ttl = 86400
negative_caching = true
serve_while_stale = 86400
}
custom_response_headers = [
"X-Cache-ID: {cdn_cache_id}",
"X-Cache-Hit: {cdn_cache_status}",
"X-Client-Location: {client_region_subdivision}, {client_city}",
"X-Client-IP-Address: {client_ip_address}"
]
}
resource "google_compute_firewall" "cloud_deployment_client" {
name = "cloud-deployment-gce-java-client"
network = module.networking.vpc_network_self_link
allow {
protocol = "tcp"
ports = [
lookup(local.named_ports_map, "client")["port"],
]
}
target_tags = ["service"]
source_ranges = module.global_addresses.addresses
}
resource "google_compute_firewall" "cloud_deployment_ssh" {
name = "cloud-deployment-gce-java-ssh"
network = module.networking.vpc_network_self_link
allow {
protocol = "tcp"
ports = [
22,
]
}
# IAP TCP forwarding IP range.
source_ranges = [
"35.235.240.0/20",
]
}
module "global_addresses" {
depends_on = [
module.project_services
]
source = "terraform-google-modules/address/google"
version = "3.2.0"
project_id = data.google_project.project.project_id
region = var.region
address_type = "EXTERNAL"
global = true
names = [
"cloud-deployment-gce-java-blue",
"cloud-deployment-gce-java-green",
]
}
Module project_services : Ce module active divers services Google Cloud nĂ©cessaires au projet, tels que Compute Engine, IAM, Cloud SQL, et Secret Manager. Il dĂ©pend d’un ID de projet passĂ© en variable et gĂšre l’activation des API spĂ©cifiĂ©es, assurant que les services requis sont disponibles pour l’ensemble des opĂ©rations de l’infrastructure.
Data google_project : Cette ressource rĂ©cupĂšre des informations sur le projet spĂ©cifiĂ©, garantissant que toutes les autres ressources et modules peuvent interagir avec le bon projet. Elle dĂ©pend du module project_services pour s’assurer que les services sont dĂ©jĂ activĂ©s.
Variables locales : Ces variables dĂ©finissent des chemins de ressources et des ports nommĂ©s utilisĂ©s dans d’autres ressources. Par exemple, le port 80 est dĂ©fini pour le client et le port 8000 pour le serveur, facilitant la gestion des configurations des ports dans l’ensemble du projet.
Ressource google_service_account : Ce bloc crĂ©e un compte de service pour le dĂ©ploiement, ce qui permet d’exĂ©cuter des opĂ©rations sur les ressources Google Cloud sous un compte sĂ©curisĂ©, assurant ainsi une gestion appropriĂ©e des autorisations.
Ressource google_project_iam_member : Cette ressource assigne plusieurs rĂŽles IAM au compte de service créé, comme l’accĂšs Ă Cloud Storage et Ă Cloud SQL. Cela garantit que le compte de service a les permissions nĂ©cessaires pour interagir avec les ressources dans le projet.
Module storage : Ce module configure un bucket Cloud Storage pour stocker des ressources, utilisant des variables pour dĂ©finir l’emplacement et le nom. Il crĂ©e un espace de stockage pour les donnĂ©es nĂ©cessaires au projet, en intĂ©grant des Ă©tiquettes pour la gestion.
Ressource google_compute_backend_bucket : Cette ressource configure un bucket de backend pour un CDN (Content Delivery Network), optimisant la distribution de contenu statique. Elle dĂ©finit des politiques de mise en cache et des en-tĂȘtes de rĂ©ponse personnalisĂ©s pour gĂ©rer les rĂ©ponses aux requĂȘtes clients.
Ressources google_compute_firewall : Ces ressources dĂ©finissent des rĂšgles de pare-feu pour contrĂŽler le trafic entrant vers les instances. La premiĂšre rĂšgle autorise l’accĂšs au port client, tandis que la seconde permet l’accĂšs SSH, sĂ©curisant ainsi les communications avec les instances de calcul.
Module global_addresses : Ce module crĂ©e des adresses globales externes pour le projet, utilisĂ©es pour les services accessibles Ă partir d’Internet. Cela permet d’exposer les applications dĂ©ployĂ©es Ă l’extĂ©rieur du rĂ©seau, facilitant les communications avec les utilisateurs finaux.
âš Passons maintenant Ă la crĂ©ation dâun fichier outputs.tf ! Ajoutez-y le contenu suivant pour obtenir des valeurs importantes de notre infrastructure đ :
output "backend_bucket_name" {
description = "The name of the backend bucket used for Cloud CDN"
value = google_compute_backend_bucket.cdn.name
}
output "bucket_name" {
description = "Bucket name"
value = module.storage.bucket_name
}
output "blue_mig_self_link" {
description = "Self link of the blue version instance group"
value = module.vm_container_blue.mig_self_link
}
output "green_mig_self_link" {
description = "Self link of the green version instance group"
value = module.vm_container_green.mig_self_link
}
output "blue_mig_load_balancer_ip" {
description = "IP address of the blue version instance group"
value = "http://${module.load_balancer_frontend_config_blue.lb_external_ip}"
}
output "green_mig_load_balancer_ip" {
description = "IP address of the green version instance group"
value = "http://${module.load_balancer_frontend_config_green.lb_external_ip}"
}
output "user_mig_load_balancer_ip" {
description = "IP address of the user instance group"
value = "http://${module.load_balancer_frontend_config_user.lb_external_ip}"
}
đ Maintenant, crĂ©ez un bucket dans GCP ! Ensuite, crĂ©ez un fichier backend.tf et remplacez le nom du bucket par le vĂŽtre .
terraform {
backend "gcs" {
bucket = "teckworld-bucket"
prefix = "terraform/state"
}
}đ§ Optionnel : Vous pouvez ajouter un fichier terraform.tfvars pour dĂ©finir les valeurs des variables de configuration. Cela reste facultatif, car chaque variable dans variables.tf a dĂ©jĂ une valeur par dĂ©faut.
âš Bravo ! AprĂšs ce long chemin, nous arrivons enfin Ă la partie cruciale : la crĂ©ation de l’infrastructure ! Sur votre terminal, accĂ©dez au rĂ©pertoire terraform-deployment, puis exĂ©cutez les commandes suivantes pour initialiser votre environnement :
terraform initđ Ensuite, vous pouvez exĂ©cuter un terraform plan pour visualiser les modifications prĂ©vues, ou bien passer directement Ă l’Ă©tape suivante avec la commande :
terraform apply --auto-aproveâ Ă ce stade, vous pouvez vous accorder une pause cafĂ© ! Votre infrastructure mettra un certain temps Ă ĂȘtre créée. Une fois que tout sera prĂȘt, vous verrez un message de sortie similaire :

đ Et voilĂ ! Maintenant que tout est en place, rendez-vous sur la console Google Cloud pour explorer et admirer votre nouvelle infrastructure. Profitez de votre travail ! đ. voici quelques capture d’ecran du mien :
Groupes d’instances :

Instances de VM:

Equilibreurs de charge :

Cloud CDN :

Blue deployment :

Green deployment :

đ Explorez votre infrastructure : Prenez le temps de parcourir chaque ressource et profitez de cette infrastructure que vous avez mise en place avec succĂšs. Une fois votre exploration terminĂ©e, vous pourrez la nettoyer facilement en exĂ©cutant la commande terraform destroy. Cela supprimera l’ensemble des ressources, libĂ©rant ainsi vos ressources cloud. vous aurez la sortie suivante :

Passons maintenant Ă la derniĂšre Ă©tape. đâš
Etape 4 : Automatisation avec Github action
đ FĂ©licitations ! Si vous ĂȘtes arrivĂ© jusquâici, bravo pour votre persĂ©vĂ©rance et vos efforts ! Il ne reste plus qu’un dernier coup de pouce pour finaliser votre projet. Donnez votre meilleur, on est presque au bout du chemin ! đȘđ
Création du compte de service pour GitHub :
Pour créer un compte de service pour GitHub afin de faciliter les actions CI/CD, suivez ces étapes :
âš Je pars du principe que vous ĂȘtes dĂ©jĂ bien Ă l’aise avec Google Cloud, donc il y aura trĂšs peu de captures dâĂ©cran pour cette Ă©tape. D’autant plus que ces Ă©tapes ont Ă©tĂ© abordĂ©es plus haut
Accédez à Google Cloud Console et allez dans IAM & Admin > Comptes de service.
Créez un compte de service avec un nom descriptif, par exemple « github-ci-cd ».
Attribuez les rĂŽles d’Ăditeur et d’Administrateur Compute. Bien que ces rĂŽles soient assez Ă©levĂ©s, ils nous permettront d’Ă©viter des complications Ă l’avenir.
Générez une clé JSON pour ce compte de service, que vous pourrez utiliser comme secret dans GitHub. suivez ce mini guide
Ajoutez la clĂ© JSON dans GitHub sous Settings > Secrets pour sĂ©curiser vos workflows : Ouvrez le fichier rĂ©cemment tĂ©lĂ©chargĂ© dans un Ă©diteur et copiez soigneusement son contenu. Ensuite, suivez les Ă©tapes dĂ©crites dans cette petite vidĂ©o đč.
Comme vous l’avez vu dans la vidĂ©o, ce secret doit ĂȘtre nommĂ© GCP_CREDENTIALS. CrĂ©ez ensuite deux autres secrets : GCP_PROJECT_ID, qui contiendra l’ID de votre projet GCP, et GCP_SA_KEY, qui stockera Ă©galement la clĂ© JSON. Ne me demandez pas pourquoi cette redondance existe ! đđ
Création des workflows backend et frontend :
Dans la racine de votre projet, crĂ©ez un dossier .github, puis un sous-dossier workflows. Ă l’intĂ©rieur de ce dernier, commencez par crĂ©er un fichier backend.yml et ajoutez-y le contenu suivant : đâš
name: Backend Tests , Cloud Build and terraform deploy
on:
push:
branches:
- main
paths:
- 'api/pom.xml'
- 'api/src/**'
- '.github/workflows/backend.yml'
pull_request:
branches:
- main
paths:
- 'api/pom.xml'
- 'api/src/**'
- '.github/workflows/backend.yml'
env:
GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
GCP_CREDENTIALS: ${{ secrets.GCP_CREDENTIALS }}
GCP_SA_KEY: ${{secrets.GCP_SA_KEY }}
jobs:
test:
runs-on: ubuntu-latest
environment: Backend
strategy:
matrix:
java-version: ["17"]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java-version }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java-version }}
distribution: 'temurin'
cache: 'maven'
cache-dependency-path: 'api/pom.xml'
- name: Install dependencies
run: mvn -B install -DskipTests
working-directory: api
- name: Run tests
run: mvn -B test
working-directory: api
- name: 'Authenticating to Google Cloud'
uses: 'google-github-actions/auth@v1'
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
- name: 'Build docker image with Cloud Build'
run: |
cd api
gcloud builds submit --config cloudbuild.yaml
packer-build:
needs: test
runs-on: ubuntu-latest
environment: Backend
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: 'Authenticating to Google Cloud'
uses: 'google-github-actions/auth@v1'
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
# Set up Google Cloud SDK
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v1
with:
project_id: ${{ secrets.GCP_PROJECT_ID }}
service_account_key: ${{ secrets.GCP_SA_KEY }}
export_default_credentials: true
# Authenticate Docker with Google Cloud Registry
- name: Authenticate Docker to Google Container Registry
run: gcloud auth configure-docker
# Trigger Cloud Build to run the Packer script
- name: Run Cloud Build for Packer
run: |
cd cloudbuild-packer-image
gcloud builds submit --config cloudbuild_packer_vm_img.yaml
terraform-deploy:
needs: packer-build
runs-on: ubuntu-latest
environment: Backend
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: 'Authenticating to Google Cloud'
uses: 'google-github-actions/auth@v1'
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
# Set up Google Cloud SDK
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v1
with:
project_id: ${{ secrets.GCP_PROJECT_ID }}
service_account_key: ${{ secrets.GCP_SA_KEY }}
export_default_credentials: true
- name: Authenticate Docker to Google Container Registry
run: gcloud auth configure-docker
# run terraform to deploy infrastructure
- name: deploy with terraform
run: |
cd terraform-deployment
terraform init
terraform apply -auto-approve -var "project_id=${{ secrets.GCP_PROJECT_ID }}"
Nom et Déclencheurs
- name : Spécifie le nom du workflow.
- on : DĂ©finit quand le workflow s’exĂ©cute :
- push : Déclenche lors des push sur la branche principale, spécifiquement pour les modifications dans
api/pom.xml,api/src/**, et.github/workflows/backend.yml. - pull_request : DĂ©clenche pour les demandes de tirage visant la branche principale avec les mĂȘmes chemins.
Variables d’Environnement
- env : Configure les variables d’environnement Ă partir des secrets GitHub.
Jobs
test :
- runs-on : SpĂ©cifie que la tĂąche s’exĂ©cute dans l’environnement Ubuntu le plus rĂ©cent.
- environment : Nomme l’environnement comme « Backend ».
- strategy : Utilise une matrice pour définir la version de Java (17).
- steps :
- Checkout repository : Clone le dépÎt.
- Set up Java : Configure la version de Java spécifiée en utilisant la distribution Temurin et met en cache les dépendances Maven.
- Install dependencies : Exécute Maven pour installer les dépendances sans exécuter les tests.
- Run tests : Exécute les tests avec Maven.
- Authenticate to Google Cloud : Authentifie avec Google Cloud en utilisant les informations d’identification stockĂ©es dans les secrets.
- Build Docker image with Cloud Build : Soumet une demande de construction à Google Cloud Build en utilisant un fichier de configuration spécifié.
packer-build:
- needs : DĂ©pend de l’achĂšvement de la tĂąche de test.
- steps :
- Checkout repository : Clone à nouveau le dépÎt.
- Authenticate to Google Cloud : Ătape d’authentification similaire.
- Set up Cloud SDK : Configure le SDK Google Cloud avec l’ID du projet et la clĂ© du compte de service.
- Authenticate Docker : Configure Docker pour utiliser le Google Container Registry.
- Run Cloud Build for Packer : Soumet une demande de construction pour créer une image VM en utilisant une configuration Packer.
terraform-deploy :
- needs : DĂ©pend de l’achĂšvement de la tĂąche de construction Packer.
- steps :
- Checkout repository : Clone le dépÎt une troisiÚme fois.
- Authenticate to Google Cloud : RĂ©pĂšte le processus d’authentification.
- Set up Cloud SDK : Configure le SDK avec les mĂȘmes paramĂštres.
- Authenticate Docker : Configure Docker pour le Google Container Registry.
- Deploy with Terraform : Initialise Terraform et exĂ©cute des commandes pour construire toute infrastructure existante en utilisant l’ID de projet spĂ©cifiĂ©.
CrĂ©ez ensuite un fichier frontend.yml dans le mĂȘme rĂ©pertoire workflows et ajoutez-y le contenu suivant : âšđ
name: Frontend tests
on:
push:
branches:
- main
paths:
- 'frontend/**'
- '.github/workflows/frontend.yml'
pull_request:
branches:
- main
paths:
- 'frontend/**'
- '.github/workflows/frontend.yml'
env:
GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
GCP_CREDENTIALS: ${{ secrets.GCP_CREDENTIALS }}
GCP_SA_KEY: ${{secrets.GCP_SA_KEY }}
jobs:
frontend-unit-tests:
runs-on: ubuntu-latest
environment: Frontend
strategy:
matrix:
node-version: [16.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Cache dependencies
uses: actions/cache@v4
with:
path: src/frontend/node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('frontend/package-lock.json') }}
- name: Install dependencies
run: npm ci
working-directory: frontend
- name: Execute unit tests
run: npm test
working-directory: frontend
CloudBuild-docker-img:
needs: frontend-unit-tests # Ensure tests pass before building
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: 'Authenticating to Google Cloud'
uses: 'google-github-actions/auth@v1'
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
# Set up Google Cloud SDK
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v1
with:
project_id: ${{ secrets.GCP_PROJECT_ID }}
service_account_key: ${{ secrets.GCP_SA_KEY }}
export_default_credentials: true
# Trigger Cloud Build to run the Packer script
- name: Build docker image
run: gcloud builds submit --config cloudbuild.yaml
working-directory: frontend
# Job for Google Cloud Build and Packer
CloudBuild-packer-img:
needs: CloudBuild-docker-img # Ensure tests pass before building
runs-on: ubuntu-latest
environment: Frontend
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: 'Authenticating to Google Cloud'
uses: 'google-github-actions/auth@v1'
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
# Set up Google Cloud SDK
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v1
with:
project_id: ${{ secrets.GCP_PROJECT_ID }}
service_account_key: ${{ secrets.GCP_SA_KEY }}
export_default_credentials: true
# Authenticate Docker with Google Cloud Registry
- name: Authenticate Docker to Google Container Registry
run: gcloud auth configure-docker
# Trigger Cloud Build to run the Packer script
- name: Run Cloud Build for Packer
run: |
gcloud builds submit --config packer-image/cloudbuild_packer_vm_img.yaml
terraform-deploy:
needs: CloudBuild-docker-img # Ensure tests pass before building
runs-on: ubuntu-latest
environment: Frontend
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: 'Authenticating to Google Cloud'
uses: 'google-github-actions/auth@v1'
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
# Set up Google Cloud SDK
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v1
with:
project_id: ${{ secrets.GCP_PROJECT_ID }}
service_account_key: ${{ secrets.GCP_SA_KEY }}
export_default_credentials: true
# Authenticate Docker with Google Cloud Registry
- name: Authenticate Docker to Google Container Registry
run: gcloud auth configure-docker
# Trigger Cloud Build to run the Packer script
- name: deploy with terraform
run: |
cd terraform-deployments
terraform init && terraform destroy -auto-approve -var "project_id=${{ secrets.GCP_PROJECT_ID }}"
Si vous avez bien compris l’explication du workflow du backend, celui-ci n’a plus de secrets pour vous. Je n’entrerai donc pas dans les dĂ©tails Ă nouveau.
Dans votre dépÎt github , créez deux environnements : backend et frontend. Voici comment procéder :
Cliquez sur l’onglet « Settings » en haut de la page.
Sélectionnez « Environments » : Dans la barre latérale gauche, trouvez et cliquez sur « Environments ».
Ajoutez un nouvel environnement : Cliquez sur le bouton « New environment ».
Nommez votre environnement : Donnez un nom Ă votre nouvel environnement (par exemple, backend ou frontend).
Je crois que j’ai omis une Ă©tape cruciale : vous devez crĂ©er un dĂ©pĂŽt Git pour votre projet sur GitHub ! đ€Šââïž Toute cette partie suppose que vous l’avez dĂ©jĂ fait. Alors, j’espĂšre que vous l’avez fait ! đ
CrĂ©ez ensuite un fichier .gitignore Ă la racine de votre projet et ajoutez-y les Ă©lĂ©ments suivants : .terraform, .terraform.lock.hcl, et *.tfvars (si vous avez créé un fichier .tfvars). Assurez-vous que ces fichiers soient exclus du suivi par Git pour garder votre dĂ©pĂŽt propre ! đ«đ
Maintenant, poussez tout votre code vers votre dĂ©pĂŽt distant et regardez vos pipelines s’exĂ©cuter ! Profitez de ce moment, c’est le rĂ©sultat de tout votre travail ! đđ
si tout ce passe bien vous aurez une sortie similaire :
Summary :

test job :

Packer-build job :

terraform-deploy job :

Vous pouvez ensuite allez verifier la creation des ressources dans google cloud.
Conclusion
Et voilĂ , nous avons atteint la fin de ce projet ! đ Si vous ĂȘtes arrivĂ© jusqu’ici, fĂ©licitations, vous faites partie des rares qui terminent ce qu’ils commencent. J’espĂšre que ce projet vous a permis d’apprendre de nouvelles compĂ©tences. N’hĂ©sitez pas Ă laisser un commentaire pour partager vos rĂ©flexions, et restez Ă l’affĂ»t pour notre prochain projet ensemble ! đ
