Gérer une Infrastructure as Code avec Terraform, Cloud Build et GitOps

Ce tutoriel explique comment gérer une Infrastructure as Code avec Terraform et Cloud Build à l'aide de la méthodologie GitOps. Le terme GitOps a été inventé par Weaveworks. Son concept clé consiste à utiliser un dépôt Git pour stocker l'état que vous souhaitez pour votre environnement. Terraform est un outil Open Source d'HashiCorp qui vous permet de créer, de modifier et d'améliorer de manière prévisible votre infrastructure cloud en utilisant du code. Dans ce tutoriel, vous allez utiliser Cloud Build, un service d'intégration continue de Google Cloud, pour appliquer automatiquement des fichiers manifestes Terraform à votre environnement.

Ce tutoriel s'adresse aux développeurs et aux opérateurs qui recherchent une stratégie conviviale pour apporter des modifications prévisibles à l'infrastructure. Dans cet article, nous partons du principe que vous connaissez Google Cloud, Linux et GitHub.

Les rapports sur l'état du DevOps identifient les fonctionnalités qui permettent d'améliorer les performances de livraison de logiciels. Ce tutoriel vous permettra d'effectuer les opérations suivantes :

Architecture

Pour comprendre comment ce tutoriel applique les pratiques de GitOps en matière de gestion des exécutions de Terraform, analysez le schéma d'architecture suivant. Notez que les branches GitHub dev et prod sont utilisées pour représenter des environnements réels. Ces environnements sont définis par des réseaux VPC (Virtual Private Cloud), respectivement dev et prod, dans un projet Google Cloud.

Infrastructure avec environnements dev et prod.

Le processus commence lorsque vous envoyez le code Terraform à la branche dev ou prod. Dans ce scénario, Cloud Build se déclenche, puis applique les fichiers manifestes Terraform pour obtenir l'état souhaité dans l'environnement concerné. D'autre part, lorsque vous transférez du code Terraform vers une autre branche, par exemple une branche de fonctionnalité, Cloud Build s'exécute pour exécuter terraform plan, mais rien n'est appliqué aux environnements.

Idéalement, les développeurs ou les opérateurs doivent faire des propositions d'infrastructure à des branches non protégées, puis les soumettre par le biais de demandes d'extraction. L'application GitHub Cloud Build, décrite plus loin dans ce tutoriel, déclenche automatiquement les tâches de compilation et associe les rapports terraform plan à ces demandes d'extraction. Ainsi, vous pouvez discuter des modifications potentielles et les examiner avec les collaborateurs, et ajouter des commits de suivi avant que les modifications ne soient fusionnées dans la branche de base.

Si aucun problème n'est soulevé, vous devez d'abord fusionner les modifications dans la branche dev. Cette fusion déclenche un déploiement d'infrastructure dans l'environnement dev, ce qui vous permet de tester cet environnement. Une fois que vous avez testé votre infrastructure et que vous êtes sûr de la fiabilité de votre déploiement, vous devez fusionner la branche dev dans la branche prod pour déclencher l'installation de l'infrastructure dans l'environnement de production.

Objectifs

  • Configurez votre dépôt GitHub.
  • Configurez Terraform pour stocker l'état dans un bucket Cloud Storage.
  • Accordez des autorisations à votre compte de service Cloud Build.
  • Connectez Cloud Build à votre dépôt GitHub.
  • Modifiez la configuration de votre environnement dans une branche de fonctionnalité.
  • Faites la promotion des modifications dans l'environnement de développement.
  • Faites la promotion des modifications dans l'environnement de production.

Coûts

Ce tutoriel utilise les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Avant de commencer

  1. Connectez-vous à votre compte Google.

    Si vous n'en possédez pas déjà un, vous devez en créer un.

  2. Dans Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Cloud.

    Accéder à la page de sélection du projet

  3. Vérifiez que la facturation est activée pour votre projet Google Cloud. Découvrez comment vérifier que la facturation est activée pour votre projet.

  4. Dans Cloud Console, activez Cloud Shell.

    Activer Cloud Shell

    En bas de la fenêtre de Cloud Console, une session Cloud Shell démarre et affiche une invite de ligne de commande. Cloud Shell est un environnement shell dans lequel le SDK Cloud est déjà installé (y compris l'outil de ligne de commande gcloud), et dans lequel des valeurs sont déjà définies pour votre projet actuel. L'initialisation de la session peut prendre quelques secondes.

  5. Dans Cloud Shell, récupérez l'ID du projet que vous venez de sélectionner :
    gcloud config get-value project
    Si cette commande ne renvoie pas l'ID du projet, configurez Cloud Shell pour utiliser votre projet, en remplaçant PROJECT_ID par l'ID de votre projet.
    gcloud config set project PROJECT_ID
  6. Activez les API requises :
    gcloud services enable cloudbuild.googleapis.com compute.googleapis.com
    Cette étape peut prendre quelques minutes.
  7. Si vous n'avez jamais utilisé Git dans Cloud Shell, configurez-le avec votre nom et votre adresse e-mail :
    git config --global user.email "your-email-address"
    git config --global user.name "your-name"
    
    Git utilise ces informations pour vous identifier en tant qu'auteur des commits que vous créez dans Cloud Shell.

Une fois que vous avez terminé ce tutoriel, vous pouvez éviter de continuer à payer des frais en supprimant les ressources que vous avez créées. Consultez la page Effectuer un nettoyage pour en savoir plus.

Configurer votre dépôt GitHub

Dans ce tutoriel, vous utilisez un seul dépôt Git pour définir votre infrastructure cloud. Vous orchestrez cette infrastructure grâce à différentes branches correspondant à différents environnements :

  • La branche dev contient les dernières modifications appliquées à l'environnement de développement.
  • La branche prod contient les dernières modifications appliquées à l'environnement de production.

Grâce à cette infrastructure, vous pouvez toujours faire référence au dépôt pour savoir quelle configuration est attendue dans chaque environnement et pour proposer de nouvelles modifications en les fusionnant d'abord dans l'environnement dev. Vous faites ensuite la promotion des modifications en fusionnant la branche dev dans la branche prod suivante.

Pour commencer, vous allez dupliquer le dépôt solutions-terraform-cloudbuild-gitops.

  1. Sur GitHub, accédez à https://github.com/GoogleCloudPlatform/solutions-terraform-cloudbuild-gitops.git.
  2. En haut à droite de la page, cliquez sur Fork (Dupliquer).

    Duplication d'un dépôt.

    Vous disposez maintenant d'une copie du dépôt solutions-terraform-cloudbuild-gitops avec les fichiers sources.

  3. Dans Cloud Shell, clonez ce dépôt dupliqué en remplaçant your-github-username par votre nom d'utilisateur GitHub :

    cd ~
    git clone https://github.com/your-github-username/solutions-terraform-cloudbuild-gitops.git
    cd ~/solutions-terraform-cloudbuild-gitops
    

Le code dans ce dépôt est structuré comme suit :

  • Le dossier environments/ contient des sous-dossiers représentant des environnements, tels que dev et prod, qui offrent une séparation logique entre les charges de travail à différents stades de maturité (développement et production, respectivement). Bien qu'il soit judicieux d'avoir des environnements aussi similaires que possible, chaque sous-dossier possède sa propre configuration Terraform. Cela permet de garantir qu'ils peuvent avoir des paramètres uniques, si nécessaire.

  • Le dossier modules/ contient des modules Terraform intégrés. Ces modules représentent des regroupements logiques de ressources connexes et sont utilisés pour partager du code dans différents environnements.

  • Le fichier cloudbuild.yaml est un fichier de configuration de compilation qui contient des instructions pour Cloud Build, par exemple pour indiquer comment effectuer des tâches correspondant à un ensemble d'étapes. Ce fichier spécifie une exécution conditionnelle selon la branche à partir de laquelle Cloud Build récupère le code, par exemple :

    • Pour les branches dev et prod, les étapes suivantes sont exécutées :

      1. terraform init
      2. terraform plan
      3. terraform apply
    • Pour toute autre branche, les étapes suivantes sont exécutées :

      1. terraform init pour tous les sous-dossiers environments
      2. terraform plan pour tous les sous-dossiers environments

terraform init et terraform plan s'exécutent pour tous les sous-dossiers environments, ce qui permet de s'assurer que les modifications proposées sont valables pour tous les environnements. Ainsi, avant de fusionner la demande d'extraction, vous pouvez passer en revue les plans pour vous assurer que l'accès n'est pas accordé à une entité non autorisée, par exemple.

Configurer Terraform pour stocker l'état dans un bucket Cloud Storage

Par défaut, Terraform stocke l'état localement dans un fichier nommé terraform.tfstate. Cette configuration par défaut peut rendre l'utilisation de Terraform difficile pour les équipes, en particulier lorsque de nombreux utilisateurs exécutent Terraform en même temps et que chaque ordinateur possède sa propre compréhension de l'infrastructure actuelle.

Pour vous aider à éviter de tels problèmes, cette section permet de configurer un état distant qui pointe vers un bucket Cloud Storage. Un état distant est une fonctionnalité des backends. Dans ce tutoriel, il est configuré dans les fichiers backend.tf. Par exemple :

# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

terraform {
  backend "gcs" {
    bucket = "PROJECT_ID-tfstate"
    prefix = "env/dev"
  }
}

Dans les étapes suivantes, vous allez créer un bucket Cloud Storage et modifier quelques fichiers pour qu'ils pointent vers votre nouveau bucket et votre projet Google Cloud.

  1. Dans Cloud Shell, créez le bucket Cloud Storage :

    PROJECT_ID=$(gcloud config get-value project)
    gsutil mb gs://${PROJECT_ID}-tfstate
    
  2. Activez la gestion des versions des objets pour conserver l'historique de vos déploiements :

    gsutil versioning set on gs://${PROJECT_ID}-tfstate
    

    L'activation de la gestion des versions des objets augmente les coûts de stockage. Vous pouvez limiter cette augmentation des coûts en configurant la gestion du cycle de vie des objets afin de supprimer les anciennes versions d'état.

  3. Remplacez l'espace réservé PROJECT_ID par l'ID de projet dans les fichiers terraform.tfvars et backend.tf :

    cd ~/solutions-terraform-cloudbuild-gitops
    sed -i s/PROJECT_ID/$PROJECT_ID/g environments/*/terraform.tfvars
    sed -i s/PROJECT_ID/$PROJECT_ID/g environments/*/backend.tf
    
  4. Vérifiez que tous les fichiers ont bien été mis à jour :

    git status
    

    Le résultat ressemble à ceci :

    On branch dev
    Your branch is up-to-date with 'origin/dev'.
    Changes not staged for commit:
     (use "git add <file>..." to update what will be committed)
     (use "git checkout -- <file>..." to discard changes in working directory)
           modified:   environments/dev/backend.tf
           modified:   environments/dev/terraform.tfvars
           modified:   environments/prod/backend.tf
           modified:   environments/prod/terraform.tfvars
    no changes added to commit (use "git add" and/or "git commit -a")
    
  5. Procédez au commit et au transfert de vos modifications :

    git add --all
    git commit -m "Update project IDs and buckets"
    git push origin dev
    

    Selon votre configuration GitHub, vous devrez peut-être vous authentifier pour appliquer les modifications précédentes.

Accorder des autorisations à votre compte de service Cloud Build

Pour permettre au compte de service Cloud Build d'exécuter des scripts Terraform dans le but de gérer des ressources Google Cloud, vous devez lui accorder l'accès approprié à votre projet. Pour plus de simplicité, l'accès à l'éditeur de projet est accordé dans ce tutoriel. Toutefois, lorsque le rôle d'éditeur de projet dispose d'une autorisation étendue, vous devez suivre les bonnes pratiques de votre entreprise en matière de sécurité informatique dans les environnements de production, en fournissant généralement un accès selon la stratégie du moindre privilège.

  1. Dans Cloud Shell, récupérez l'adresse e-mail du compte de service Cloud Build de votre projet :

    CLOUDBUILD_SA="$(gcloud projects describe $PROJECT_ID \
        --format 'value(projectNumber)')@cloudbuild.gserviceaccount.com"
    
  2. Accordez l'accès requis à votre compte de service Cloud Build :

    gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member serviceAccount:$CLOUDBUILD_SA --role roles/editor
    

Connecter directement Cloud Build à votre dépôt GitHub

Cette section vous explique comment installer l'application GitHub Cloud Build. Cette installation vous permet de connecter votre dépôt GitHub à votre projet Google Cloud afin que Cloud Build puisse automatiquement appliquer vos fichiers manifestes Terraform chaque fois que vous créez une branche ou que vous transférez du code vers GitHub.

Procédez comme suit pour installer l'application uniquement pour le dépôt solutions-terraform-cloudbuild-gitops. Vous pouvez également choisir de l'installer pour plusieurs dépôts ou l'ensemble de ceux-ci.

  1. Accédez à la page GitHub Marketplace pour l'application Cloud Build :

    Ouvrir la page de l'application Cloud Build

  2. Si c'est la première fois que vous configurez une application dans GitHub, cliquez sur Setup with Google Cloud Build (Configurer avec Google Cloud Build). Sinon, cliquez sur Edit your plan (Modifier votre forfait), sélectionnez vos informations de facturation, puis sur la page Edit your plan (Modifier votre forfait), cliquez sur grant this app access (Accorder l'accès à cette application).

  3. Sur la page Install Google Cloud Build (Installer Google Cloud Build), sélectionnez Only select repositories (Sélectionner uniquement les dépôts) et saisissez your-user/solutions-terraform-cloudbuild-gitops pour vous connecter à votre dépôt dupliqué.

  4. Cliquez sur Installer.

  5. Connectez-vous à Google Cloud.

    La page d'autorisation s'affiche. Vous êtes invité à autoriser l'application GitHub Cloud Build à se connecter à Google Cloud.

    Connectez-vous à Google Cloud

  6. Cliquez sur Authorize Google Cloud Build by GoogleCloudBuild (Autoriser Google Cloud Build par GoogleCloudBuild).

    Vous êtes redirigé vers Cloud Console.

  7. Sélectionnez le projet Google Cloud sur lequel vous travaillez.

  8. Si vous acceptez les conditions d'utilisation, cochez la case puis cliquez sur Next (Suivant).

  9. À l'étape Repository selection (Sélection du dépôt), sélectionnez your-user/solutions-terraform-cloudbuild-gitops pour vous connecter à votre projet Google Cloud, puis cliquez sur Connect (Se connecter).

  10. Cliquez sur Done (Terminé), puis sur Connect (Se connecter).

L'application GitHub Cloud Build est maintenant configurée et votre dépôt GitHub est lié à votre projet Google Cloud. À partir de maintenant, les modifications apportées au dépôt GitHub déclenchent des exécutions de Cloud Build, qui renvoient les résultats à GitHub à l'aide des vérifications GitHub.

Modifier la configuration de votre environnement dans une nouvelle branche de fonctionnalité

Votre environnement est désormais presque entièrement configuré. Il est donc temps de modifier le code de votre environnement de développement.

  1. Sur GitHub, accédez à la page principale de votre dépôt dupliqué.
  2. Assurez-vous que vous vous trouvez dans la branche dev.

    Assurez-vous que vous êtes dans la branche dev.

  3. Pour ouvrir le fichier pour modification, accédez au fichier modules/firewall/main.tf et cliquez sur l'icône en forme de crayon.

  4. À la ligne 15, corrigez la faute de frappe "http-server**2**" dans le champ target_tags.

    La valeur doit être égale à "http-server".

  5. Ajoutez un message de commit en bas de la page, tel que "Correction de la cible de pare-feu http", puis sélectionnez Create a new branch for this commit (Créer une branche pour ce commit).

  6. Cliquez sur Propose file change (Proposer la modification de fichier).

  7. Sur la page suivante, cliquez sur Create pull request (Créer une demande d'extraction) pour ouvrir une nouvelle demande d'extraction avec votre modification.

    Une fois votre demande d'extraction ouverte, une tâche Cloud Build est automatiquement lancée.

  8. Cliquez sur Show all checks (Afficher toutes les vérifications) et attendez que les coches de la fenêtre de vérification passent au vert.

    Affichez toutes les vérification dans une demande d'extraction.

  9. Cliquez sur Details (Détails) pour afficher plus d'informations, y compris la sortie de terraform plan sur le lien Afficher plus d'informations sur Google Cloud Build.

Notez que la tâche Cloud Build a exécuté le pipeline défini dans le fichier cloudbuild.yaml. Comme indiqué précédemment, ce pipeline présente différents comportements en fonction de la branche à récupérer. La compilation vérifie si la variable $BRANCH_NAME correspond à un dossier d'environnement. Si oui, Cloud Build exécute terraform plan pour cet environnement. Sinon, Cloud Build exécute terraform plan pour tous les environnements afin de s'assurer que la modification proposée est valable pour tous. Si l'exécution de l'un de ces plans échoue, la compilation échoue également.

- id: 'tf plan'
  name: 'hashicorp/terraform:0.11.14'
  entrypoint: 'sh'
  args:
  - '-c'
  - |
      if [ -d "environments/$BRANCH_NAME/" ]; then
        cd environments/$BRANCH_NAME
        terraform plan
      else
        for dir in environments/*/
        do
          cd ${dir}
          env=${dir%*/}
          env=${env#*/}
          echo ""
          echo "*************** TERRAFOM PLAN ******************"
          echo "******* At environment: ${env} ********"
          echo "*************************************************"
          terraform plan || exit 1
          cd ../../
        done
      fi 

De même, la commande terraform apply s'exécute pour les branches de l'environnement, mais elle est complètement ignorée dans les autres cas. Dans cette section, vous avez soumis une modification de code à une nouvelle branche, et aucun déploiement d'infrastructure n'a été appliqué à votre projet Google Cloud.

- id: 'tf apply'
  name: 'hashicorp/terraform:0.11.14'
  entrypoint: 'sh'
  args:
  - '-c'
  - |
      if [ -d "environments/$BRANCH_NAME/" ]; then
        cd environments/$BRANCH_NAME
        terraform apply -auto-approve
      else
        echo "***************************** SKIPPING APPLYING *******************************"
        echo "Branch '$BRANCH_NAME' does not represent an oficial environment."
        echo "*******************************************************************************"
      fi

Forcer l'exécution Cloud Build avant la fusion des branches

Pour vous assurer que les fusions ne peuvent être appliquées que lorsque les exécutions Cloud Build respectives ont abouti, procédez comme suit :

  1. Sur GitHub, accédez à la page principale de votre dépôt dupliqué.
  2. Sous le nom de votre dépôt, cliquez sur Settings (Paramètres).
  3. Dans le menu de gauche, cliquez sur Branches.
  4. Sous Branch protection rules (Règles de protection des branches), cliquez sur Add rule (Ajouter une règle).
  5. Sous Branch name pattern (Modèle de nom de branche), sélectionnez dev.
  6. Sous Rule settings (Paramètres de la règle), sélectionnez Require status checks to pass before merging (Exiger la validation des vérifications d'état avant la fusion), puis sous Status checks found in the last week for this repository (Vérifications d'état trouvées la semaine dernière pour ce dépôt), cliquez sur Build (Compiler).
  7. Cliquez sur Create (Créer).
  8. Répétez les étapes 5 à 8 en définissant le modèle de nom de branche sur prod.

Cette configuration est importante pour protéger les branches dev et prod. En effet, les commits doivent d'abord être transférés vers une autre branche avant de pouvoir être fusionnés dans la branche protégée. Dans ce tutoriel, la protection nécessite que l'exécution de Cloud Build réussisse pour que la fusion soit autorisée.

Promouvoir des modifications dans l'environnement de développement

Vous disposez d'une demande d'extraction en attente de fusion. Il est temps d'appliquer l'état de votre choix à votre environnement dev.

  1. Sur GitHub, accédez à la page principale de votre dépôt dupliqué.
  2. Sous le nom de votre dépôt, cliquez sur Pull requests (Demandes d'extraction).
  3. Cliquez sur la demande d'extraction que vous venez de créer.
  4. Cliquez sur Merge pull request (Fusionner la demande d'extraction), puis sur Confirm merge (Confirmer la fusion).

    Confirmez la fusion.

  5. Vérifiez qu'une nouvelle compilation Cloud Build a été déclenchée :

    Accéder à la page Cloud Build

  6. Ouvrez la compilation et vérifiez les journaux.

    Lorsque la compilation est terminée, vous devez voir quelque chose de semblable à ceci :

    Step #3 - "tf apply": external_ip = external-ip-value
    Step #3 - "tf apply": firewall_rule = dev-allow-http
    Step #3 - "tf apply": instance_name = dev-apache2-instance
    Step #3 - "tf apply": network = dev
    Step #3 - "tf apply": subnet = dev-subnet-01
    
  7. Copiez external-ip-value et ouvrez l'adresse dans un navigateur Web.

    Ce provisionnement peut prendre quelques secondes pour démarrer la machine virtuelle et propager la règle de pare-feu. Au final, vous devez voir Environnement: dev dans le navigateur Web.

Promouvoir des modifications dans l'environnement de production

Maintenant que votre environnement de développement est entièrement testé, vous pouvez promouvoir votre code d'infrastructure en production.

  1. Sur GitHub, accédez à la page principale de votre dépôt dupliqué.
  2. Cliquez sur New pull request (Nouvelle demande d'extraction).
  3. Sélectionnez le dépôt que vous venez de dupliquer sous base repository (dépôt de base).
  4. Sous base, sélectionnez prod et sous compare (comparer), sélectionnez dev.
  5. Cliquez sur Create pull request (Créer une demande d'extraction).
  6. Sous title (titre), saisissez un titre tel que Promoting networking changes, puis cliquez sur Create pull request (Créer une demande d'extraction).
  7. Examinez les modifications proposées, y compris les détails de terraform plan de Cloud Build, puis cliquez sur Merge pull request (Fusionner la demande d'extraction).
  8. Cliquez sur Confirm merge (Confirmer la fusion).
  9. Dans Cloud Console, ouvrez la page Build History (Historique de compilation) pour voir vos modifications appliquées à l'environnement de production :

    Accéder à la page Cloud Build

  10. Attendez la fin de la compilation, puis consultez les journaux.

    À la fin des journaux, vous devez voir quelque chose de semblable à ceci :

    Step #3 - "tf apply": external_ip = external-ip-value
    Step #3 - "tf apply": firewall_rule = prod-allow-http
    Step #3 - "tf apply": instance_name = prod-apache2-instance
    Step #3 - "tf apply": network = prod
    Step #3 - "tf apply": subnet = prod-subnet-01
    
  11. Copiez external-ip-value et ouvrez l'adresse dans un navigateur Web.

    Ce provisionnement peut prendre quelques secondes pour démarrer la machine virtuelle et propager la règle de pare-feu. Au final, vous devez voir Environnement: prod dans le navigateur Web.

Vous avez configuré avec succès un pipeline d'Infrastructure as Code (IaC) sans serveur sur Cloud Build. À l'avenir, vous voudrez peut-être essayer les opérations suivantes :

  • Ajouter des déploiements pour des cas d'utilisation distincts
  • Créer des environnements supplémentaires pour répondre à vos besoins
  • Utiliser un projet par environnement au lieu d'un VPC par environnement

Effectuer un nettoyage

Une fois que vous avez terminé le tutoriel, nettoyez les ressources que vous avez créées sur Google Cloud pour qu'elles ne vous soient plus facturées à l'avenir.

Supprimer le projet

  1. Dans Cloud Console, accédez à la page Gérer les ressources.

    Accéder à la page Gérer les ressources

  2. Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer .
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.

Étapes suivantes