Créer un pipeline d'image d'OS personnalisée à l'aide de gcloud ou de Terraform

Configurez et envoyez un pipeline Image Builder de manière programmatique à l'aide de la Google Cloud CLI ou de Terraform. La configuration programmatique de votre pipeline vous permet de définir les paramètres d'infrastructure, les images d'OS de base, les actions de personnalisation et les tests de validation dans des fichiers de configuration déclaratifs.

Avant de commencer

Rôles requis

Pour obtenir les autorisations nécessaires pour créer et envoyer des pipelines de personnalisation d'images à l'aide de la Google Cloud CLI ou de Terraform, demandez à votre administrateur de vous accorder les rôles IAM suivants sur votre projet :

Pour en savoir plus sur l'attribution de rôles, consultez la page Gérer l'accès aux projets, aux dossiers et aux organisations.

Vous pouvez également obtenir les autorisations requises avec des rôles personnalisés ou d'autres rôles prédéfinis.

Créez des fichiers de configuration.

Pour configurer votre pipeline à l'aide de gcloud CLI ou de Terraform, créez deux fichiers de configuration :

  • imagebuilder.yaml : définit la recette de personnalisation de l'image, y compris l'image d'OS de base, les paramètres d'infrastructure de VM de nœud de calcul, les détails de sortie de l'image cible et les étapes de personnalisation séquentielles telles que l'exécution de scripts shell, le transfert de fichiers ou le redémarrage.
  • cloudbuild.yaml : orchestre les étapes du processus de compilation dans Cloud Build, y compris l'analyse, la validation, la compilation, le test et la publication de l'image d'OS personnalisée.

Créer le fichier de configuration de l'image

Pour spécifier la configuration de votre image, créez un fichier nommé imagebuilder.yaml dans votre répertoire local. Pour obtenir la liste complète de tous les champs de schéma et actions de personnalisation compatibles, consultez Schéma de recette de personnalisation et Actions de personnalisation compatibles.

L'exemple de fichier imagebuilder.yaml suivant configure un pipeline qui crée une image Ubuntu 22.04 LTS personnalisée à l'aide d'une VM de nœud de calcul e2-standard-4 dans la région et la zone que vous avez spécifiées, et effectue une mise à jour du package système.

apiVersion: imagebuilder.gcp.com/v1
kind: OSImageCustomization
metadata:
  name: customized-ubuntu-baseline
  description: "Ubuntu 22.04 LTS custom OS baseline image"
infrastructureConfig:
  machineType: e2-standard-4
  zone: ZONE
  debug: false
source:
  imageFamily: projects/ubuntu-os-cloud/global/images/family/ubuntu-2204-lts
destinations:
  - diskImage:
      name: custom-ubuntu-v1
      family: custom-ubuntu-family
      project: PROJECT_ID
      storageLocations:
        - REGION
spec:
  config:
    skipSystemTests: false
  steps:
    -   name: "System Package Update"
      action: Shell
      inputs:
        command: "apt-get update -y && apt-get upgrade -y"

Remplacez les valeurs d'espace réservé suivantes :

  • PROJECT_ID : ID de votre projet.
  • REGION : emplacement de stockage de l'image cible (par exemple, us-east1 ou europe-west1). Assurez-vous de remplir les conditions régionales et zonales suivantes :
    • Image Builder n'est disponible que dans les régions où Cloud Build est disponible.
    • La VM de nœud de calcul ZONE doit se trouver dans le REGION que vous avez spécifié.
    • Pour minimiser la latence réseau et éviter les frais de sortie interrégionaux, assurez-vous que la zone de votre VM de nœud de calcul, votre bucket Cloud Storage de préproduction, votre dépôt Artifact Registry et l'emplacement de stockage de l'image cible se trouvent dans la même région.
  • ZONE : zone située dans la REGION que vous avez spécifiée, par exemple us-east1-b ou europe-west1-b.

Créer le fichier de compilation de l'orchestrateur

Créez un fichier nommé cloudbuild.yaml dans le même répertoire. Ce fichier appelle les étapes du conteneur Image Builder pour créer, valider et publier l'image d'OS personnalisée.

substitutions:
  _GCS_WORKDIR: 'gs://STAGING_BUCKET_NAME/workdir/'
  _IMAGE_BUILDER_CONFIG_PATH: 'imagebuilder.yaml'
  _SERVICE_ACCOUNT: 'projects/PROJECT_ID/serviceAccounts/SERVICE_ACCOUNT_EMAIL'
  _IMAGE_OUTPUT_PATH: 'image-builder/binaryOut'
  _ARTIFACT_REGISTRY_RESOURCE_URI: 'projects/PROJECT_ID/locations/REGION/repositories/REPOSITORY_NAME/packages/PACKAGE_NAME/versions/v${BUILD_ID}'

steps:
  #  Step 1: Parse configs and run OS customization on worker VM
  - name: 'REGION-docker.pkg.dev/image-builder-official/release/builder:stable'
    script: |
      #!/usr/bin/env bash
      /build
    id: 'imagebuilder-customize'
    results:
      - name: image_builder_telemetry_metrics
      - name: base_image
        attestationType: "https://cloudbuild.googleapis.com/attestations/build_content_restrictions"
        attestationContent: base_image

  # Step 2: Validate by running system boot checks on a test VM
  - name: 'REGION-docker.pkg.dev/image-builder-official/release/validator:stable'
    script: |
      #!/usr/bin/env bash
      /validate
    id: 'imagebuilder-validate'
    results:
      - name: image_builder_telemetry_metrics

  # Step 3: Register image in Compute Engine and upload tar files to Artifact Registry
  - name: 'REGION-docker.pkg.dev/image-builder-official/release/builder:stable'
    script: |
      #!/usr/bin/env bash
      /publish
    id: 'imagebuilder-publish'
    results:
      - name: image_builder_telemetry_metrics

options:
  automapSubstitutions: true
  requestedVerifyOption: VERIFIED
  substitutionOption: ALLOW_LOOSE
  dynamicSubstitutions: true
  logging: CLOUD_LOGGING_ONLY

artifacts:
  generic_artifacts:
    - folder: '${_IMAGE_OUTPUT_PATH}'
      registry_path: '${_ARTIFACT_REGISTRY_RESOURCE_URI}'

timeout: '3600s'

Remplacez les valeurs d'espace réservé suivantes :

  • STAGING_BUCKET_NAME : bucket Cloud Storage existant dans votre projet à utiliser comme espace de préparation temporaire. Si vous n'avez pas de bucket, vous pouvez en créer un en exécutant gcloud storage buckets create gs://STAGING_BUCKET_NAME. Si vous déployez votre pipeline à l'aide de Terraform, Terraform crée automatiquement ce bucket.
  • PROJECT_ID : ID de votre projet Cloud de Confiance .
  • REGION : région de votre dépôt Artifact Registry, par exemple Cloud de Confiance , us-east1 ou europe-west1.
  • SERVICE_ACCOUNT_EMAIL : adresse e-mail du compte de service que vous avez configuré avec les autorisations IAM requises.
  • REPOSITORY et PACKAGE : dépôt cible et nom du package créés dans Artifact Registry. Pour configurer le registre Artifact Registry, consultez Configurer Artifact Registry.

Créer et envoyer le pipeline de compilation

Pour exécuter votre pipeline de personnalisation d'image, envoyez une compilation à l'aide de la gcloud CLI ou déployez votre pipeline à l'aide de Terraform. Sélectionnez l'un des onglets suivants :

gcloud

Pour déployer et exécuter votre pipeline de personnalisation d'image, exécutez la commande gcloud builds submit à partir du répertoire de votre terminal local contenant les deux fichiers de configuration :

gcloud builds submit . \
    --config=cloudbuild.yaml \
    --project=PROJECT_ID \
    --service-account="projects/PROJECT_ID/serviceAccounts/SERVICE_ACCOUNT_EMAIL" \
    --region=REGION

Remplacez les éléments suivants :

  • PROJECT_ID : ID de votre projet Cloud de Confiance .
  • REGION : région Cloud de Confiance dans laquelle exécuter votre job de pipeline de personnalisation d'image.
  • SERVICE_ACCOUNT_EMAIL : adresse e-mail du compte de service configuré avec les autorisations IAM requises.

Cette commande importe votre espace de travail de personnalisation, enregistre l'exécution Cloud Build et lance les conteneurs d'orchestration.

Terraform

Pour provisionner l'infrastructure requise pour créer et valider automatiquement des images d'OS personnalisées, vous pouvez utiliser Terraform. Cette configuration Terraform effectue les tâches suivantes :

  • Active les API Cloud de Confiance requises.
  • Crée un bucket Cloud Storage dédié (workdir_bucket) pour stocker les journaux temporaires et les artefacts de compilation.
  • Configure un déclencheur de compilation Cloud Build (image_builder_trigger) associé à votre connexion au dépôt GitHub Developer Connect.

Créer vos fichiers de configuration Terraform

Pour organiser et déployer l'infrastructure de votre pipeline à l'aide de Terraform, procédez comme suit :

  1. Créez un répertoire dédié sur votre poste de travail local ou dans votre environnement CI/CD, distinct de votre dépôt d'application, puis accédez-y :

    mkdir terraform-image-builder && cd terraform-image-builder
    
  2. Dans ce répertoire, créez les cinq fichiers de configuration Terraform suivants :

    • terraform.tfvars : définit les valeurs des variables spécifiques au projet.
    • main.tf : provisionne des ressources dans votre projet Cloud de Confiance , y compris en activant les API requises, en créant le bucket Cloud Storage intermédiaire (workdir_bucket) et en déployant le déclencheur de compilation Cloud Build (image_builder_trigger).
    • outputs.tf : définit les valeurs de sortie affichées dans votre terminal après le déploiement, telles que l'ID du déclencheur et le nom du bucket intermédiaire.
    • providers.tf : spécifie la version Terraform requise (>= 1.3) et configure le fournisseur Cloud de Confiance by S3NS (hashicorp/google).
    • variables.tf : définit les variables d'entrée, les valeurs par défaut et les règles de validation pour le déploiement.

    Sélectionnez l'un des onglets suivants pour afficher et copier la configuration de chaque fichier dans votre répertoire local :

    terraform.tfvars

    Ce fichier spécifie les valeurs des paramètres de votre environnement pour les variables déclarées :

    project_id                = "PROJECT_ID"
    builder_service_account   = "SERVICE_ACCOUNT_EMAIL"
    github_repo_name          = "projects/PROJECT_ID/locations/LOCATION/connections/CONNECTION/repositories/REPO_NAME"
    region                    = "REGION"
    trigger_name              = "TRIGGER_NAME"
    cloudbuild_yaml_path      = "CLOUDBUILD_YAML_PATH"
    image_builder_config_path = "RECIPE_PATH"
    gcs_lifecycle_age_days    = LIFECYCLE_DAYS
    ar_repository_id          = "REPOSITORY_NAME"
    ar_package_name           = "PACKAGE_NAME"
    

    Remplacez les espaces réservés suivants par vos ressources préexistantes :

    • PROJECT_ID : ID de votre projetCloud de Confiance by S3NS existant.
    • SERVICE_ACCOUNT_EMAIL : adresse e-mail de votre compte de service de compilation configuré dans Configurer le compte de service Image Builder.
    • LOCATION, CONNECTION et REPO_NAME : région hôte, nom de la connexion et lien du dépôt Developer Connect configurés dans Associer un dépôt.
    • CLOUDBUILD_YAML_PATH : chemin relatif de votre fichier cloudbuild.yaml dans votre répertoire local. Vous n'avez pas besoin d'enregistrer cloudbuild.yaml dans votre dépôt Git.
    • RECIPE_PATH : chemin relatif vers votre fichier de recette de personnalisation imagebuilder.yaml enregistré dans votre dépôt Git.
    • REPOSITORY_NAME : dépôt Artifact Registry générique existant créé dans Configurer Artifact Registry.

    Remplacez les espaces réservés suivants pour les ressources que Terraform crée :

    • REGION : région Cloud de Confiancecible dans laquelle Terraform provisionne le bucket Cloud Storage intermédiaire et le déclencheur de compilation Cloud Build, par exemple us-central1.
    • TRIGGER_NAME : nom du nouveau déclencheur de dépôt Cloud Build créé par Terraform, par exemple git-push-os-builder.
    • LIFECYCLE_DAYS : période de conservation en jours avant la suppression automatique des artefacts intermédiaires dans le bucket de préproduction Cloud Storage créé par Terraform, par exemple 30.
    • PACKAGE_NAME : nom que vous souhaitez que Terraform utilise pour le package créé dans votre dépôt Artifact Registry. Ce package stocke les versions publiées des images d'OS, par exemple ubuntu-custom.

    main.tf

    Ce fichier déclare les ressources d'infrastructure et les sources de données principales pour le déploiement :

    # Main resource configurations for Image Builder.
    # 1. Enable Required APIs
    resource "google_project_service" "apis" {
      for_each = toset([
        "compute.googleapis.com",
        "cloudbuild.googleapis.com",
        "artifactregistry.googleapis.com",
        "serviceusage.googleapis.com",
        "cloudresourcemanager.googleapis.com",
        "iam.googleapis.com",
        "storage.googleapis.com"
      ])
      project = var.project_id
      service = each.key
      disable_on_destroy = false
    }
    
    # 2. Project data source to retrieve Project Number
    data "google_project" "project" {
      project_id = var.project_id
      depends_on = [google_project_service.apis]
    }
    
    locals {
      builder_sa = var.builder_service_account
    }
    
    # 3. Storage Bucket for Image Builder Workdir
    resource "google_storage_bucket" "workdir_bucket" {
      name                        = var.gcs_bucket_name != "" ? var.gcs_bucket_name : "${var.project_id}-vm-builder-workdir"
      project                     = var.project_id
      location                    = var.region
      force_destroy               = true
      uniform_bucket_level_access = true
      lifecycle_rule {
        action {
          type = "Delete"
        }
        condition {
          age = var.gcs_lifecycle_age_days
        }
      }
      depends_on = [google_project_service.apis]
    }
    
    # 4. Cloud Build Trigger
    resource "google_cloudbuild_trigger" "image_builder_trigger" {
      name        = var.trigger_name
      location    = var.region
      project     = var.project_id
      description = "Trigger that runs Image Builder customization"
    
      service_account = var.builder_service_account != "" ? "projects/${var.project_id}/serviceAccounts/${var.builder_service_account}" : null
    
      repository_event_config {
        repository = replace(var.github_repo_name, "gitRepositoryLinks", "repositories")
        push {
          branch = "^main$"
        }
      }
      filename = var.cloudbuild_yaml_path
    
      substitutions = {
        _GCS_WORKDIR                    = "gs://${google_storage_bucket.workdir_bucket.name}/workdir/"
        _SERVICE_ACCOUNT                = "projects/${var.project_id}/serviceAccounts/${local.builder_sa}"
        _IMAGE_OUTPUT_PATH              = "image-builder/binaryOut"
        _PROJECT_ID                     = var.project_id
        _LOCATION                       = var.region
        _REPOSITORY_NAME                = var.ar_repository_id
        _PACKAGE_NAME                   = var.ar_package_name
        _IMAGE_BUILDER_CONFIG_PATH      = var.image_builder_config_path
        _ARTIFACT_REGISTRY_RESOURCE_URI = "projects/${var.project_id}/locations/${var.region}/repositories/${var.ar_repository_id}/packages/${var.ar_package_name}/versions/v$${BUILD_ID}"
      }
      depends_on = [
        google_project_service.apis
      ]
    }
    

    outputs.tf

    Ce fichier définit les attributs de sortie renvoyés à votre terminal après le déploiement :

    output "builder_service_account" {
      value       = local.builder_sa
      description = "The email representation of the resolved Image Builder service account."
    }
    
    output "workdir_bucket" {
      value       = google_storage_bucket.workdir_bucket.name
      description = "The name of the storage workdir bucket."
    }
    
    output "artifact_registry_repository" {
      value       = "projects/${var.project_id}/locations/${var.region}/repositories/${var.ar_repository_id}"
      description = "The fully qualified resource path of the Artifact Registry repository."
    }
    
    output "cloud_build_trigger_id" {
      value       = google_cloudbuild_trigger.image_builder_trigger.trigger_id
      description = "The unique ID for the created Cloud Build Trigger."
    }
    

    providers.tf

    Ce fichier configure la version et les paramètres de région Terraform requis :

    terraform {
      required_version = ">= 1.3"
      required_providers {
        google = {
          source  = "hashicorp/google"
          version = ">= 5.0, < 7.0"
        }
      }
    }
    
    provider "google" {
      project = var.project_id
      region  = var.region
    }
    

    variables.tf

    Ce fichier déclare toutes les variables d'entrée obligatoires et facultatives, ainsi que les règles de validation :

    variable "project_id" {
      type        = string
      description = "The target Project ID where resources will be created."
      validation {
        condition     = can(regex("^[a-z0-9-]{6,30}$", var.project_id))
        error_message = "The project_id must consist of lowercase letters, numbers, and hyphens, and be between 6 and 30 characters."
      }
    }
    
    variable "region" {
      type        = string
      default     = "us-central1"
      description = "Location used for cloud build triggers, storage buckets, and artifact registry."
    }
    
    variable "github_repo_name" {
      type        = string
      default     = ""
      description = "Developer Connect github repository details, format: projects/PROJECT_ID/locations/LOCATION/connections/CONNECTION/repositories/REPO_LINK"
      validation {
        condition     = can(regex("^projects/[^/]+/locations/[^/]+/connections/[^/]+/(gitRepositoryLinks|repositories)/[^/]+$", var.github_repo_name))
        error_message = "The github_repo_name must follow either the Cloud Build v2 repository link format (using '/repositories/') or the Developer Connect resource format (using '/gitRepositoryLinks/')."
      }
    }
    
    variable "builder_service_account" {
      type        = string
      default     = ""
      description = "The email representation of the pre-existing Image Builder service account. If omitted, the default Cloud Build service account will be used."
    }
    
    variable "gcs_bucket_name" {
      type        = string
      default     = ""
      description = "Custom name for the workdir storage bucket. If left empty, a default name using the project ID will be constructed."
    }
    
    variable "gcs_lifecycle_age_days" {
      type        = number
      default     = 30
      description = "The number of days after which temporary logs and artifacts in the storage workdir bucket are deleted."
    }
    
    variable "ar_repository_id" {
      type        = string
      default     = "vm-images"
      description = "The repository ID for the generic Artifact Registry hosting the final OS image tarballs."
    }
    
    variable "ar_package_name" {
      type        = string
      default     = "image-builder"
      description = "The package name under which the generic OS image artifact will be registered in Artifact Registry."
    }
    
    variable "trigger_name" {
      type        = string
      default     = "custom-os-image-builder"
      description = "The name of the Cloud Build trigger."
    }
    
    variable "cloudbuild_yaml_path" {
      type        = string
      default     = "cloudbuild.yaml"
      description = "The path to the cloudbuild.yaml configuration file relative to the repository root."
    }
    
    variable "image_builder_config_path" {
      type        = string
      default     = "imagebuilder.yaml"
      description = "The path to the imagebuilder.yaml configuration file relative to the repository root."
    }
    
  3. Pour déployer les configurations, exécutez les commandes suivantes dans le répertoire contenant vos fichiers Terraform :

    1. Initialisez le répertoire :
      terraform init
    2. Validez la syntaxe :
      terraform validate
    3. Prévisualisez le déploiement :
      terraform plan
    4. Appliquez la configuration :
      terraform apply

Vérifier et surveiller la compilation

Pour suivre la progression de votre pipeline de compilation, procédez comme suit :

  1. Dans la console Cloud de Confiance , accédez à la page Cloud Build.

    Accéder à Cloud Build

  2. Dans le menu de navigation, cliquez sur Historique pour afficher les tâches actives ou terminées.

  3. Dans la liste Compilations, cliquez sur l'ID de compilation de votre compilation pour inspecter les journaux d'exécution du conteneur. Les journaux affichent les étapes effectuées dans la VM du nœud de calcul, telles que les mises à jour des packages système ou les commandes shell personnalisées, suivies des résultats des tests de validation de la VM de test et de l'enregistrement de la sortie finale.

Étapes suivantes