gcloud 또는 Terraform을 사용하여 커스텀 OS 이미지 파이프라인 만들기

Google Cloud CLI 또는 Terraform을 사용하여 Image Builder 파이프라인을 프로그래매틱 방식으로 구성하고 제출합니다. 파이프라인을 프로그래매틱 방식으로 구성하면 선언적 구성 파일에서 인프라 설정, 기본 OS 이미지, 맞춤설정 작업, 유효성 검사 테스트를 정의할 수 있습니다.

시작하기 전에

필요한 역할

Google Cloud CLI 또는 Terraform을 사용하여 이미지 맞춤설정 파이프라인을 만들고 제출하는 데 필요한 권한을 얻으려면 관리자에게 프로젝트에 대한 다음 IAM 역할을 부여해 달라고 요청하세요.

역할 부여에 대한 자세한 내용은 프로젝트, 폴더, 조직에 대한 액세스 관리를 참조하세요.

커스텀 역할이나 다른 사전 정의된 역할을 통해 필요한 권한을 얻을 수도 있습니다.

구성 파일 만들기

gcloud CLI 또는 Terraform을 사용하여 파이프라인을 구성하려면 다음 두 가지 구성 파일을 만듭니다.

  • 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-east1 또는 europe-west1)입니다. 다음 리전 및 영역 요구사항을 충족하는지 확인합니다.
    • Image Builder는 Cloud Build를 사용할 수 있는 리전에서만 지원됩니다.
    • 작업자 VM ZONE은 지정된 REGION 내에 있어야 합니다.
    • 네트워크 지연 시간을 최소화하고 리전 간 이그레스 요금을 방지하려면 작업자 VM 영역, Cloud Storage 스테이징 버킷, Artifact Registry 저장소, 대상 이미지 저장 위치가 동일한 리전에 함께 있어야 합니다.
  • ZONE: 지정된 REGION 내에 있는 영역(예: us-east1-b 또는 europe-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-east1 또는 europe-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: 필요한 API 사용 설정, Cloud Storage 스테이징 버킷 (workdir_bucket) 만들기, Cloud Build 트리거 (image_builder_trigger) 배포를 포함하여 Google Cloud 프로젝트에서 리소스를 프로비저닝합니다.
    • outputs.tf: 트리거 ID 및 스테이징 버킷 이름과 같이 배포 후 터미널에 표시되는 출력 값을 정의합니다.
    • providers.tf: 필요한 Terraform 버전 (>= 1.3)을 지정하고 프로바이더 (hashicorp/google)를 구성합니다. Google Cloud
    • 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 서비스 계정 구성에서 구성된 빌드 서비스 계정의 이메일 주소입니다.
    • LOCATION, CONNECTION, 및 REPO_NAME: 저장소 연결에서 구성된 Developer Connect 호스트 리전, 연결 이름, 저장소 링크입니다.
    • CLOUDBUILD_YAML_PATH: 로컬 디렉터리에 있는 cloudbuild.yaml 파일의 상대 경로입니다. cloudbuild.yaml을 Git 저장소에 체크인할 필요는 없습니다.
    • RECIPE_PATH: Git 저장소에 체크인된 imagebuilder.yaml 맞춤설정 레시피 파일의 상대 경로입니다.
    • REPOSITORY_NAME: Artifact Registry 구성에서 만든 기존 일반 Artifact Registry 저장소입니다. Configure Artifact Registry

    Terraform에서 만드는 리소스의 다음 자리표시자를 바꿉니다.

    • REGION: Terraform에서 Cloud Storage 스테이징 버킷 및 Cloud Build 빌드 트리거를 프로비저닝하는 대상 Google Cloud리전(예: us-central1)입니다.
    • TRIGGER_NAME: Terraform에서 만든 새 Cloud Build 저장소 트리거의 이름(예: git-push-os-builder)입니다.
    • LIFECYCLE_DAYS: Terraform에서 만든 Cloud Storage 스테이징 버킷의 중간 아티팩트가 자동으로 삭제되기 전의 보관 기간(일)(예: 30)입니다.
    • PACKAGE_NAME: Terraform에서 Artifact Registry 저장소 내에 만든 패키지에 사용할 이름입니다. 이 패키지는 게시된 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의 유효성 검사 테스트 결과, 최종 출력 등록이 표시됩니다.

다음 단계