gcloud または Terraform を使用してカスタム OS イメージ パイプラインを作成する

Google Cloud CLI または Terraform を使用して、Image Builder パイプラインをプログラムで構成して送信します。パイプラインをプログラムで構成すると、宣言型構成ファイルでインフラストラクチャ設定、ベース OS イメージ、カスタマイズ アクション、検証テストを定義できます。

始める前に

必要なロール

Google Cloud CLI または Terraform を使用してイメージ カスタマイズ パイプラインを作成して送信するために必要な権限を取得するには、 プロジェクトに対する 次の IAM ロールの付与を管理者に依頼してください。

ロールの付与については、プロジェクト、フォルダ、組織へのアクセス権の管理をご覧ください。

必要な権限は、カスタム ロールや他の事前定義 ロールから取得することもできます。

構成ファイルを作成する

gcloud CLI または Terraform を使用してパイプラインを構成するには、次の 2 つの構成ファイルを作成します。

  • imagebuilder.yaml: ベース OS イメージ、ワーカー VM インフラストラクチャの設定、ターゲット イメージの出力の詳細、シェル スクリプトの実行、ファイルの転送、再起動などの順次カスタマイズ手順など、イメージのカスタマイズ レシピを定義します。
  • cloudbuild.yaml: カスタム OS イメージの解析、検証、ビルド、テスト、公開など、Cloud Build のビルド プロセス ステップを調整します。

イメージ構成ファイルを作成する

イメージ構成を指定するには、ローカル ディレクトリに imagebuilder.yaml という名前のファイルを作成します。サポートされているすべてのスキーマ フィールドと カスタマイズ アクションの完全なリストについては、カスタマイズ レシピのスキーマサポートされているカスタマイズ アクションをご覧ください。

次の imagebuilder.yaml サンプルファイルは、指定したリージョンとゾーンの e2-standard-4 ワーカー VM を使用してカスタマイズされた Ubuntu 22.04 LTS イメージをビルドし、システム パッケージの更新を実行するパイプラインを構成します。

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"

プレースホルダの値を次のように置き換えます。

  • PROJECT_ID: プロジェクト ID。
  • REGION: ターゲット イメージの保存場所(us-east1europe-west1 など)。次のリージョンとゾーンの要件を満たしていることを確認してください。
    • Image Builder は、 Cloud Build が利用可能なリージョンでのみサポートされています。
    • ワーカー VM ZONE は、指定した REGION 内に配置する必要があります。
    • ネットワーク レイテンシを最小限に抑え、リージョン間エグレス料金を回避するには、ワーカー VM ゾーン、Cloud Storage ステージング バケット、Artifact Registry リポジトリ、ターゲット イメージの保存場所を同じリージョンに配置します。
  • ZONE: 指定した REGION 内にあるゾーン(us-east1-beurope-west1-b など)。

オーケストレーター ビルドファイルを作成する

同じディレクトリに cloudbuild.yaml という名前のファイルを作成します。このファイルは、Image Builder コンテナ ステップを呼び出して、カスタム OS イメージをビルド、検証、公開します。

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'

プレースホルダの値を次のように置き換えます。

  • STAGING_BUCKET_NAME: 一時的なステージング ワークスペースとして使用する、プロジェクト内の既存の Cloud Storage バケット。バケットがない場合は、 gcloud storage buckets create gs://STAGING_BUCKET_NAME を実行して作成できます。 Terraform を使用してパイプラインをデプロイすると、Terraform によってこのバケットが自動的に作成されます。
  • PROJECT_ID: 実際の Google Cloud プロジェクト ID。
  • REGION:Artifact Registry リポジトリの Google Cloud リージョン(us-east1europe-west1 など)。
  • SERVICE_ACCOUNT_EMAIL:必要な IAM 権限で構成したサービス アカウントのメールアドレス
  • REPOSITORYPACKAGE: Artifact Registry で作成されたターゲット リポジトリとパッケージ名。Artifact Registry レジストリを設定するには、 Artifact Registry を構成するをご覧ください。

ビルド パイプラインを作成して送信する

イメージ カスタマイズ パイプラインを実行するには、gcloud CLI を使用してビルドを送信するか、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 プロジェクト ID。
  • REGION: イメージ カスタマイズ パイプライン ジョブを実行する Google Cloud リージョン。
  • SERVICE_ACCOUNT_EMAIL: 必要な IAM 権限で構成されたサービス アカウントのメールアドレス。

このコマンドは、カスタマイズ ワークスペースをアップロードし、Cloud Build の実行を登録して、オーケストレーション コンテナを起動します。

Terraform

カスタマイズされた OS イメージを自動的にビルドして検証するために必要なインフラストラクチャをプロビジョニングするには、Terraform を使用します。この Terraform 構成では、次のタスクが完了します。

  • 必要な Google Cloud API を有効にします。
  • 一時ログとビルド アーティファクトを保存するための専用の Cloud Storage バケット(workdir_bucket)を作成します。
  • Developer Connect GitHub リポジトリ接続にリンクされた Cloud Build トリガー(image_builder_trigger)を構成します。

Terraform 構成ファイルを作成する

Terraform を使用してパイプライン インフラストラクチャを整理してデプロイするには、次の手順を完了します。

  1. アプリケーション リポジトリとは別の、ローカル ワークステーションまたは CI/CD 環境に専用のディレクトリを作成し、そのディレクトリに移動します。

    mkdir terraform-image-builder && cd terraform-image-builder
    
  2. このディレクトリに、次の 5 つの Terraform 構成ファイルを作成します。

    • terraform.tfvars: プロジェクト固有の変数の値を設定します。
    • main.tf: 実際の Google Cloud プロジェクトでリソースをプロビジョニングします。 必要な API の有効化、Cloud Storage ステージング バケット(workdir_bucket)の作成、Cloud Build トリガー (image_builder_trigger)のデプロイなどを行います。
    • outputs.tf: デプロイ後にターミナルに表示される出力値(トリガー ID、ステージング バケット名など)を定義します。
    • 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"
    

    既存のリソースのプレースホルダを次のように置き換えます。

    • PROJECT_ID: 既存の Google Cloud プロジェクト ID。
    • SERVICE_ACCOUNT_EMAIL: Image Builder サービス アカウントの構成 で構成されたビルド サービス アカウントのメールアドレス
    • LOCATIONCONNECTION、 および REPO_NAMEリポジトリを接続するで構成された Developer Connect ホスト リージョン、接続名、リポジトリ リンク。
    • CLOUDBUILD_YAML_PATH: ローカル ディレクトリ内の cloudbuild.yaml ファイルへの相対パス。cloudbuild.yaml を Git リポジトリにチェックインする必要はありません。
    • RECIPE_PATH: Git リポジトリにチェックインされた imagebuilder.yaml カスタマイズ レシピ ファイルへの相対パス。
    • REPOSITORY_NAME: Artifact Registry の構成で作成された既存の汎用 Artifact Registry リポジトリ。

    Terraform が作成するリソースのプレースホルダを次のように置き換えます。

    • REGION: Terraform が Cloud Storage ステージング Google Cloud バケットと Cloud Build ビルドトリガーをプロビジョニングするターゲット リージョン(us-central1 など)。
    • TRIGGER_NAME: Terraform によって作成された新しい Cloud Build リポジトリ トリガーの名前(git-push-os-builder など)。
    • LIFECYCLE_DAYS: Terraform によって作成された Cloud Storage ステージング バケット内の中間アーティファクトが自動的に削除されるまでの保持期間(日数)(30 など)。
    • PACKAGE_NAME: Artifact Registry リポジトリ内に作成されたパッケージに Terraform が使用する名前。このパッケージには、公開された OS イメージ バージョン(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

ビルドを検証してモニタリングする

ビルド パイプラインの進行状況を追跡するには、次の手順を完了します。

  1. コンソール Google Cloud で、Cloud Build ページに移動します。

    Cloud Build に移動

  2. ナビゲーション メニューで [履歴] をクリックして、アクティブなジョブまたは完了したジョブを表示します。

  3. [ビルド] リストで、ビルドの [ビルド ID] をクリックして、コンテナ実行ログを検査します。ログには、ワーカー VM 内で実行されているステップ(システム パッケージの更新やカスタムシェル コマンドなど)が表示され、その後にテスト VM からの検証テストの結果と最終出力の登録が表示されます。

次のステップ