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 des déclencheurs Cloud Build. Lorsque vous planifiez des compilations d'images d'OS personnalisées, Cloud Build met automatiquement à jour vos images de référence avec des correctifs du système d'exploitation et des règles de sécurité de l'organisation, sans nécessiter d'intervention manuelle.

Avant de commencer

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 gcloud CLI ou des SDK clients Go :

gcloud

Pour planifier des compilations d'images OS récurrentes à l'aide de gcloud CLI, procédez comme suit :

  1. Créez un déclencheur de compilation Cloud Build manuel qui spécifie votre fichier cloudbuild.yaml cible en exécutant la commande gcloud builds triggers create manual. Étant donné que Cloud Scheduler appelle ce déclencheur manuellement plutôt que sur des é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 commande gcloud builds triggers describe :

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

    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 fichier cloudbuild.yaml dans votre répertoire local, par exemple cloudbuild.yaml.
  • PROJECT_ID : ID de votre 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 : identifiant 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 programmatique dans vos applications d'entreprise à l'aide des bibliothèques clientes de l'API  Cloud de Confiance 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'

Étapes suivantes