תזמון של יצירת תמונות של מערכת הפעלה בהתאמה אישית

אפשר לתזמן הפעלות חוזרות של Image Builder באמצעות Cloud Scheduler וטריגרים של Cloud Build. כשמתזמנים יצירה של תמונות מערכת הפעלה מותאמות אישית, Cloud Build מעדכן אוטומטית את תמונות הבסיס עם תיקוני מערכת הפעלה ומדיניות אבטחה ארגונית, בלי שנדרשת התערבות ידנית.

לפני שמתחילים

  • משלימים את השלבים להגדרת הסביבה במאמר הכנת הסביבה.
  • אם עדיין לא עשיתם את זה, תצטרכו להגדיר אימות. אימות הוא תהליך שבו מאמתים את הזהות שלכם כדי לקבל גישה לממשקי API ולשירותים של Google Cloud . כדי להריץ קוד או דוגמאות מסביבת פיתוח מקומית, אפשר לבצע אימות ל-Compute Engine באחת מהדרכים הבאות:

    צריך לבחור את הכרטיסייה הרלוונטית לאופן שבו תכננתם להשתמש בדוגמאות בדף הזה:

    gcloud

    1. התקינו את ה-CLI של Google Cloud. אחר כך, אתחלו את ה-CLI של Google Cloud באמצעות הפקודה הבאה:

      gcloud init

      אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

  • הגדרת אזור ותחום כברירת מחדל
  • המשך

    כדי להשתמש בדוגמאות של Go שבדף הזה בסביבת פיתוח מקומית, מתקינים ומפעילים את ה-CLI של gcloud, ואז מגדירים את Application Default Credentials באמצעות פרטי הכניסה של המשתמש.

    1. התקינו את ה-CLI של Google Cloud.

    2. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

    3. אם אתם משתמשים במעטפת מקומית, אתם צריכים ליצור פרטי כניסה לאימות מקומי עבור חשבון המשתמש:

      gcloud auth application-default login

      אם אתם משתמשים ב-Cloud Shell, אין צורך לבצע את הפעולה הזו.

      אם מוחזרת שגיאת אימות ואתם משתמשים בספק זהויות חיצוני (IdP), ודאו ש נכנסתם ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

    מידע נוסף זמין במאמר הגדרת אימות לסביבת פיתוח מקומית.

התפקידים הנדרשים

כדי לקבל את ההרשאות שדרושות ליצירת טריגרים ולתזמון של בנייה, צריך לבקש מהאדמין להקצות לכם את תפקידי ה-IAM הבאים בפרויקט:

להסבר על מתן תפקידים, ראו איך מנהלים את הגישה ברמת הפרויקט, התיקייה והארגון.

יכול להיות שאפשר לקבל את ההרשאות הנדרשות גם באמצעות תפקידים בהתאמה אישית או תפקידים מוגדרים מראש.

יצירת פייפליין מתוזמן של בנייה

אפשר להגדיר יצירה חוזרת של אימג'ים באמצעות ה-CLI של gcloud או ערכות ה-SDK של לקוח Go:

gcloud

כדי לתזמן בנייה חוזרת של תמונות מערכת הפעלה באמצעות ה-CLI של gcloud:

  1. כדי ליצור טריגר ידני של Cloud Build שמציין את קובץ cloudbuild.yaml היעד, מריצים את הפקודה gcloud builds triggers create manual. מכיוון ש-Cloud Scheduler מפעיל את הטריגר הזה באופן ידני ולא על אירועים במאגר, אל תציינו מסננים של בקשות push או בקשות משיכה (pull request):

    gcloud builds triggers create manual \
        --name="TRIGGER_NAME" \
        --region=REGION \
        --build-config=CLOUDBUILD_YAML_PATH \
        --project=PROJECT_ID
    
  2. כדי לאחזר את המזהה הייחודי (trigger_id) שנוצר לטריגר החדש, מריצים את הפקודה gcloud builds triggers describe:

    gcloud builds triggers describe TRIGGER_NAME \
        --region=REGION \
        --format="value(id)" \
        --project=PROJECT_ID
    
  3. כדי ליצור משימה ב-Cloud Scheduler ששולחת בקשת HTTP POSTל-Cloud Build API‏ (projects.locations.triggers.run) לפי לוח הזמנים הנדרש ב-Unix cron, למשל, אחת לשבוע בימי שני בשעה 6:00 בבוקר באמצעות '0 6 * * 1', מריצים את הפקודה 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: מזהה הפרויקט.
  • JOB_NAME: השם של משימת ה-cron ב-Cloud Scheduler, לדוגמה, weekly-custom-os-patching.
  • CRON_SCHEDULE: ביטוי cron של unix, לדוגמה, '0 6 * * 1'.
  • TRIGGER_ID: המזהה הייחודי (trigger_id) שנוצר לטריגר.
  • SERVICE_ACCOUNT_EMAIL: כתובת האימייל בחשבון השירות שמאשרת ל-Cloud Scheduler להפעיל את הטריגר. לחשבון השירות הזה נדרשות הרשאות roles/cloudbuild.builds.editor.

המשך

אפשר לתזמן צינורות באופן פרוגרמטי בתוך אפליקציות ארגוניות באמצעות Google Cloud ספריות הלקוח של Go API (cloudbuild/v1 ו-cloudscheduler/v1).

בדוגמה המלאה הבאה של Go מוסבר איך לנתח את האפשרויות של שורת הפקודה וליצור את בקשת Build מההגדרה של imagebuilder.yaml. לאחר מכן, התוכנית רושמת טריגר ידני של Cloud Build ומקצה משימה של Cloud Scheduler כשמספקים את הדגל --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
}

הפעלת אפליקציית 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'

המאמרים הבאים