Programmer la création d'images d'OS personnalisées

Vous pouvez planifier des exécutions récurrentes d'Image Builder à l'aide de Cloud Scheduler et de déclencheurs Cloud Build. Lorsque vous planifiez des compilations d'images de système d'exploitation personnalisées, Cloud Build met automatiquement à jour vos images de référence avec des correctifs de système d'exploitation et des stratégies de sécurité organisationnelles sans nécessiter d'intervention manuelle.

Avant de commencer

  • Suivez les étapes de configuration de l'environnement décrites dans Préparer votre environnement.
  • Si ce n'est pas déjà fait, configurez l'authentification. L'authentification permet de valider votre identité pour accéder aux Google Cloud services et aux API. Pour exécuter du code ou des exemples depuis un environnement de développement local, vous pouvez vous authentifier auprès de Compute Engine en sélectionnant l'une des options suivantes :

    Sélectionnez l'onglet correspondant à la façon dont vous prévoyez d'utiliser les exemples de cette page :

    gcloud

    1. Installez la Google Cloud CLI. Une fois que la Google Cloud CLI est installée, initialisezla en exécutant la commande suivante :

      gcloud init

      Si vous utilisez un fournisseur d'identité (IdP) externe, vous devez d'abord vous connecter à la gcloud CLI avec votre identité fédérée.

  • Définissez une région et une zone par défaut.
  • Go

    Pour utiliser les exemples Go de cette page dans un environnement de développement local, installez et initialisez la gcloud CLI, puis configurez le service Identifiants par défaut de l'application avec vos identifiants utilisateur.

    1. Installez la Google Cloud CLI.

    2. Si vous utilisez un fournisseur d'identité (IdP) externe, vous devez d'abord vous connecter à la gcloud CLI avec votre identité fédérée.

    3. Si vous utilisez un shell local, créez des identifiants d'authentification locaux pour votre compte utilisateur : Créer des identifiants d'authentification locaux pour votre compte utilisateur :

      gcloud auth application-default login

      Vous n'avez pas besoin de le faire si vous utilisez Cloud Shell.

      Si une erreur d'authentification est renvoyée et que vous utilisez un fournisseur d'identité (IdP) externe, vérifiez que vous vous êtes connecté à la gcloud CLI avec votre identité fédérée.

    Pour en savoir plus, consultez Configurer l'authentification pour un environnement de développement local.

Rôles requis

Pour obtenir les autorisations nécessaires pour créer des déclencheurs et planifier des compilations, 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éer un pipeline de compilation planifié

Vous pouvez configurer des compilations d'images récurrentes à l'aide de la gcloud CLI ou des SDK clients Go :

gcloud

Pour planifier des compilations d'images de système d'exploitation récurrentes à l'aide de la gcloud CLI, procédez comme suit :

  1. Créez un déclencheur de compilation Cloud Build manuel qui spécifie votre fichier cible cloudbuild.yaml en exécutant la commande gcloud builds triggers create manual. Étant donné que Cloud Scheduler appelle ce déclencheur manuellement plutôt que lors d'événements de dépôt, ne spécifiez pas de filtres de requête push ou de demande d'extraction :

    gcloud builds triggers create manual \
        --name="TRIGGER_NAME" \
        --region=REGION \
        --build-config=CLOUDBUILD_YAML_PATH \
        --project=PROJECT_ID
    
  2. Récupérez l'ID unique (trigger_id) généré pour votre nouveau déclencheur en exécutant la gcloud builds triggers describe commande :

    gcloud builds triggers describe TRIGGER_NAME \
        --region=REGION \
        --format="value(id)" \
        --project=PROJECT_ID
    
  3. Pour créer une tâche Cloud Scheduler qui envoie une HTTP POST requête à l'API Cloud Build (projects.locations.triggers.run) selon la planification cron Unix requise (par exemple, tous les lundis à 6h00 avec '0 6 * * 1'), exécutez la gcloud scheduler jobs create http commande :

    gcloud scheduler jobs create http JOB_NAME \
        --schedule="CRON_SCHEDULE" \
        --uri="https://cloudbuild.googleapis.com/v1/projects/PROJECT_ID/locations/REGION/triggers/TRIGGER_ID:run" \
        --message-body="{}" \
        --oauth-service-account-email="SERVICE_ACCOUNT_EMAIL" \
        --location=REGION \
        --project=PROJECT_ID
    

Remplacez les éléments suivants :

  • TRIGGER_NAME: nom de votre déclencheur de compilation Cloud Build manuel, par exemple, weekly-ubuntu-builder.
  • REGION: région dans laquelle créer le déclencheur, par exemple, us-central1.
  • CLOUDBUILD_YAML_PATH : chemin d'accès à votre cloudbuild.yaml fichier dans votre répertoire local, par exemple, cloudbuild.yaml.
  • PROJECT_ID : ID du projet.
  • JOB_NAME: nom de votre job Cron Cloud Scheduler, par exemple, weekly-custom-os-patching.
  • CRON_SCHEDULE : expression de planification cron Unix, par exemple, '0 6 * * 1'.
  • TRIGGER_ID: ID unique (trigger_id) généré pour votre déclencheur.
  • SERVICE_ACCOUNT_EMAIL: adresse e-mail du compte de service qui autorise Cloud Scheduler à appeler le déclencheur. Ce compte de service nécessite des autorisations roles/cloudbuild.builds.editor.

Go

Vous pouvez planifier des pipelines de manière automatisée dans vos applications d'entreprise à l'aide des Google Cloud bibliothèques clientes de l'API Go (cloudbuild/v1 et cloudscheduler/v1).

L'exemple Go complet suivant montre comment analyser les indicateurs de ligne de commande et construire la requête Build à partir de votre définition imagebuilder.yaml. Le programme enregistre ensuite un déclencheur Cloud Build manuel et provisionne une tâche Cloud Scheduler lorsque vous fournissez l'indicateur --cron_schedule.

// Program main demonstrates how to submit or schedule a Image Builder pipeline using Go SDKs.
package main

import (
    "context"
    "flag"
    "fmt"
    "os"

    "google.golang.org/api/cloudbuild/v1"
    "google.golang.org/api/cloudscheduler/v1"
)

var (
    projectID        = flag.String("project_id", "", "The target Project ID")
    region           = flag.String("region", "us-central1", "Region of resources")
    gcsWorkdir       = flag.String("gcs_workdir", "", "The storage workspace directory URI, e.g. gs://my-bucket/workdir/")
    serviceAccount   = flag.String("service_account", "", "Service account email to run the worker VM")
    imageBuilderYAML = flag.String("config_path", "imagebuilder.yaml", "Path to the imagebuilder.yaml configuration file")
    arRepositoryID   = flag.String("ar_repo_id", "os-images", "Name of the target generic Artifact Registry repository")
    arPackageName    = flag.String("ar_package_name", "custom-os", "Package identifier for OS images")
    cronSchedule     = flag.String("cron_schedule", "", "Optional cron schedule to run this build periodically (e.g. '0 6 * * 1')")
)

func main() {
    flag.Parse()

    if *projectID == "" || *serviceAccount == "" || *gcsWorkdir == "" {
        fmt.Fprintln(os.Stderr, "Error: --project_id, --gcs_workdir, and --service_account are required flags")
        flag.Usage()
        os.Exit(1)
    }

    ctx := context.Background()

    if *cronSchedule != "" {
        fmt.Printf("Scheduling build with cron schedule: %s\n", *cronSchedule)
        if err := scheduleBuild(ctx); err != nil {
            fmt.Fprintf(os.Stderr, "Failed to schedule Cloud Build: %v\n", err)
            os.Exit(1)
        }
    } else {
        fmt.Println("Submitting build request immediately to Cloud Build API...")
        if err := triggerBuild(ctx); err != nil {
            fmt.Fprintf(os.Stderr, "Failed to trigger Cloud Build: %v\n", err)
            os.Exit(1)
        }
    }
}

func createBuildRequest() *cloudbuild.Build {
    subs := map[string]string{
        "_GCS_WORKDIR":                    *gcsWorkdir,
        "_SERVICE_ACCOUNT":                fmt.Sprintf("projects/%s/serviceAccounts/%s", *projectID, *serviceAccount),
        "_IMAGE_OUTPUT_PATH":              "image-builder/binaryOut",
        "_IMAGE_BUILDER_CONFIG_PATH":      *imageBuilderYAML,
        "_ARTIFACT_REGISTRY_RESOURCE_URI": fmt.Sprintf("projects/%s/locations/%s/repositories/%s/packages/%s/versions/v${BUILD_ID}", *projectID, *region, *arRepositoryID, *arPackageName),
    }

    return &cloudbuild.Build{
        Steps: []*cloudbuild.BuildStep{
            {
                Name:   "us-central1-docker.pkg.dev/image-builder-official/release/builder:stable",
                Script: "#!/usr/bin/env bash\n/build",
                Id:     "imagebuilder-customize",
            },
            {
                Name:   "us-central1-docker.pkg.dev/image-builder-official/release/validator:stable",
                Script: "#!/usr/bin/env bash\n/validate",
                Id:     "imagebuilder-validate",
            },
            {
                Name:   "us-central1-docker.pkg.dev/image-builder-official/release/builder:stable",
                Script: "#!/usr/bin/env bash\n/publish",
                Id:     "imagebuilder-publish",
            },
        },
        Substitutions: subs,
        Options: &cloudbuild.BuildOptions{
            AutomapSubstitutions:  true,
            RequestedVerifyOption: "VERIFIED",
            SubstitutionOption:    "ALLOW_LOOSE",
            DynamicSubstitutions:  true,
        },
        Timeout: "3600s",
    }
}

func triggerBuild(ctx context.Context) error {
    cbService, err := cloudbuild.NewService(ctx)
    if err != nil {
        return err
    }
    build := createBuildRequest()
    op, err := cbService.Projects.Locations.Builds.Create(fmt.Sprintf("projects/%s/locations/%s", *projectID, *region), build).Do()
    if err != nil {
        return err
    }
    fmt.Printf("Build submitted: %s\n", op.Name)
    return nil
}

func scheduleBuild(ctx context.Context) error {
    cbService, err := cloudbuild.NewService(ctx)
    if err != nil {
        return err
    }
    csService, err := cloudscheduler.NewService(ctx)
    if err != nil {
        return err
    }

    // 1. Create a manual Cloud Build trigger
    trigger := &cloudbuild.BuildTrigger{
        Build:       createBuildRequest(),
        Name:        "scheduled-image-builder-trigger",
        Description: "Manual trigger invoked periodically via Cloud Scheduler for Image Builder",
    }
    createdTrigger, err := cbService.Projects.Locations.Triggers.Create(fmt.Sprintf("projects/%s/locations/%s", *projectID, *region), trigger).Do()
    if err != nil {
        return fmt.Errorf("failed creating trigger: %w", err)
    }
    fmt.Printf("Created Cloud Build trigger with ID: %s\n", createdTrigger.Id)

    // 2. Create the Cloud Scheduler job
    targetURI := fmt.Sprintf("https://cloudbuild.googleapis.com/v1/projects/%s/locations/%s/triggers/%s:run", *projectID, *region, createdTrigger.Id)
    job := &cloudscheduler.Job{
        Name:        fmt.Sprintf("projects/%s/locations/%s/jobs/weekly-image-builder-job", *projectID, *region),
        Schedule:    *cronSchedule,
        Description: "Scheduled job to run custom OS Image Builder pipeline",
        HttpTarget: &cloudscheduler.HttpTarget{
            Uri:        targetURI,
            HttpMethod: "POST",
            OauthToken: &cloudscheduler.OAuthToken{
                ServiceAccountEmail: *serviceAccount,
            },
        },
    }

    createdJob, err := csService.Projects.Locations.Jobs.Create(fmt.Sprintf("projects/%s/locations/%s", *projectID, *region), job).Do()
    if err != nil {
        return fmt.Errorf("failed creating scheduler job: %w", err)
    }
    fmt.Printf("Successfully scheduled job: %s\n", createdJob.Name)
    return nil
}

Exécuter l'application Go planifiée

Pour exécuter le binaire Go compilé et planifier une exécution récurrente tous les lundis à 6h00, exécutez la commande suivante :

go run main.go \
  --project_id PROJECT_ID \
  --region us-central1 \
  --gcs_workdir gs://STAGING_BUCKET/workdir/ \
  --service_account SERVICE_ACCOUNT_EMAIL \
  --config_path gs://STAGING_BUCKET/imagebuilder.yaml \
  --cron_schedule '0 6 * * 1'

Étape suivante