Dans ce guide, vous apprendrez à créer une solution Terraform entièrement modulaire et réutilisable, déployant des ressources sur Azure, Kubernetes et Cloudflare.
Dans mon article précédent, vous avez appris à configurer un cluster Kubernetes et à exécuter Plausible Analytics en utilisant une série de commandes CLI. Bien que cette approche fonctionne, elle n'est pas idéale. Une meilleure solution plus durable est d'utiliser Terraform. Avec Terraform, vous décrivez votre infrastructure dans son état désiré, et Terraform détermine les étapes nécessaires pour y parvenir.
J'ai également utilisé helmfile auparavant, qui est excellent pour gérer les versions Helm. Mais ce que j'aime dans Terraform, c'est qu'il va plus loin. Il ne s'arrête pas à Kubernetes, il vous donne une solution unique pour définir toutes les ressources de votre pile.
Avantages clés
- Définir l'état désiré - Plus de scripts CLI manuels ou de dérive de configuration (votre environnement représente toujours votre code)
- Récupérable - Comme vous avez défini votre état désiré, il est facile de récupérer un environnement entier avec tous ses paramètres
- Conception modulaire - Créer des modules réutilisables dans différents environnements
- Agnostique de l'environnement - Déployer sur Azure, Kubernetes, Cloudflare et bien d'autres plateformes
- Contrôle de version - Suivre les changements d'infrastructure dans le contrôle de version, en maintenant une piste d'audit complète
Configurer votre solution comme décrit dans cet article étend ces avantages avec:
- HTTPS automatique - Certificats Let's Encrypt pour toutes vos applications exposées
- Optimisé en coûts - Une seule IP publique, une configuration VM rentable et pas de disques VM supplémentaires
- Pas de dérive de configuration - État d'infrastructure stocké de manière centralisée
- Sécurité des données - Les disques Azure stockent vos données, permettant une sauvegarde et une restauration faciles via des instantanés ou un coffre de sauvegarde
Prérequis
Assurez-vous que les outils suivants sont installés sur votre machine: Azure CLI, Terraform & Outils Kubernetes.
Ou alternativement, utilisez Cloud Shell dans Azure Portal, où tous les outils nécessaires sont pré-installés. Lors de l'utilisation de Cloud Shell, montez le clouddrive pour persister tout entre les sessions shell: clouddrive mount & cd clouddrive.
Si vous êtes sous Windows, veuillez utiliser Cloud Shell ou WSL, car tous les scripts sont écrits en bash.
Commencer
Il y a une petite étape manuelle à faire avant de plonger dans Terraform. Notre solution a besoin d'un backend (stockage) pour le fichier d'état, nous devons donc d'abord créer un compte de stockage avec un conteneur dans Azure. Il y a un script pratique dans notre projet Terraform qui fait cela pour vous. Obtenons d'abord notre projet Terraform.
Vous pouvez le faire rapidement en exécutant le script shell suivant:
#!/usr/bin/env bash
# Télécharger et extraire le projet terraform du dépôt
curl -L "https://github.com/jeroenbach/bach.software/archive/refs/heads/main.zip" -o "bach.software-terraform.zip"
unzip -q "bach.software-terraform.zip" "bach.software-main/src/app/examples/post4/terraform/*"
# Déplacer le dossier extrait vers le répertoire actuel et supprimer le zip et le dossier extrait
mv "bach.software-main/src/app/examples/post4/terraform" "./terraform"
rm -rf "bach.software-terraform.zip" "bach.software-main"
# Naviguer vers le répertoire terraform
cd "./terraform"
Avant d'exécuter Terraform ou l'un des prochains scripts, assurez-vous toujours d'être connecté à l'abonnement Azure correct en utilisant az login.
Créons le compte de stockage et le conteneur pour notre état Terraform:
az login
./scripts/create-tfstate-storage.sh
Exécuter Terraform
Lors de l'exécution de terraform apply, Terraform demandera certaines variables d'entrée. Vous pouvez trouver les valeurs nécessaires dans le fichier input-output.tf du même dossier.
Vous pouvez également créer un fichier terraform.tfvars avec les valeurs nécessaires, donc vous n'avez pas à les saisir à chaque fois.
Note: Assurez-vous de ne pas enregistrer vos fichiers .tfvars dans le contrôle de version
azure_subscription_id = "<azure-subscription-id>"
azure_cluster_name = "aks-westeu-prod"
cloudflare_api_token = "<cloudflare-api-token>"
cloudflare_zone_id = "<cloudflare-zone-id>"
plausible_dns = "plausible.example.com"
letsencrypt_email = "[email protected]"
Avant de commencer, assurez-vous d'avoir les informations requises disponibles. Vous pouvez créer un compte Cloudflare gratuit et le lier à un DNS que vous possédez, ou créer un nouveau DNS. Vous pouvez créer un jeton API en suivant ces instructions et utiliser le modèle "Edit zone DNS". Vous pouvez trouver votre ID de zone en suivant ces instructions. Vous pouvez trouver votre ID d'abonnement Azure en suivant ces instructions.
Si vous ne spécifiez pas les variables Cloudflare, le DNS ne sera pas mis à jour, mais tout le reste fonctionnera toujours et vous verrez l'adresse IP (à utiliser pour accéder à Plausible) à la fin. Vous devez créer un enregistrement DNS avec cette adresse IP vous-même, car l'émetteur de certificat en a besoin pour valider l'enregistrement DNS avant de pouvoir émettre un certificat valide.
Maintenant, déployons votre environnement:
# Nom de l'environnement: Azure Kubernetes Service - Europe Occidentale - Production
cd aks-westeu-prod
terraform init
terraform apply
Terraform va:
- Déployer le cluster AKS
- Installer Plausible via Helm
- Mettre à jour le DNS Cloudflare
Sauvegarde & Restauration (Optionnel)
Dans votre groupe de ressources rg-nodes-aks-westeu-prod, vous trouverez les deux disques Azure qui contiennent toutes les données de la solution Plausible: pv-disk-plausible-analytics-v3-clickhouse-0 & pv-disk-plausible-analytics-v3-postgresql-0. Vous pouvez créer des sauvegardes horaires, quotidiennes ou hebdomadaires de ces disques en utilisant Azure Backup Vault.
Pour restaurer une sauvegarde, créez un instantané de la sauvegarde spécifique vers un groupe de ressources et remplissez les ID d'instantané dans les variables suivantes trouvées dans le fichier aks-westeu-prod/input-output.tf: postgresql_restore_snapshot_id et clickhouse_restore_snapshot_id.
La prochaine fois que vous exécuterez terraform apply, Plausible sera restauré avec les sauvegardes.
Détruire l'environnement
Pour détruire l'environnement et toutes les ressources associées, vous pouvez exécuter la commande suivante:
terraform destroy
Structure de la solution
Pour faire fonctionner la solution du début à la fin, il y avait quelques obstacles à surmonter. Dans ce chapitre, j'examinerai ces obstacles et comment je les ai résolus, mais laissez-moi d'abord fournir un aperçu général de la solution.
terraform/
├── aks-westeu-prod/
│ ├── app-plausible.tf
│ ├── aks-cluster.tf
│ └── provider.tf
├── helm-charts/
│ ├── letsencrypt-cert-issuer/
│ │ ├── templates/
│ │ │ ├── letsencrypt-cluster-issuer-staging.yaml
│ │ │ └── letsencrypt-cluster-issuer.yaml
│ │ ├── Chart.yaml
│ │ └── values.yaml
├── modules/
│ ├── aks-cluster/
│ │ ├── aks-cluster.tf
│ │ ├── ingress-and-certificates.tf
│ │ └── input-output.tf
│ ├── persistent-azure-disk-volume/
│ │ ├── input.tf
│ │ └── persistent-azure-disk-volume.tf
│ └── plausible/
│ │ ├── disks.tf
│ │ ├── input.tf
│ │ ├── namespace.tf
│ │ └── plausible.tf
├── scripts/
│ ├── create-tfstate-storage.sh
│ └── download-terraform-project.sh
aks-westeu-prod: Une configuration d'environnement de production pour le déploiement vers Azure West Europe. Vous pouvez utiliser ce dossier comme modèle pour créer plus d'environnements. Les fichiers préfixés avecapp-montrent les différentes applications installées dans le cluster.helm-charts: Graphiques Helm personnalisésletsencrypt-cert-issuer: Au lieu de déployer les ressources ClusterIssuer séparément, je les ai empaquetées dans un graphique Helm
modules: Chaque module encapsule une responsabilité spécifiqueaks-cluster: Déploie un cluster AKS avec un émetteur de certificat Let's Encrypt, nginx ingress comme équilibreur de charge, et attend que l'IP publique soit disponiblepersistent-azure-disk-volume: Crée un disque Azure ou en restaure un en utilisant un instantané et crée ensuite un volume persistant et une réclamation de volume persistant dans Kubernetesplausible: Installe Plausible et ses dépendances via Helm
Obstacle: Détails de connexion du nouveau cluster pas encore disponibles
Après avoir créé le cluster Kubernetes, nous voulons pouvoir déployer des ressources dessus. Mais à l'étape du plan Terraform, les informations sur la façon de se connecter à ce nouvel environnement ne sont pas encore disponibles. Par conséquent, nous avons dû prendre deux mesures pour créer un déploiement transparent.
- Configuration dynamique du fournisseur: Les informations du cluster AKS sont définies dynamiquement pour les fournisseurs Helm et Kubernetes en récupérant les informations de connexion du cluster nouvellement créé:
provider "helm" {
kubernetes = {
# Utiliser la configuration dynamique du fournisseur pour utiliser le cluster nouvellement créé directement
host = module.aks_cluster.kube_config.host
client_certificate = base64decode(module.aks_cluster.kube_config.client_certificate)
client_key = base64decode(module.aks_cluster.kube_config.client_key)
cluster_ca_certificate = base64decode(module.aks_cluster.kube_config.cluster_ca_certificate)
}
}
provider "kubernetes" {
# Utiliser la configuration dynamique du fournisseur pour utiliser le cluster nouvellement créé directement
host = module.aks_cluster.kube_config.host
client_certificate = base64decode(module.aks_cluster.kube_config.client_certificate)
client_key = base64decode(module.aks_cluster.kube_config.client_key)
cluster_ca_certificate = base64decode(module.aks_cluster.kube_config.cluster_ca_certificate)
}
- Définir le contexte kubectl local: Après la création du cluster AKS, nous écrivons la nouvelle configuration kube et définissons le contexte kubectl sur la machine locale, de cette façon les commandes local-exec peuvent immédiatement se connecter au nouveau cluster.
resource "null_resource" "set_kube_context" {
provisioner "local-exec" {
command = <<EOT
# Nous l'obtenons de l'état Terraform et l'ajoutons au kubeconfig
echo '${azurerm_kubernetes_cluster.aks_cluster.kube_config_raw}' > ~/.kube/config
export KUBECONFIG=~/.kube/config
kubectl config use-context ${azurerm_kubernetes_cluster.aks_cluster.name}
EOT
}
// Toujours définir le contexte kube lors de l'exécution d'apply, même si aucun changement n'a été apporté au cluster
triggers = {
always_run = "${timestamp()}"
}
depends_on = [azurerm_kubernetes_cluster.aks_cluster]
}
Obstacle: IP de l'équilibreur de charge pas encore disponible
Lors du déploiement d'une version helm, terraform se termine avant que la version ne soit complètement déployée. Il ne fournit pas non plus les informations IP de l'équilibreur de charge. Par conséquent, j'ai implémenté deux scripts locaux qui attendent le déploiement de nginx ingress et collectent l'IP de l'équilibreur de charge, qui est nécessaire pour mettre à jour votre DNS.
# Attendre que la version helm ingress-nginx soit déployée
resource "null_resource" "wait_for_ingress_nginx" {
provisioner "local-exec" {
command = <<EOT
for i in {1..30}; do
kubectl get svc -n ingress-nginx ${helm_release.ingress_nginx.name}-controller && sleep 30 && break || sleep 30;
done
EOT
}
depends_on = [helm_release.ingress_nginx]
}
# Obtenir l'IP externe en utilisant kubectl
data "external" "ingress_external_ip" {
program = ["bash", "-c", <<EOT
EXTERNAL_IP=$(kubectl get svc -n ingress-nginx ${helm_release.ingress_nginx.name}-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo "")
echo "{\"ip\":\"$EXTERNAL_IP\"}"
EOT
]
depends_on = [null_resource.wait_for_ingress_nginx]
}
Obstacle: Les données dans notre solution ne sont pas sûres
Lors de l'utilisation du graphique helm plausible, il crée deux bases de données: PostgreSQL et ClickHouse. Par défaut, ces bases de données utilisent un stockage éphémère, ce qui signifie que lorsque le pod est supprimé ou replanifié, toutes les données sont perdues. Pour s'assurer que nos données sont en sécurité, nous devons utiliser un stockage persistant. Dans un environnement cloud comme Azure, nous pouvons utiliser les disques Azure pour cela.
J'ai créé un module pour créer ou restaurer un disque Azure et le connecter à Kubernetes en créant un volume persistant et une réclamation de volume persistant.
Voici comment vous pouvez utiliser le module et le lier à votre déploiement Helm Plausible.
module "create_pv_postgresql" {
source = "../persistent-azure-disk-volume"
snapshot_id = var.postgresql_restore_snapshot_id
azure_location = var.azure_disk_location
pvc_namespace = var.namespace
pv_name = "pv-disk-${var.name}-postgresql-0"
pvc_name = "pvc-disk-${var.name}-postgresql-0"
azure_resource_group_name = var.azure_disk_resource_group_name
disk_size_gb = var.plausible_config_disk_size # Gardez ceci égal à la taille définie dans le graphique helm plausible
depends_on = [kubernetes_namespace.plausible_analytics]
}
# l'existingClaim est défini sur le pvc_name pour postgresql et clickhouse
postgresql:
primary:
persistence:
enabled: true
existingClaim: pvc-disk-${var.name}-postgresql-0
size: ${var.plausible_config_disk_size}Gi # Cette base de données n'est utilisée que pour les paramètres et les données utilisateur, elle n'a donc pas besoin d'être très grande
...
clickhouse:
persistence:
enabled: true
existingClaim: pvc-disk-${var.name}-clickhouse-0
size: ${var.plausible_data_disk_size}Gi # Cette base de données est utilisée pour stocker toutes les données d'analyse, elle doit donc être plus grande
Obstacle: La version Helm de Plausible n'est pas exposée
Lors du déploiement de Plausible via Helm, il n'expose pas le service par défaut. Pour le rendre accessible depuis Internet, nous devons configurer une ressource ingress. Lors de la configuration de l'ingress, nous pouvons également spécifier l'annotation cert-manager pour garantir la création du certificat.
ingress:
enabled: true
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-production"
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
className: nginx
hosts:
- ${var.plausible_dns}
path: /
pathType: Prefix
tls:
- secretName: letsencrypt-production
hosts:
- ${var.plausible_dns}
Obstacle: Impossible de restaurer plausible à partir d'une sauvegarde lorsque l'environnement existe déjà
Lors du changement des ID d'instantané dans le fichier terraform.tfvars, Terraform ne recrée pas la version helm de plausible ou les réclamations de volume persistant, car il ne voit aucun changement en eux. Cela empêche la suppression et la recréation des disques azure et des volumes persistants, car ils ne sont jamais déliés. J'ai donc ajouté une null_resource qui déclenche un remplacement de la version plausible et des réclamations de volume persistant lorsque les ID d'instantané changent. De cette façon, vous pouvez spécifier de nouveaux ID d'instantané et faire recréer les ressources sans intervention manuelle.
resource "null_resource" "snapshot_trigger" {
triggers = {
postgresql_snapshot = var.postgresql_restore_snapshot_id
clickhouse_snapshot = var.clickhouse_restore_snapshot_id
}
}
...
lifecycle {
replace_triggered_by = [
null_resource.snapshot_trigger
]
}
resource "null_resource" "snapshot_trigger" {
triggers = {
snapshot = var.snapshot_id
}
}
...
lifecycle {
replace_triggered_by = [
null_resource.snapshot_trigger
]
}
Obstacle: coûts inutiles dans notre cluster AKS
Dans mon article précédent Éliminer les bannières de cookies: Exécutez Plausible Analytics sur Azure Kubernetes, vous avez appris quelques astuces pour réduire les coûts de votre cluster AKS. Celles-ci sont également intégrées dans cette solution.
- Utiliser des disques éphémères: Ceux-ci sont stockés directement sur le stockage local de la VM et sont fournis sans coût supplémentaire.
- Configuration Standard_B2s: La configuration VM la plus rentable disponible
- Augmenter le nombre de pods par nœud: Pour permettre plus de charges de travail sur l'instance Standard_B2s
Réflexions finales
Nous avons réussi à transformer les scripts bash de notre article précédent en un déploiement Kubernetes de qualité production sur Azure. En exploitant l'approche déclarative de Terraform et l'infrastructure gérée d'AKS, vous avez maintenant une instance Plausible Analytics qui ne se contente pas de fonctionner—elle est évolutive, maintenable et prête pour le trafic du monde réel.
La beauté de cette approche Infrastructure as Code réside dans sa répétabilité. Besoin d'un environnement de staging? Dupliquez simplement le dossier aks-westeu-prod avec des variables différentes. Vous voulez déployer dans une autre région? Changez un seul paramètre. Chaque décision d'infrastructure est documentée dans le code, examinée via des pull requests, et peut être annulée si nécessaire.
Bien que cette configuration puisse sembler excessive pour un simple outil d'analyse, les modèles que vous avez appris ici (Terraform modularisé, intégration cert-manager, gestion appropriée des secrets) vous serviront bien pour toute charge de travail Kubernetes en production.
