tools

packer - Multi-Cloud (AWS + GCP)

Packer의 큰 강점은 동일한 빌드 파이프라인으로 여러 클라우드 이미지를 동시에 만들 수 있다는 점입니다. 이번 글에서는 하나의 템플릿으로 AWS(AMI)와 GCP(Compute Engine Image)를 한 번에 생성하는 방법과, 각 빌더의 핵심 필드·자격 증명·네트워킹 주의 사항까지 정리합니다.

아래 템플릿은 AWS의 amazon-ebs 빌더와 GCP의 googlecompute 빌더를 함께 선언하고, 하나의 build 블록에서 두 이미지를 병렬로 빌드합니다.

packer
packer {
  required_plugins { # 최신 Packer(HCL2)에서는 Amazon, Google 빌더 모두 required_plugins로 명시해야 packer init이 동작
    amazon = {
      source  = "github.com/hashicorp/amazon"
      version = ">= 1.0.0"
    }
    googlecompute = {
      source  = "github.com/hashicorp/googlecompute"
      version = ">= 1.0.0"
    }
  }
}
 
# -------- Variables
variable "image_prefix"     { type = string, default = "multi-cloud" }
 
# AWS
variable "aws_region"       { type = string, default = "us-east-1" }
variable "aws_instance_type"{ type = string, default = "t3.micro" }
 
# GCP
variable "gcp_project_id"   { type = string, default = "my-gcp-project" }
variable "gcp_zone"         { type = string, default = "us-central1-a" }
 
locals {
  ts = timestamp()
}
 
# -------- AWS: amazon-ebs
source "amazon-ebs" "aws-image" {
  ami_name      = "${var.image_prefix}-aws-${local.ts}"
  region        = var.aws_region
  instance_type = var.aws_instance_type
 
  source_ami_filter {
    filters = {
      name                = "ubuntu/images/*ubuntu-20.04-amd64-server-*"
      root-device-type    = "ebs"
      virtualization-type = "hvm"
    }
    owners      = ["099720109477"] # Canonical
    most_recent = true
  }
 
  ssh_username = "ubuntu"
 
  # (선택) 기본 VPC가 없다면 명시하세요
  # subnet_id           = "subnet-xxxx"
  # security_group_ids  = ["sg-xxxx"]
}
 
# -------- GCP: googlecompute
source "googlecompute" "gcp-image" {
  image_name          = "${var.image_prefix}-gcp-${lower(replace(local.ts, ":", "-"))}"
  project_id          = var.gcp_project_id
  source_image_family = "ubuntu-2004-lts"
  zone                = var.gcp_zone
 
  # Ubuntu 계열 기본 사용자
  ssh_username        = "ubuntu"
 
  # (선택) 네트워크/서브넷/서비스 계정 지정
  # network            = "default"
  # subnetwork         = "default"
  # service_account_email = "builder@${var.gcp_project_id}.iam.gserviceaccount.com"
}
 
# -------- Build (기본 병렬 실행)
build {
  name    = "aws-gcp-parallel"
  sources = [
    "source.amazon-ebs.aws-image",
    "source.googlecompute.gcp-image",
  ]
 
  # 두 플랫폼에 공통 적용되는 간단한 프로비저닝
  provisioner "shell" {
    inline = [
      "echo 'Configured for multi-cloud!' | sudo tee /tmp/info.txt"
    ]
  }
 
  post-processor "manifest" {
    output     = "manifest.json"
    strip_path = true
    custom_data = {
      aws_region   = var.aws_region
      gcp_project  = var.gcp_project_id
      image_prefix = var.image_prefix
    }
  }
}

멀티 클라우드 빌드를 도입하면 하나의 프로비저닝 스크립트를 AWS와 GCP 모두에 적용해 런타임 차이를 최소화할 수 있습니다. 여기서 런타임 차이를 최소화한다는 말은, 같은 애플리케이션이라도 클라우드 환경마다 기본 이미지, 네트워크 설정, 보안 옵션, 패키지 버전 등이 조금씩 달라 실행 동작에 미묘한 차이가 생길 수 있는데, 이를 줄인다는 뜻입니다.

또한, 단일 실행으로 두 이미지를 병렬 빌드해 파이프라인 전체 시간을 단축할 수 있다. sources 배열에 Azure, VMware 등 새로운 빌더를 손쉽게 추가할 수 있어 확장성과 유연성이 크게 향상된다.

1. source "amazon-ebs" "aws-image"

AWS에서 EBS 기반 AMI를 생성하는 빌더입니다.

  • ami_name: AMI 이름. timestamp()로 매 빌드마다 고유화

  • region / instance_type: 빌드가 진행될 리전과 EC2 타입

  • source_ami_filter: 베이스 AMI 검색 조건

    • name: Ubuntu 20.04 이미지 패턴
    • owners: Canonical 공식 계정 ID 099720109477
    • most_recent: 최신 이미지 선택
  • ssh_username: 우분투 공식 AMI의 기본 SSH 사용자(ubuntu)

필요 시, VPC/서브넷/보안그룹을 고정하려면 subnet_id, security_group_id(s) 등을 추가할 수 있습니다. 디폴트 VPC가 없는 계정이라면 반드시 명시하세요.

2. source "googlecompute" "gcp-image"

GCP에서 Compute Engine 이미지를 생성하는 빌더입니다.

  • image_name: 최종 생성될 이미지 이름(영문 소문자·숫자·대시 권장)
  • project_id: 이미지를 만들 GCP 프로젝트
  • source_image_family: 베이스 이미지 패밀리(여기서는 ubuntu-2004-lts)
  • zone: 빌드용 인스턴스를 띄울 존
  • ssh_username: 빌드 시 사용할 SSH 사용자명

GCP에서는 Compute Engine API가 활성화되어 있어야 하며, 빌드 VM로의 SSH(포트 22) 접속을 허용하는 방화벽 규칙이 필요합니다. 프로젝트·VPC 보안 정책에 따라 추가 설정이 요구될 수 있습니다.

3. build 블록과 병렬 빌드

sources 배열에 여러 source 블록을 나열하면, Packer는 각 빌더를 독립적으로 실행합니다. 일반적으로 동시에(병렬) 진행되므로 빌드 시간이 단축됩니다. Packer는 build 블록에 여러 source를 나열하면 기본적으로 병렬 실행하지만, 빌드 옵션에 따라 직렬(순차) 실행도 가능합니다. “기본적으로 병렬, 필요시 -parallel-builds=1 옵션으로 직렬화 가능합니다.

provisioner "shell"각 빌드 머신 내부에서 동일하게 실행됩니다. 위 예제에서는 /tmp/info.txt에 간단한 문자열을 기록합니다. AWS 인스턴스와 GCP 인스턴스에서 각각 이 명령이 수행되어 결과 이미지에 반영됩니다.

동일한 프로비저닝을 두 플랫폼에 모두 적용하면 일관성이 좋아지지만, 클라우드별 차이(패키지 관리자, 네트워킹, 메타데이터 서비스 등)가 있는 작업이라면 조건 분기(예: 쉘 스크립트 내부에서 클라우드 감지)를 고려하세요.


자격 증명 & 환경 준비

AWS

자격 증명(로그인 정보) 준비

AWS에서 빌드를 하려면 내가 누구인지 증명할 방법이 필요합니다. 대표적으로 3가지 방법이 있습니다.

  1. 환경변수

AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY(필요 시 AWS_SESSION_TOKEN)

export AWS_ACCESS_KEY_ID=AKIA123...
export AWS_SECRET_ACCESS_KEY=abcd1234...
export AWS_SESSION_TOKEN=xyz...   # (임시 자격일 때만 필요)
  1. 공유 자격 파일

~/.aws/credentials 파일에 아래와 같이 셋팅합니다.

[default]
aws_access_key_id = AKIA123...
aws_secret_access_key = abcd1234...
  1. IAM Role

EC2 인스턴스에 **역할(Role)**을 붙여주면, 키를 따로 저장하지 않아도 자동으로 권한이 생깁니다.

필요 권한

이미지 빌드를 위해선 EC2와 EBS 관련 권한이 필요합니다. 예를 들어, 최소한 이런 권한들이 있어야 합니다.

  • 이미지 관련: ec2:CreateImage, ec2:DeregisterImage, ec2:DescribeImages
  • 인스턴스 관련: ec2:RunInstances, ec2:TerminateInstances
  • 디스크 관련: ec2:CreateSnapshot, ec2:DeleteSnapshot

아래는 IAM 정책(JSON) 예시입니다.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:CreateImage",
        "ec2:DeregisterImage",
        "ec2:DescribeImages",
        "ec2:RunInstances",
        "ec2:TerminateInstances",
        "ec2:CreateSnapshot",
        "ec2:DeleteSnapshot"
      ],
      "Resource": "*"
    }
  ]
}

확인 방법

aws sts get-caller-identity   # 현재 내가 누구로 로그인했는지 확인
aws ec2 describe-images --owners self --max-results 1   # 내가 만든 AMI 확인

빌드 흐름을 다이어그램으로 표현하면 아래와 같습니다.

[사용자/빌드 도구]

        │ (1) 자격 증명: 환경변수 / credentials 파일 / IAM Role

 ┌───────────────┐
 │   AWS STS/IAM │  ← 신분증 확인
 └───────┬───────┘
         │ (2) 권한 확인

 ┌─────────────────────┐
 │ Amazon EC2 (인스턴스) │
 └───┬─────────────────┘

     │ (3) 디스크/스냅샷 작업

 ┌───────────────┐
 │   Amazon EBS  │
 └───────────────┘

GCP

자격 증명 준비

GCP에서는 보통 **서비스 계정(Service Account)**을 만들어 키(JSON 파일)를 발급합니다. 계정 키(JSON)를 직접 쓰는 방식은 보안상 취약할 수 있어, Workload Identity Federation(OIDC 기반)도 고려할 수 있습니다.

  1. 환경변수 등록 (권장)
export GOOGLE_APPLICATION_CREDENTIALS="/home/user/my-sa.json"
  1. gcloud CLI 사용
gcloud auth application-default login

필요 권한

  • Compute Engine API를 켜야 함 (기본은 꺼져 있음)
  • 네트워크: 빌드용 VM이 SSH 연결이 가능해야 함 (방화벽 규칙 열기)
  • 역할(Role): 서비스 계정에 아래 권한이 필요해요:
    • VM 인스턴스 관리 → roles/compute.instanceAdmin.v1
    • 이미지/스냅샷 관리 → roles/compute.imageAdmin
    • (필요 시) VM에 서비스 계정 붙이기 → roles/iam.serviceAccountUser

gcloud 명령어는 아래와 같습니다.

gcloud projects add-iam-policy-binding my-project \
  --member="serviceAccount:builder@my-project.iam.gserviceaccount.com" \
  --role="roles/compute.instanceAdmin.v1"

확인 방법

gcloud auth list                # 현재 인증된 계정
gcloud config list project      # 현재 프로젝트 확인
gcloud compute images list --limit=1   # 접근 가능한 이미지 확인

빌드 흐름을 다이어그램으로 표현하면 아래와 같습니다.

[사용자/빌드 도구]

        │ (1) 서비스 계정 키(JSON) 또는 gcloud 로그인

 ┌───────────────────┐
 │ Google IAM (ADC) │  ← 신분증 확인
 └─────────┬─────────┘
           │ (2) 역할(Role) 확인

 ┌───────────────────┐
 │ Compute Engine VM │
 └───┬──────────────┘

     │ (3) 디스크/이미지/스냅샷 조작

 ┌───────────────────────┐
 │ Compute Engine Images │
 └───────────────────────┘

실행 방법

Packer HCL 파일(*.pkr.hcl)이 있는 프로젝트 폴더에서 실행하고, AWS/GCP 자격 증명은 이미 설정돼 있다고 가정하고 진행하겠습니다.

0. 시작 전 확인

packer -v                     # Packer 버전 확인
aws sts get-caller-identity   # (선택) AWS 자격 확인
gcloud config list project    # (선택) GCP 현재 프로젝트 확인

1. 플러그인 설치 & 검증

required_plugins 블록에 선언된 플러그인을 내려받고, 구성이 맞는지 미리 점검합니다.

packer init .
packer validate .
  • 성공 Msg -> Success! Everything looks good.
  • 자주 나는 검증 오류
    • A variable ... is required but was not set -> -var 또는 -var-file로 값 주입
    • unknown plugin -> required_plugins 블록 확인 후 다시 packer init .

2. 동시 빌드 실행

템플릿에 여러 소스가 있으면 가능한 범위에서 병렬로 빌드됩니다.

packer build .

source.amazon-ebs.aws-image 와 source.googlecompute.gcp-image 두 개가 정의돼 있으면 둘 다 실행됩니다.

3. (옵션) 특정 클라우드만 선택 빌드

-only에 소스 주소(형식: source.<builder>.<name>)를 넘겨 원하는 것만 빌드합니다.

# AWS만 빌드
packer build -only=source.amazon-ebs.aws-image .
 
# GCP만 빌드
packer build -only=source.googlecompute.gcp-image .
 
# 여러 개 고를 땐 쉼표로 나열
packer build -only=source.amazon-ebs.aws-image,source.googlecompute.gcp-image .
 

4. 로그 확인

문제 발생 시 로그 레벨을 올리거나 디버그 모드로 확인 할 수 있습니다.

# 터미널에 상세 로그 출력
PACKER_LOG=1 packer build .
 
# (추천) 파일로 저장
PACKER_LOG=1 PACKER_LOG_PATH=packer.log packer build .

5. 변수 주입으로 설정 분리

프로젝트/리전/이미지 이름 등 환경 값을 관리가 가능합니다.

# 단일 변수
packer build -var 'aws_region=ap-northeast-2' .
 
# 변수 파일(HCL)
packer build -var-file=env/dev.pkrvars.hcl .
  • ex. env/dev.pkrvars.hcl
aws_region = "ap-northeast-2"
gcp_project_id = "my-dev-project"
image_prefix = "myapp-dev"

빌드 결과 확인

  • AWS: EC2 콘솔 → AMIs에서 multi-cloud-aws-... 확인
  • GCP: Compute Engine 콘솔 → 이미지에서 multi-cloud-gcp-... 확인

각 이미지를 기반으로 인스턴스를 띄운 뒤 /tmp/info.txt가 존재하는지 확인하면 프로비저닝이 반영되었는지 검증할 수 있습니다.


주의사항

  • 네트워킹 고정: 기업 환경에서는 기본 VPC 미사용이 흔합니다. VPC/Subnet/SG(보안그룹) 또는 GCP VPC/Firewall을 명시적으로 지정하세요.
  • 프로비저닝 분기: 공통 스크립트 + 클라우드별 스크립트 조합으로 유지보수성을 높일 수 있습니다.
  • 이미지 네이밍 규칙: 앱명-환경-OS-버전-타임스탬프 형태로 표준화하면 운영 가독성이 올라갑니다.
  • 보안: 빌드 머신에 임시 자격 증명/키를 쓰는 경우 이미지에 잔존하지 않도록 마지막에 정리하세요.
  • 비용 관리: 실패한 빌드도 스냅샷/디스크가 남을 수 있습니다. 정리 스크립트나 주기적 점검을 권장합니다.

정리

이번 예제의 핵심은 하나의 Packer 템플릿으로 AWS와 GCP 이미지를 동시에 빌드할 수 있다는 점이며, build 블록의 sources 배열에 여러 source를 지정하면 동일한 프로비저닝이 각 클라우드 빌더에 적용되고 기본적으로 병렬로 진행되어 빌드 시간을 단축할 수 있습니다. 이 패턴을 익히면 동일한 빌드 파이프라인을 Azure, VMware 등 다른 플랫폼으로도 손쉽게 확장할 수 있고, 멀티 클라우드 환경에서 이미지 표준화와 일관성을 자연스럽게 확보할 수 있습니다.


참고 자료