יצירת צינור עיבוד נתונים של תמונת מערכת הפעלה בהתאמה אישית באמצעות gcloud או Terraform

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

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

  • משלימים את השלבים להגדרת הסביבה במאמר הכנת הסביבה.
  • אם אתם מתכוונים לפרוס את צינור עיבוד הנתונים באמצעות Terraform או להפוך את הבנייה לאוטומטית ממאגר, אתם יכולים לחבר את המאגר שלכם ב-GitHub, ב-GitLab או ב-Bitbucket באמצעות מאגרי Cloud Build ‏ (2nd gen) או קישורי חיבור של Developer Connect.
  • אם אתם מתכננים להשתמש ב-Terraform, אתם צריכים להתקין את Terraform CLI בגרסה 1.3 ואילך.
  • אם עדיין לא עשיתם את זה, תצטרכו להגדיר אימות. אימות הוא תהליך שבו מאמתים את הזהות שלכם כדי לקבל גישה לממשקי API ולשירותים של Google Cloud . כדי להריץ קוד או דוגמאות מסביבת פיתוח מקומית, אפשר לבצע אימות ל-Compute Engine באחת מהדרכים הבאות:

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

    gcloud

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

      gcloud init

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

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

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

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

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

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

      gcloud auth application-default login

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

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

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

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

כדי לקבל את ההרשאות שדרושות ליצירה ולשליחה של צינורות להתאמה אישית של תמונות באמצעות Google Cloud CLI או Terraform, צריך לבקש מהאדמין להקצות לכם את תפקידי ה-IAM הבאים בפרויקט:

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

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

יצירת קובצי תצורה

כדי להגדיר את הפייפליין באמצעות ה-CLI של gcloud או Terraform, צריך ליצור שני קובצי תצורה:

  • imagebuilder.yaml: הגדרת מתכון ההתאמה האישית של התמונה, כולל תמונת מערכת ההפעלה הבסיסית, הגדרות התשתית של מכונת ה-VM של העובד, פרטי הפלט של תמונת היעד ושלבי ההתאמה האישית הרציפים, כמו הפעלת סקריפטים של מעטפת, העברת קבצים או ביצוע הפעלה מחדש.
  • cloudbuild.yaml: מבצע תזמור של השלבים בתהליך ה-build ב-Cloud Build, כולל ניתוח, אימות, יצירה, בדיקה ופרסום של תמונת מערכת ההפעלה בהתאמה אישית.

יצירת קובץ הגדרות אישיות של תמונה

כדי לציין את הגדרות התמונה, יוצרים קובץ בשם imagebuilder.yaml בספרייה המקומית. רשימה מלאה של כל שדות הסכימה הנתמכים ופעולות ההתאמה האישית מופיעה במאמרים סכימת מתכוני התאמה אישית ופעולות התאמה אישית נתמכות.

קובץ imagebuilder.yaml לדוגמה שמופיע בהמשך מגדיר פייפליין שיוצר אימג' מותאם אישית של Ubuntu 22.04 LTS באמצעות worker VM e2-standard-4 באזור ובאזור הזמין שצוינו, ומבצע עדכון של חבילת מערכת.

apiVersion: imagebuilder.gcp.com/v1
kind: OSImageCustomization
metadata:
  name: customized-ubuntu-baseline
  description: "Ubuntu 22.04 LTS custom OS baseline image"
infrastructureConfig:
  machineType: e2-standard-4
  zone: ZONE
  debug: false
source:
  imageFamily: projects/ubuntu-os-cloud/global/images/family/ubuntu-2204-lts
destinations:
  - diskImage:
      name: custom-ubuntu-v1
      family: custom-ubuntu-family
      project: PROJECT_ID
      storageLocations:
        - REGION
spec:
  config:
    skipSystemTests: false
  steps:
    -   name: "System Package Update"
      action: Shell
      inputs:
        command: "apt-get update -y && apt-get upgrade -y"

מחליפים את ערכי ה-placeholder הבאים:

  • PROJECT_ID: מזהה הפרויקט.
  • REGION: מיקום האחסון של תמונת היעד, לדוגמה, us-east1 או europe-west1. חשוב לוודא שאתם עומדים בדרישות האזוריות והאזוריאליות הבאות:
    • הכלי Image Builder נתמך רק באזורים שבהם Cloud Build זמין.
    • המכונה הווירטואלית של העובד ZONE צריכה להיות ממוקמת בתוך REGION שציינתם.
    • כדי לצמצם את זמן האחזור ברשת ולמנוע חיובים על תעבורת נתונים יוצאת (egress) בין אזורים, צריך לוודא שהאזור של המכונה הווירטואלית של העובד, קטגוריית האחסון הזמני ב-Cloud Storage, מאגר Artifact Registry ומיקום האחסון של תמונת היעד נמצאים באותו אזור.
  • ZONE: אזור שנמצא בתוך REGION שצוין, למשל us-east1-b או europe-west1-b.

יצירת קובץ ה-build של כלי התזמור

יוצרים קובץ בשם cloudbuild.yaml באותה ספרייה. הקובץ הזה קורא לשלבים של קונטיינר Image Builder כדי ליצור, לאמת ולפרסם את קובץ האימג' של מערכת ההפעלה המותאמת אישית.

substitutions:
  _GCS_WORKDIR: 'gs://STAGING_BUCKET_NAME/workdir/'
  _IMAGE_BUILDER_CONFIG_PATH: 'imagebuilder.yaml'
  _SERVICE_ACCOUNT: 'projects/PROJECT_ID/serviceAccounts/SERVICE_ACCOUNT_EMAIL'
  _IMAGE_OUTPUT_PATH: 'image-builder/binaryOut'
  _ARTIFACT_REGISTRY_RESOURCE_URI: 'projects/PROJECT_ID/locations/REGION/repositories/REPOSITORY_NAME/packages/PACKAGE_NAME/versions/v${BUILD_ID}'

steps:
  #  Step 1: Parse configs and run OS customization on worker VM
  - name: 'REGION-docker.pkg.dev/image-builder-official/release/builder:stable'
    script: |
      #!/usr/bin/env bash
      /build
    id: 'imagebuilder-customize'
    results:
      - name: image_builder_telemetry_metrics
      - name: base_image
        attestationType: "https://cloudbuild.googleapis.com/attestations/build_content_restrictions"
        attestationContent: base_image

  # Step 2: Validate by running system boot checks on a test VM
  - name: 'REGION-docker.pkg.dev/image-builder-official/release/validator:stable'
    script: |
      #!/usr/bin/env bash
      /validate
    id: 'imagebuilder-validate'
    results:
      - name: image_builder_telemetry_metrics

  # Step 3: Register image in Compute Engine and upload tar files to Artifact Registry
  - name: 'REGION-docker.pkg.dev/image-builder-official/release/builder:stable'
    script: |
      #!/usr/bin/env bash
      /publish
    id: 'imagebuilder-publish'
    results:
      - name: image_builder_telemetry_metrics

options:
  automapSubstitutions: true
  requestedVerifyOption: VERIFIED
  substitutionOption: ALLOW_LOOSE
  dynamicSubstitutions: true
  logging: CLOUD_LOGGING_ONLY

artifacts:
  generic_artifacts:
    - folder: '${_IMAGE_OUTPUT_PATH}'
      registry_path: '${_ARTIFACT_REGISTRY_RESOURCE_URI}'

timeout: '3600s'

מחליפים את ערכי ה-placeholder הבאים:

  • STAGING_BUCKET_NAME: קטגוריה קיימת של Cloud Storage בפרויקט שלכם, שתשמש כסביבת עבודה זמנית להכנה. אם אין לכם קטגוריית אחסון, אתם יכולים ליצור אותה באמצעות הפקודה gcloud storage buckets create gs://STAGING_BUCKET_NAME. אם פורסים את צינור עיבוד הנתונים באמצעות Terraform, המערכת יוצרת את קטגוריית האחסון הזו באופן אוטומטי.
  • PROJECT_ID: מזהה הפרויקט ב- Google Cloud .
  • REGION: האזור Google Cloud של מאגר Artifact Registry, לדוגמה, us-east1 או europe-west1.
  • SERVICE_ACCOUNT_EMAIL: כתובת האימייל של חשבון השירות שהגדרתם לו את הרשאות ה-IAM הנדרשות.
  • REPOSITORY ו-PACKAGE: מאגר היעד ושם החבילה שנוצרו ב-Artifact Registry. כדי להגדיר את מאגר Artifact Registry, אפשר לעיין במאמר הגדרת Artifact Registry.

יצירה ושליחה של צינור עיבוד נתונים לבנייה

כדי להריץ את פייפליין התאמה אישית של התמונה, שולחים גרסת build באמצעות ה-CLI של gcloud או פורסים את הפייפליין באמצעות Terraform. בוחרים אחת מהכרטיסיות הבאות:

gcloud

כדי לפרוס ולהריץ את צינור העיבוד של התאמה אישית של קובץ אימג', מריצים את הפקודה gcloud builds submit בספרייה של הטרמינל המקומי שמכילה את שני קובצי התצורה:

gcloud builds submit . \
    --config=cloudbuild.yaml \
    --project=PROJECT_ID \
    --service-account="projects/PROJECT_ID/serviceAccounts/SERVICE_ACCOUNT_EMAIL" \
    --region=REGION

מחליפים את מה שכתוב בשדות הבאים:

  • PROJECT_ID: מזהה הפרויקט ב- Google Cloud .
  • REGION: ה Google Cloud אזור שבו יופעל ג'וב צינור ההתאמה האישית של התמונה.
  • SERVICE_ACCOUNT_EMAIL: כתובת האימייל של חשבון השירות שהוגדר עם הרשאות IAM הנדרשות.

הפקודה הזו מעלה את סביבת העבודה להתאמה אישית, רושמת את ההפעלה של Cloud Build ומפעילה את קונטיינרים של תזמור.

Terraform

כדי להקצות את התשתית שנדרשת לבנייה ולאימות אוטומטיים של תמונות מערכת הפעלה בהתאמה אישית, אפשר להשתמש ב-Terraform. ההגדרה הזו של Terraform מבצעת את המשימות הבאות:

  • הפעלת ממשקי API נדרשים Google Cloud .
  • יוצר קטגוריה ייעודית של Cloud Storage‏ (workdir_bucket) לאחסון יומנים זמניים וארטיפקטים של בנייה.
  • מגדיר טריגר לפיתוח גרסת Build של Cloud Build ‏(image_builder_trigger) שמקושר לחיבור שלכם למאגר GitHub ב-Developer Connect.

יצירת קובצי התצורה של Terraform

כדי לארגן ולפרוס את תשתית הצינור באמצעות Terraform, מבצעים את השלבים הבאים:

  1. יוצרים ספרייה ייעודית בתחנת העבודה המקומית או בסביבת CI/CD, בנפרד ממאגר האפליקציות, ועוברים אליה:

    mkdir terraform-image-builder && cd terraform-image-builder
    
  2. בספרייה הזו, יוצרים את חמשת קובצי התצורה הבאים של Terraform:

    • terraform.tfvars: הגדרת ערכים למשתנים ספציפיים לפרויקט.
    • main.tf: מקצה משאבים בפרויקט Google Cloud , כולל הפעלת ממשקי ה-API הנדרשים, יצירת מאגר זמני של Cloud Storage‏ (workdir_bucket) ופריסת טריגר לפיתוח גרסת Build‏ (image_builder_trigger).
    • outputs.tf: מגדיר ערכי פלט שמוצגים במסוף אחרי הפריסה, כמו מזהה הטריגר ושם מאגר ה-staging.
    • providers.tf: מציין את הגרסה הנדרשת של Terraform ‏ (>= 1.3) ומגדיר את ספק Google Cloud (hashicorp/google).
    • variables.tf: הגדרה של משתני קלט, ערכי ברירת מחדל וכללי אימות לפריסה.

    בוחרים אחת מהכרטיסיות הבאות כדי להציג ולהעתיק את ההגדרות של כל קובץ לספרייה המקומית:

    terraform.tfvars

    בקובץ הזה מצוינים ערכי הפרמטרים של הסביבה עבור המשתנים שהוגדרו:

    project_id                = "PROJECT_ID"
    builder_service_account   = "SERVICE_ACCOUNT_EMAIL"
    github_repo_name          = "projects/PROJECT_ID/locations/LOCATION/connections/CONNECTION/repositories/REPO_NAME"
    region                    = "REGION"
    trigger_name              = "TRIGGER_NAME"
    cloudbuild_yaml_path      = "CLOUDBUILD_YAML_PATH"
    image_builder_config_path = "RECIPE_PATH"
    gcs_lifecycle_age_days    = LIFECYCLE_DAYS
    ar_repository_id          = "REPOSITORY_NAME"
    ar_package_name           = "PACKAGE_NAME"
    

    מחליפים את ה-placeholders הבאים במשאבים הקיימים:

    • PROJECT_ID: מזהה הפרויקט הקיים ב-Google Cloud .
    • SERVICE_ACCOUNT_EMAIL: כתובת האימייל של חשבון השירות של שירות ה-Build שהוגדר בהגדרת חשבון השירות של Image Builder.
    • LOCATION, CONNECTION ו-REPO_NAME: האזור המארח של Developer Connect, שם החיבור והקישור למאגר שהוגדרו בחיבור מאגר.
    • CLOUDBUILD_YAML_PATH: הנתיב היחסי לקובץ cloudbuild.yaml בספרייה המקומית. אין צורך להכניס את cloudbuild.yaml למאגר Git.
    • RECIPE_PATH: הנתיב היחסי לקובץ המתכון להתאמה אישית imagebuilder.yaml שנשמר במאגר Git.
    • REPOSITORY_NAME: מאגר Artifact Registry גנרי קיים שנוצר בהגדרת Artifact Registry.

    מחליפים את ה-placeholders הבאים במשאבים שנוצרו ב-Terraform:

    • REGION: אזור היעד שבו Terraform מקצה את קטגוריית הביניים של Cloud Storage ואת הטריגר של Cloud Build Google Cloud, לדוגמה, us-central1.
    • TRIGGER_NAME: השם של טריגר חדש למאגר Cloud Build שנוצר על ידי Terraform, לדוגמה, git-push-os-builder.
    • LIFECYCLE_DAYS: תקופת השמירה בימים לפני שמחיקה אוטומטית של ארטיפקטים זמניים בקטגוריית הביניים של Cloud Storage שנוצרה על ידי Terraform, לדוגמה, 30.
    • PACKAGE_NAME: השם שרוצים ש-Terraform ישתמש בו לחבילה שנוצרה במאגר Artifact Registry. החבילה הזו מאחסנת גרסאות של קובץ אימג' של מערכת הפעלה שפורסמו, לדוגמה, ubuntu-custom.

    main.tf

    בקובץ הזה מוגדרים משאבי התשתית ומקורות הנתונים של הפריסה:

    # Main resource configurations for Image Builder.
    # 1. Enable Required APIs
    resource "google_project_service" "apis" {
      for_each = toset([
        "compute.googleapis.com",
        "cloudbuild.googleapis.com",
        "artifactregistry.googleapis.com",
        "serviceusage.googleapis.com",
        "cloudresourcemanager.googleapis.com",
        "iam.googleapis.com",
        "storage.googleapis.com"
      ])
      project = var.project_id
      service = each.key
      disable_on_destroy = false
    }
    
    # 2. Project data source to retrieve Project Number
    data "google_project" "project" {
      project_id = var.project_id
      depends_on = [google_project_service.apis]
    }
    
    locals {
      builder_sa = var.builder_service_account
    }
    
    # 3. Storage Bucket for Image Builder Workdir
    resource "google_storage_bucket" "workdir_bucket" {
      name                        = var.gcs_bucket_name != "" ? var.gcs_bucket_name : "${var.project_id}-vm-builder-workdir"
      project                     = var.project_id
      location                    = var.region
      force_destroy               = true
      uniform_bucket_level_access = true
      lifecycle_rule {
        action {
          type = "Delete"
        }
        condition {
          age = var.gcs_lifecycle_age_days
        }
      }
      depends_on = [google_project_service.apis]
    }
    
    # 4. Cloud Build Trigger
    resource "google_cloudbuild_trigger" "image_builder_trigger" {
      name        = var.trigger_name
      location    = var.region
      project     = var.project_id
      description = "Trigger that runs Image Builder customization"
    
      service_account = var.builder_service_account != "" ? "projects/${var.project_id}/serviceAccounts/${var.builder_service_account}" : null
    
      repository_event_config {
        repository = replace(var.github_repo_name, "gitRepositoryLinks", "repositories")
        push {
          branch = "^main$"
        }
      }
      filename = var.cloudbuild_yaml_path
    
      substitutions = {
        _GCS_WORKDIR                    = "gs://${google_storage_bucket.workdir_bucket.name}/workdir/"
        _SERVICE_ACCOUNT                = "projects/${var.project_id}/serviceAccounts/${local.builder_sa}"
        _IMAGE_OUTPUT_PATH              = "image-builder/binaryOut"
        _PROJECT_ID                     = var.project_id
        _LOCATION                       = var.region
        _REPOSITORY_NAME                = var.ar_repository_id
        _PACKAGE_NAME                   = var.ar_package_name
        _IMAGE_BUILDER_CONFIG_PATH      = var.image_builder_config_path
        _ARTIFACT_REGISTRY_RESOURCE_URI = "projects/${var.project_id}/locations/${var.region}/repositories/${var.ar_repository_id}/packages/${var.ar_package_name}/versions/v$${BUILD_ID}"
      }
      depends_on = [
        google_project_service.apis
      ]
    }
    

    outputs.tf

    הקובץ הזה מגדיר את מאפייני הפלט שמוחזרים למסוף אחרי הפריסה:

    output "builder_service_account" {
      value       = local.builder_sa
      description = "The email representation of the resolved Image Builder service account."
    }
    
    output "workdir_bucket" {
      value       = google_storage_bucket.workdir_bucket.name
      description = "The name of the storage workdir bucket."
    }
    
    output "artifact_registry_repository" {
      value       = "projects/${var.project_id}/locations/${var.region}/repositories/${var.ar_repository_id}"
      description = "The fully qualified resource path of the Artifact Registry repository."
    }
    
    output "cloud_build_trigger_id" {
      value       = google_cloudbuild_trigger.image_builder_trigger.trigger_id
      description = "The unique ID for the created Cloud Build Trigger."
    }
    

    providers.tf

    בקובץ הזה מוגדרים האזור והגרסה הנדרשים של Terraform:

    terraform {
      required_version = ">= 1.3"
      required_providers {
        google = {
          source  = "hashicorp/google"
          version = ">= 5.0, < 7.0"
        }
      }
    }
    
    provider "google" {
      project = var.project_id
      region  = var.region
    }
    

    variables.tf

    בקובץ הזה מצהירים על כל משתני הקלט הנדרשים והאופציונליים ועל כללי האימות:

    variable "project_id" {
      type        = string
      description = "The target Project ID where resources will be created."
      validation {
        condition     = can(regex("^[a-z0-9-]{6,30}$", var.project_id))
        error_message = "The project_id must consist of lowercase letters, numbers, and hyphens, and be between 6 and 30 characters."
      }
    }
    
    variable "region" {
      type        = string
      default     = "us-central1"
      description = "Location used for cloud build triggers, storage buckets, and artifact registry."
    }
    
    variable "github_repo_name" {
      type        = string
      default     = ""
      description = "Developer Connect github repository details, format: projects/PROJECT_ID/locations/LOCATION/connections/CONNECTION/repositories/REPO_LINK"
      validation {
        condition     = can(regex("^projects/[^/]+/locations/[^/]+/connections/[^/]+/(gitRepositoryLinks|repositories)/[^/]+$", var.github_repo_name))
        error_message = "The github_repo_name must follow either the Cloud Build v2 repository link format (using '/repositories/') or the Developer Connect resource format (using '/gitRepositoryLinks/')."
      }
    }
    
    variable "builder_service_account" {
      type        = string
      default     = ""
      description = "The email representation of the pre-existing Image Builder service account. If omitted, the default Cloud Build service account will be used."
    }
    
    variable "gcs_bucket_name" {
      type        = string
      default     = ""
      description = "Custom name for the workdir storage bucket. If left empty, a default name using the project ID will be constructed."
    }
    
    variable "gcs_lifecycle_age_days" {
      type        = number
      default     = 30
      description = "The number of days after which temporary logs and artifacts in the storage workdir bucket are deleted."
    }
    
    variable "ar_repository_id" {
      type        = string
      default     = "vm-images"
      description = "The repository ID for the generic Artifact Registry hosting the final OS image tarballs."
    }
    
    variable "ar_package_name" {
      type        = string
      default     = "image-builder"
      description = "The package name under which the generic OS image artifact will be registered in Artifact Registry."
    }
    
    variable "trigger_name" {
      type        = string
      default     = "custom-os-image-builder"
      description = "The name of the Cloud Build trigger."
    }
    
    variable "cloudbuild_yaml_path" {
      type        = string
      default     = "cloudbuild.yaml"
      description = "The path to the cloudbuild.yaml configuration file relative to the repository root."
    }
    
    variable "image_builder_config_path" {
      type        = string
      default     = "imagebuilder.yaml"
      description = "The path to the imagebuilder.yaml configuration file relative to the repository root."
    }
    
  3. כדי לפרוס את ההגדרות, מריצים את הפקודות הבאות בספרייה שמכילה את קובצי Terraform:

    1. מפעילים את הספרייה:
      terraform init
    2. בודקים את התחביר:
      terraform validate
    3. תצוגה מקדימה של הפריסה:
      terraform plan
    4. מחילים את ההגדרה:
      terraform apply

אימות ומעקב אחר ה-build

כדי לעקוב אחרי ההתקדמות של צינור ה-build, מבצעים את השלבים הבאים:

  1. במסוף Google Cloud , נכנסים לדף Cloud Build.

    כניסה ל-Cloud Build

  2. בתפריט הניווט, לוחצים על היסטוריה כדי לראות משרות פעילות או משרות שהושלמו.

  3. ברשימה Builds (גרסאות Build), לוחצים על Build ID (מזהה גרסת ה-Build) של גרסת ה-Build כדי לבדוק את יומני ההפעלה של הקונטיינר. בקטעי היומן מוצגים השלבים שמבוצעים במכונה הווירטואלית של העובד, כמו עדכונים של חבילות מערכת או פקודות מותאמות אישית של מעטפת, ואחריהם תוצאות בדיקת האימות מהמכונה הווירטואלית של הבדיקה ורישום הפלט הסופי.

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