클라우드 아카이브

[Infra/IaC] 오픈소스로 살펴보는 Packer 개념과 구성요소 본문

Cloud Native Solution/[IaC] packer

[Infra/IaC] 오픈소스로 살펴보는 Packer 개념과 구성요소

Cloud Engineer 2022. 2. 19. 12:33
반응형

1. 들어가기 전

Packer CI

패커(Packer)는 하시코프(HashiCorp)에서 만든 IaC 도구 중 하나이며, AWS, GCP, Azure를 비롯하여 VMware, Vagrant, OpenStack 등 다양한 플랫폼에서 사용할 수 있는 이미지를 동적으로 사용할 수 있게 해줍니다. 공식 홈페이지(링크)에서는 다양한 플랫폼에서 머신 이미지를 만드는 것을 자동화하는 도구라고 소개합니다.

본 포스팅의 기반 코드는 깃허브에 있는 레퍼지토리(링크)를 기반으로 작성되었습니다. BSD-2 라이센스로 출처를 밝힐 경우 주요 내용 복제, 배포, 수정의 권한 허용 가능하다는 항목을 확인한 이후 포스팅했음을 밝힙니다.

1.1 패커를 사용하는 이유

  1. 신속한 인프라 구축
    • Terraform과 연계하여 몇 초 내에 Packer 이미지를 프로비저닝하여 인스턴스를 시작할 수 있도록 해줍니다.
    • Terraform + Ansible + Packer 조합이 자주 사용되며 이를 통해 머신 템플릿 생성부터 인스턴스 프로비저닝까지 신속하게 머신 구성을 할 수 있도록 도와줍니다.
  2. 다중 공급자 이식성
    • 동일한 이미지를 사용하여 AWS, GCP, Azure, VMware 등 다양한 플랫폼에서 환경 별로 인스턴스를 실행할 수 있습니다.
    • 어떠한 플랫폼에서든 코드를 기반으로 일관되게 VM/Container 이미지 템플릿을 적용할 수 있게 해줍니다.

2. Packer 구성 요소

2.1 Packer Workflow

관련 코드 출처: https://github.com/vmware-samples/packer-examples-for-vsphere/tree/main/builds

2.2 Packer 구성 요소

2.2.1 Packer Block : 본 템플릿에서 사용할 Packer의 구성 정의

Packer 최소 버전과 사용할 플러그인에 대한 정보를 입력합니다.

// linux-rhel.pkr.hcl
// BLOCK: packer
// The Packer configuration.

packer {
  required_version = ">= 1.7.10"
  required_plugins {
    vsphere = {
      version = ">= v1.0.3"
      source  = "github.com/hashicorp/vsphere"
    }
  }
}

2.2.2 Global Variable : variable 파일에서 변수 바인딩

Packer 템플릿에서 전역 변수를 사용하기 위해 variable에 정의된 변수 이름(2.2.2.1 참고)과 .pkrvar 확장자로 정의된 변수 값(2.2.2.2 참고)을 바인딩해줍니다.

2.2.3 Variable 파일 : 사용할 전역 변수의 명세를 정의

Terraform의 Variable과 유사한 방식이며, 여러 개의 packer 템플릿 파일에서 공유하여 사용할 수 있는 전역 변수(Global Variable)를 정의합니다.

/*
    DESCRIPTION:
    Red Hat Enterprise Linux 8 variables using the Packer Builder for VMware vSphere (vsphere-iso).
*/
//  variables.pkr.hcl
//  BLOCK: variable
//  Defines the input variables.

// Red Hat Subscription Manager Credentials

variable "rhsm_username" {
  type        = string
  description = "The username to Red Hat Subscription Manager."
  sensitive   = true
}

variable "rhsm_password" {
  type        = string
  description = "The password to login to Red Hat Subscription Manager."
  sensitive   = true
}

....

2.2.4 .pkrvars 파일 : 전역 변수에 바인딩할 값을 입력 (Terraform의 Module과 유사)

Terraform의 Module과 유사한 방식이며, variable 파일을 통해 정의한 전역 변수(Global Variable)에 바인딩할 값을 입력합니다.

// vsphere.pkrvars.hcl.example

/*
    DESCRIPTION:
    VMware vSphere variables used for all builds.
    - Variables are use by the source blocks.
*/

// vSphere Credentials
vsphere_endpoint            = "sfo-w01-vc01.rainpole.io"
vsphere_username            = "svc-packer-vsphere@rainpole.io"
vsphere_password            = "R@in!$aG00dThing."
vsphere_insecure_connection = false

....

2.2.5 local 블록에서 로컬 변수를 바인딩

여러 개의 이미지 템플릿 파일에서 공유하는 변수가 아닌, 본 이미지 템플릿에서만 사용할 Local Variable를 정의합니다.

//  linux-rhel.pkr.hcl
//  BLOCK: locals
//  Defines the local variables.

locals {
  build_by      = "Built by: HashiCorp Packer ${packer.version}"
  build_date    = formatdate("YYYY-MM-DD hh:mm ZZZ", timestamp())
  build_version = formatdate("YY.MM", timestamp())
  manifest_date = formatdate("YYYY-MM-DD hh:mm:ss", timestamp())
  manifest_path = "${path.cwd}/manifests/"
  data_source_content = {
    "/ks.cfg" = templatefile("${abspath(path.root)}/data/ks.pkrtpl.hcl", {
      build_username           = var.build_username
      build_password_encrypted = var.build_password_encrypted
      rhsm_username            = var.rhsm_username
      rhsm_password            = var.rhsm_password
      vm_guest_os_language     = var.vm_guest_os_language
      vm_guest_os_keyboard     = var.vm_guest_os_keyboard
      vm_guest_os_timezone     = var.vm_guest_os_timezone
    })
  }
  data_source_command = var.common_data_source == "http" ? "inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks.cfg" : "inst.ks=cdrom:/ks.cfg"
}

2.2.6 Source : 이미지로 빌드하기 위한 Base Information을 정의

이미지를 빌드하기 위해 필요한 기본 정보(vCenter Credential, CPU/Memory 등의 리소스 스펙 등)를 정의합니다.

//  linux-rhel.pkr.hcl
//  BLOCK: source
//  Defines the builder configuration blocks.

source "vsphere-iso" "linux-rhel" {

  // vCenter Server Endpoint Settings and Credentials
  vcenter_server      = var.vsphere_endpoint
  username            = var.vsphere_username
  password            = var.vsphere_password
  insecure_connection = var.vsphere_insecure_connection

  // vSphere Settings
  datacenter = var.vsphere_datacenter
  cluster    = var.vsphere_cluster
  datastore  = var.vsphere_datastore
  folder     = var.vsphere_folder

  // Virtual Machine Settings
  guest_os_type        = var.vm_guest_os_type
  vm_name              = "${var.vm_guest_os_family}-${var.vm_guest_os_name}-${var.vm_guest_os_version}-v${local.build_version}"
  firmware             = var.vm_firmware
  CPUs                 = var.vm_cpu_sockets
  cpu_cores            = var.vm_cpu_cores
  CPU_hot_plug         = var.vm_cpu_hot_add
  RAM                  = var.vm_mem_size
  RAM_hot_plug         = var.vm_mem_hot_add
  cdrom_type           = var.vm_cdrom_type
  disk_controller_type = var.vm_disk_controller_type
  storage {
    disk_size             = var.vm_disk_size
    disk_thin_provisioned = var.vm_disk_thin_provisioned
  }
  network_adapters {
    network      = var.vsphere_network
    network_card = var.vm_network_card
  }
  vm_version           = var.common_vm_version
  remove_cdrom         = var.common_remove_cdrom

....

2.2.7 Build : Source 정보를 기반으로 이미지에 대한 프로비저닝 수행

Build 블록에서 provisioner 기능을 통해 다양한 구성 방식 및 도구(ansible, shell 등)를 사용하여 이미지에 대한 프로비저닝을 수행할 수 있으며, 본 작업 이후 Post-Process 기능으로 추가로 하고자하는 작업을 정의할 수 있습니다. Post-Process에서는 생성한 이미지에 대한 상세 정보를 기록하는 manifest와 같은 작업을 주로 수행합니다.

//  linux-rhel.pkr.hcl
//  BLOCK: build
//  Defines the builders to run, provisioners, and post-processors.

build {
  sources = ["source.vsphere-iso.linux-rhel"]

  provisioner "ansible" {
    playbook_file = "${path.cwd}/ansible/main.yml"
    roles_path    = "${path.cwd}/ansible/roles"
    ansible_env_vars = [
      "ANSIBLE_CONFIG=${path.cwd}/ansible/ansible.cfg"
    ]
    extra_arguments = [
      "--extra-vars", "display_skipped_hosts=false",
      "--extra-vars", "BUILD_USERNAME=${var.build_username}",
      "--extra-vars", "BUILD_SECRET='${var.build_key}'",
      "--extra-vars", "ANSIBLE_USERNAME=${var.ansible_username}",
      "--extra-vars", "ANSIBLE_SECRET='${var.ansible_key}'",
    ]
  }

  post-processor "manifest" {
    output     = "${local.manifest_path}${local.manifest_date}.json"
    strip_path = true
    strip_time = true
    custom_data = {
      ansible_username         = var.ansible_username
      build_username           = var.build_username
      build_date               = local.build_date
      build_version            = local.build_version
      common_data_source       = var.common_data_source
      common_vm_version        = var.common_vm_version
      vm_cpu_cores             = var.vm_cpu_cores
      vm_cpu_sockets           = var.vm_cpu_sockets
      vm_disk_size             = var.vm_disk_size
      vm_disk_thin_provisioned = var.vm_disk_thin_provisioned
      vm_firmware              = var.vm_firmware
      vm_guest_os_type         = var.vm_guest_os_type
      vm_mem_size              = var.vm_mem_size
      vm_network_card          = var.vm_network_card
      vsphere_cluster          = var.vsphere_cluster
      vsphere_datacenter       = var.vsphere_datacenter
      vsphere_datastore        = var.vsphere_datastore
      vsphere_endpoint         = var.vsphere_endpoint
      vsphere_folder           = var.vsphere_folder
      vsphere_iso_path         = "[${var.common_iso_datastore}] ${var.iso_path}/${var.iso_file}"
    }
  }
}

마무리

Packer 관련 오픈소스를 기반으로 Packer에 대한 개념과 구성 요소를 알아보는 포스팅을 마무리하겠습니다. 일반적으로 Terraform + Packer + Ansible 조합과 빌드 및 테스트를 위한 CI(Continuous Integration)를 구성하여 DevOps 파이프라인을 구축하기도 합니다. 본 조합은 Private Cloud(vCenter, OpenStack 등) 뿐만 아니라 Public Cloud(AWS, GCP, Azure 등)에서도 구성이 가능합니다. 뛰어난 데브옵스 엔지니어가 되기 위해서는 반드시 알고 있어야 하는 Packer에 대한 포스팅을 마치겠습니다.

Comments