您可以使用 Cloud Scheduler 和 Cloud Build 触发器来安排定期运行 Image Builder。在安排自定义操作系统映像构建时,Cloud Build 会自动使用操作系统补丁和组织安全政策更新基准映像,而无需手动干预。
准备工作
- 完成 准备环境中的环境设置步骤。
-
如果您尚未设置身份验证,请进行设置。
身份验证用于验证您的身份,以便访问 Google Cloud 服务和 API。如需从本地开发环境运行
代码或示例,您可以通过选择以下选项之一向
Compute Engine 进行身份验证:
{1
选择标签页以了解您打算如何使用本页面上的示例:
gcloud
-
安装 Google Cloud CLI。 安装完成后, 初始化 Google Cloud CLI,方法是运行以下命令:
gcloud init如果您使用的是外部身份提供方 (IdP),则必须先 使用联合身份登录 gcloud CLI。
-
- 设置默认区域和可用区。
-
安装 Google Cloud CLI。
-
如果您使用的是外部身份提供方 (IdP),则必须先 使用联合身份登录 gcloud CLI。
-
如果您使用的是本地 shell,请为您的用户 账号创建本地身份验证凭证:
gcloud auth application-default login
如果您使用的是 Cloud Shell,则无需执行此操作。
如果返回了身份验证错误,并且您使用的是外部身份提供方 (IdP),请确认您已 使用联合身份登录 gcloud CLI。
Go
如需在本地开发环境中使用本页面上的 Go 示例,请安装并 初始化 gcloud CLI,然后使用您的用户凭证设置应用默认凭据。
如需了解详情,请参阅 为本地开发环境设置身份验证。
所需的角色
如需获得创建触发器和安排构建所需的权限,请让您的管理员为您授予项目的以下 IAM 角色:
- Cloud Build Editor (
roles/cloudbuild.builds.editor) - Cloud Scheduler Job Runner (
roles/cloudscheduler.jobRunner) 或 Cloud Scheduler Admin (roles/cloudscheduler.admin) - Service Account User (
roles/iam.serviceAccountUser)
如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。
您也可以通过自定义 角色或其他预定义 角色来获取所需的权限。
创建已安排的构建流水线
您可以使用 gcloud CLI 或 Go 客户端 SDK 配置定期映像构建:
gcloud
如需使用 gcloud CLI 安排定期操作系统映像构建,请完成以下步骤:
运行
gcloud builds triggers create manual命令,创建一个手动 Cloud Build 构建触发器,以指定目标cloudbuild.yaml文件。由于 Cloud Scheduler 是手动调用此触发器 而不是在发生代码库事件时调用,因此请勿指定推送或拉取请求 过滤条件:gcloud builds triggers create manual \ --name="TRIGGER_NAME" \ --region=REGION \ --build-config=CLOUDBUILD_YAML_PATH \ --project=PROJECT_ID运行
gcloud builds triggers describe命令,检索为新触发器生成的唯一 ID (trigger_id) :gcloud builds triggers describe TRIGGER_NAME \ --region=REGION \ --format="value(id)" \ --project=PROJECT_ID如需创建 Cloud Scheduler 作业,以根据您所需的 Unix Cron 时间表(例如每周一上午 6:00 使用
'0 6 * * 1')向 Cloud Build API (projects.locations.triggers.run) 发送HTTP POST请求,请运行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
替换以下内容:
TRIGGER_NAME:手动 Cloud Build 构建触发器的名称,例如weekly-ubuntu-builder。REGION:用于创建触发器的区域,例如us-central1。CLOUDBUILD_YAML_PATH:本地目录中cloudbuild.yaml文件的路径,例如cloudbuild.yaml。PROJECT_ID:您的项目 ID。JOB_NAME:Cloud Scheduler Cron 作业的名称,例如weekly-custom-os-patching。CRON_SCHEDULE:Unix Cron 时间表表达式, 例如'0 6 * * 1'。TRIGGER_ID:为触发器生成的唯一 ID (trigger_id)。SERVICE_ACCOUNT_EMAIL:授权 Cloud Scheduler 调用触发器的服务帐号电子邮件地址。此服务帐号需要具有roles/cloudbuild.builds.editor权限。
Go
您可以使用 Google Cloud Go API 客户端库
(cloudbuild/v1 和 cloudscheduler/v1)以编程方式在企业
应用中安排流水线。
以下完整的 Go 示例演示了如何解析命令行标志,以及如何根据 imagebuilder.yaml 定义构建 Build 请求。然后,当您提供 --cron_schedule 标志时,该程序会注册手动 Cloud Build 触发器并预配 Cloud Scheduler 作业。
// 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
}
执行已安排的 Go 应用
如需运行已编译的 Go 二进制文件并安排每周一 上午 6:00 定期执行,请运行以下命令:
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'
后续步骤
- 了解如何使用 Terraform 在 Git 推送、标记或合并事件发生时自动执行构建 。
- 查看自定义配方架构 以定义软件包和验证程序。