Programa compilaciones de imágenes personalizadas del SO

Puedes programar ejecuciones recurrentes de Image Builder con los activadores de Cloud Scheduler y Cloud Build. Cuando programas compilaciones de imágenes de SO personalizadas, 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

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 la biblioteca cliente de Go:

gcloud

Para programar compilaciones de imagen de SO recurrentes 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. Debido a que Cloud Scheduler invoca este activador de forma manual, en lugar de hacerlo en eventos del repositorio, no especifiques filtros de envío ni de solicitud de extracción:

    gcloud builds triggers create manual \
        --name="TRIGGER_NAME" \
        --region=REGION \
        --build-config=CLOUDBUILD_YAML_PATH \
        --project=PROJECT_ID
    
  2. Ejecuta el comando gcloud builds triggers describe para recuperar el ID único (trigger_id) que se generó para tu activador nuevo:

    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 la programación de cron de Unix que necesites, por ejemplo, semanalmente los lunes a las 6:00 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 del activador de compilación manual de Cloud Build, por ejemplo, weekly-ubuntu-builder.
  • REGION: 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: Es el ID del proyecto.
  • JOB_NAME: Es el nombre del 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 el correo electrónico de la cuenta de servicio que autoriza a Cloud Scheduler a invocar el activador. Esta cuenta de servicio requiere permisos de roles/cloudbuild.builds.editor.

Go

Para programar canalizaciones de manera programática con las bibliotecas cliente de Go de Cloud Build y Cloud Scheduler, usa la función scheduleBuild.

Antes de ejecutar esta muestra, completa los siguientes requisitos previos:

  • Sube tu archivo de configuración imagebuilder.yaml a tu bucket de Cloud Storage, por ejemplo, gs://STAGING_BUCKET/imagebuilder.yaml.
  • Otorga a la cuenta de servicio de programación cloud-build-trigger-scheduler@PROJECT_ID.s3ns.iam.gserviceaccount.com, el rol de editor de Cloud Build (roles/cloudbuild.builds.editor) en tu proyecto y el rol de usuario de cuenta de servicio (roles/iam.serviceAccountUser) en la cuenta de servicio que ejecuta la compilación. Para obtener más información, consulta Programa compilaciones en la documentación de Cloud Build.
import (
	"context"
	"fmt"
	"io"

	scheduler "cloud.google.com/go/scheduler/apiv1"
	"cloud.google.com/go/scheduler/apiv1/schedulerpb"
	"google.golang.org/api/cloudbuild/v1"
)

// scheduleBuild creates a manual Cloud Build trigger that runs an Image
// Builder pipeline, and a Cloud Scheduler job that runs the trigger every day.
//
// Before you run this sample, upload your imagebuilder.yaml configuration file
// to Cloud Storage, create a generic Artifact Registry repository, and create a
// service account to run the build.
//
// The job runs the trigger as the default scheduling service account,
// cloud-build-trigger-scheduler@PROJECT_ID.iam.gserviceaccount.com. Grant that
// service account the Cloud Build Editor role, and the Service Account User
// role on the service account that runs the build. For details, see
// https://cloud.google.com/build/docs/schedule-builds.
func scheduleBuild(w io.Writer, projectID, gcsWorkdir, configPath, serviceAccount string) error {
	// projectID := "my-project-id"
	region := "us-central1"
	// gcsWorkdir := "gs://my-bucket/workdir/"
	// configPath := "gs://my-bucket/imagebuilder.yaml"
	repository := "vm-images"
	packageName := "custom-os-images"
	// serviceAccount := "image-builder@my-project-id.iam.gserviceaccount.com"
	triggerName := "scheduled-image-builder"
	jobName := "scheduled-image-builder-job"
	schedule := "0 9 * * *"

	ctx := context.Background()
	cbService, err := cloudbuild.NewService(ctx)
	if err != nil {
		return fmt.Errorf("cloudbuild.NewService: %w", err)
	}
	schedulerClient, err := scheduler.NewCloudSchedulerClient(ctx)
	if err != nil {
		return fmt.Errorf("scheduler.NewCloudSchedulerClient: %w", err)
	}
	defer schedulerClient.Close()

	parent := fmt.Sprintf("projects/%s/locations/%s", projectID, region)
	sa := fmt.Sprintf("projects/%s/serviceAccounts/%s", projectID, serviceAccount)

	trigger := &cloudbuild.BuildTrigger{
		Name:        triggerName,
		Description: "Manual trigger for scheduled VM image builds",
		EventType:   "MANUAL",
		Build: &cloudbuild.Build{
			ServiceAccount: sa,
			// AutomapSubstitutions exposes these substitutions to every step as
			// environment variables. See
			// https://cloud.google.com/build/docs/configuring-builds/substitute-variable-values#mapping_substitutions_to_environment_variables
			Substitutions: map[string]string{
				"_GCS_WORKDIR":               gcsWorkdir,
				"_SERVICE_ACCOUNT":           sa,
				"_IMAGE_OUTPUT_PATH":         "image-builder/binaryOut",
				"_IMAGE_BUILDER_CONFIG_PATH": configPath,
				"_ARTIFACT_REGISTRY_RESOURCE_URI": fmt.Sprintf(
					"projects/%s/locations/%s/repositories/%s/packages/%s/versions/v${BUILD_ID}",
					projectID, region, repository, packageName,
				),
			},
			Steps: []*cloudbuild.BuildStep{
				{
					Id:     "imagebuilder-customize",
					Name:   "us-docker.pkg.dev/image-builder-official/release/builder:stable",
					Script: "#!/usr/bin/env bash\n/build",
					Results: []*cloudbuild.StepResult{
						{
							Name:            "base_image",
							AttestationType: "https://cloudbuild.googleapis.com/attestations/build_content_restrictions",
						},
						{Name: "image_builder_telemetry_metrics"},
					},
				},
				{
					Id:      "imagebuilder-validate",
					Name:    "us-docker.pkg.dev/image-builder-official/release/validator:stable",
					Script:  "#!/usr/bin/env bash\n/validate",
					Results: []*cloudbuild.StepResult{{Name: "image_builder_telemetry_metrics"}},
				},
				{
					Id:      "imagebuilder-publish",
					Name:    "us-docker.pkg.dev/image-builder-official/release/builder:stable",
					Script:  "#!/usr/bin/env bash\n/publish",
					Results: []*cloudbuild.StepResult{{Name: "image_builder_telemetry_metrics"}},
				},
			},
			Options: &cloudbuild.BuildOptions{
				AutomapSubstitutions:  true,
				DynamicSubstitutions:  true,
				RequestedVerifyOption: "VERIFIED",
				SubstitutionOption:    "ALLOW_LOOSE",
				// Builds that run as a user-specified service account must set a
				// logging option.
				Logging: "CLOUD_LOGGING_ONLY",
			},
			Artifacts: &cloudbuild.Artifacts{
				GenericArtifacts: []*cloudbuild.GenericArtifact{
					{
						Folder:       "${_IMAGE_OUTPUT_PATH}",
						RegistryPath: "${_ARTIFACT_REGISTRY_RESOURCE_URI}",
					},
				},
			},
			Timeout: "3600s",
		},
	}
	createdTrigger, err := cbService.Projects.Locations.Triggers.Create(parent, trigger).
		Context(ctx).Do()
	if err != nil {
		return fmt.Errorf("Triggers.Create: %w", err)
	}
	fmt.Fprintf(w, "Created trigger %s (ID: %s)\n", createdTrigger.Name, createdTrigger.Id)

	schedulerSA := fmt.Sprintf(
		"cloud-build-trigger-scheduler@%s.iam.gserviceaccount.com", projectID,
	)
	job := &schedulerpb.Job{
		Name:        fmt.Sprintf("%s/jobs/%s", parent, jobName),
		Description: "Runs the Image Builder trigger on a schedule",
		Schedule:    schedule,
		Target: &schedulerpb.Job_HttpTarget{
			HttpTarget: &schedulerpb.HttpTarget{
				Uri: fmt.Sprintf(
					"https://cloudbuild.googleapis.com/v1/projects/%s/locations/%s/triggers/%s:run",
					projectID, region, createdTrigger.Id,
				),
				HttpMethod: schedulerpb.HttpMethod_POST,
				AuthorizationHeader: &schedulerpb.HttpTarget_OauthToken{
					OauthToken: &schedulerpb.OAuthToken{
						ServiceAccountEmail: schedulerSA,
					},
				},
			},
		},
	}
	req := &schedulerpb.CreateJobRequest{Parent: parent, Job: job}
	createdJob, err := schedulerClient.CreateJob(ctx, req)
	if err != nil {
		return fmt.Errorf("CreateJob: %w", err)
	}
	fmt.Fprintf(w, "Created job %s with schedule %q\n", createdJob.Name, createdJob.Schedule)
	return nil
}

¿Qué sigue?