Programa compilaciones de imágenes personalizadas del SO

Puedes programar ejecuciones recurrentes de Image Builder con Cloud Scheduler y los activadores de Cloud Build. Cuando programas compilaciones de imágenes personalizadas del SO, Cloud Build actualiza automáticamente tus imágenes de referencia con parches del sistema operativo y políticas de seguridad de la organización sin necesidad de intervención manual.

Antes de comenzar

  • Completa los pasos de configuración del entorno en Prepara tu entorno.
  • Si aún no lo hiciste, configura la autenticación. La autenticación verifica tu identidad para acceder a los servicios y las APIs de Cloud de Confiance by S3NS . Para ejecutar código o muestras desde un entorno de desarrollo local, puedes autenticarte en Compute Engine seleccionando una de las siguientes opciones:

    Selecciona la pestaña que corresponda a la forma en que planeas usar las muestras de esta página:

    gcloud

    1. Instala Google Cloud CLI y, luego, accede a gcloud CLI con tu identidad federada. Después de acceder, inicializa Google Cloud CLI con el siguiente comando:

      gcloud init
  • Configura una región y una zona predeterminadas.
  • Go

    Para usar las muestras de Go de esta página en un entorno de desarrollo local, instala e inicializa la gcloud CLI y, luego, configura las credenciales predeterminadas de la aplicación con tus credenciales de usuario.

    1. Instala Google Cloud CLI.

    2. Configura gcloud CLI para usar tu identidad federada.

      Para obtener más información, consulta Accede a la gcloud CLI con tu identidad federada.

    3. Crea credenciales de autenticación locales para tu cuenta de usuario:

      gcloud auth application-default login

      Si se devuelve un error de autenticación y usas un proveedor de identidad (IdP) externo, confirma que accediste a la gcloud CLI con tu identidad federada.

    Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

Roles obligatorios

Para obtener los permisos que necesitas para crear activadores y programar compilaciones, pídele a tu administrador que te otorgue los siguientes roles de IAM en tu proyecto:

Para obtener más información sobre cómo otorgar roles, consulta Administra el acceso a proyectos, carpetas y organizaciones.

También puedes obtener los permisos necesarios a través de roles personalizados o cualquier otro rol predefinido.

Crea una canalización de compilación programada

Puedes configurar compilaciones de imágenes recurrentes con gcloud CLI o los SDKs de cliente de Go:

gcloud

Para programar compilaciones recurrentes de imagen de SO con gcloud CLI, completa los siguientes pasos:

  1. Ejecuta el comando gcloud builds triggers create manual para crear un activador manual de Cloud Build que especifique tu archivo cloudbuild.yaml de destino. Como Cloud Scheduler invoca este activador de forma manual en lugar de hacerlo en eventos del repositorio, no especifiques filtros de solicitudes de extracción o envío:

    gcloud builds triggers create manual \
        --name="TRIGGER_NAME" \
        --region=REGION \
        --build-config=CLOUDBUILD_YAML_PATH \
        --project=PROJECT_ID
    
  2. Para recuperar el ID único (trigger_id) generado para tu nuevo activador, ejecuta el comando gcloud builds triggers describe:

    gcloud builds triggers describe TRIGGER_NAME \
        --region=REGION \
        --format="value(id)" \
        --project=PROJECT_ID
    
  3. Para crear un trabajo de Cloud Scheduler que envíe una solicitud HTTP POST a la API de Cloud Build (projects.locations.triggers.run) según el programa cron de Unix que necesites, por ejemplo, semanalmente los lunes a las 6 a.m. con '0 6 * * 1', ejecuta el comando 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
    

Reemplaza lo siguiente:

  • TRIGGER_NAME: Es el nombre de tu activador de compilación manual de Cloud Build, por ejemplo, weekly-ubuntu-builder.
  • REGION: Es la región en la que se creará el activador, por ejemplo, us-central1.
  • CLOUDBUILD_YAML_PATH: Es la ruta de acceso a tu archivo cloudbuild.yaml en tu directorio local, por ejemplo, cloudbuild.yaml.
  • PROJECT_ID: el ID de tu proyecto
  • JOB_NAME: Es el nombre de tu trabajo cron de Cloud Scheduler, por ejemplo, weekly-custom-os-patching.
  • CRON_SCHEDULE: Es la expresión de programación cron de Unix, por ejemplo, '0 6 * * 1'.
  • TRIGGER_ID: Es el ID único (trigger_id) generado para tu activador.
  • SERVICE_ACCOUNT_EMAIL: Es la dirección de correo electrónico de la cuenta de servicio que autoriza a Cloud Scheduler a invocar el activador. Esta cuenta de servicio requiere permisos roles/cloudbuild.builds.editor.

Go

Puedes programar canalizaciones de forma programática dentro de tus aplicaciones empresariales con las Cloud de Confiance bibliotecas cliente de la API de Go (cloudbuild/v1 y cloudscheduler/v1).

En el siguiente ejemplo completo de Go, se muestra cómo analizar las marcas de línea de comandos y construir la solicitud Build a partir de la definición de imagebuilder.yaml. Luego, el programa registra un activador manual de Cloud Build y aprovisiona un trabajo de Cloud Scheduler cuando proporcionas la marca --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
}

Ejecuta la aplicación de Go programada

Para ejecutar el objeto binario de Go compilado y programar la ejecución recurrente todos los lunes a las 6:00 a.m., ejecuta el siguiente comando:

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'

¿Qué sigue?