커스텀 post-processor 만들기

커스텀 post-processor 만들기

Packer post-processor는 한 아티팩트를 다른 아티팩트로 변환해요. 예를 들어 post-processor가 파일을 압축하거나 업로드할 수 있죠.

출처: Packer 공식 문서

본문

압축 예시에서 변환은 파일 세트를 담은 아티팩트를 받아 그 파일들을 압축하고, 단일 파일(압축된 아카이브)만 담은 새 아티팩트를 반환하는 방식이에요. 업로드 예시에서는 파일 세트를 담은 아티팩트를 받아 그 파일들을 업로드하고, 업로드 URL이라는 단일 ID를 담은 아티팩트를 반환해요.

Post-processor 플러그인은 packer.PostProcessor 인터페이스를 구현하고, plugin.ServePostProcessor 함수로 서빙돼요.

이 글은 커스텀 post-processor를 구현하고 서빙하는 방법을 설명해요. post-processor가 HashiCorp Cloud Platform(HCP) Packer를 지원하게 하려면 HCP Packer 지원 문서도 함께 검토해 주세요.

경고: 이 글은 Packer와 Packer 플러그인에 대한 깊은 지식을 요구하는 고급 주제예요.

시작하기 전에 (Before You Begin)

개발을 시작하기 전에 다음 자료를 검토하는 걸 권장해요:

  • 플러그인 개발 - 개요
  • Go 언어. 커스텀 플러그인은 Go로 작성해야 하므로, 이 가이드는 그 언어에 익숙하다고 가정해요.

인터페이스 (The Interface)

post-processor를 위해 구현해야 하는 인터페이스는 packer.PostProcessor 인터페이스예요. 참고를 위해 아래에 재현해 뒀어요. 소스 코드에 있는 실제 인터페이스는 각 메서드가 무엇을 해야 하는지 설명하는 기본 문서도 담고 있어요.

type PostProcessor interface {
  ConfigSpec() hcldec.ObjectSpec
  Configure(interface{}) error
  PostProcess(context.Context, Ui, Artifact) (a Artifact, keep, mustKeep bool, err error)
}

"ConfigSpec" 메서드

이 메서드는 hcldec.ObjectSpec을 반환해요. 이는 Packer와 함께 HCL2 템플릿을 사용하는 데 필요한 spec이에요. 이 함수를 사용·구현하는 방법은 object spec 문서를 확인해 주세요.

"Configure" 메서드

각 post-processor의 Configure 메서드는 post-processor를 구성하기 위해 빌드 프로세스 초기에 호출돼요. 구성은 원시 interface{}로 전달돼요. Configure 메서드는 이 구성을 내부 구조로 번역하고, 검증하며, 오류가 있으면 반환하는 책임이 있어요.

interface{}를 의미 있는 구조로 디코딩하려면 mapstructure 라이브러리를 권장해요. Mapstructure는 interface{}를 받아 임의로 복잡한 struct로 디코딩해요. 오류가 있으면 configure 메서드에서 바로 반환할 수 있을 만큼 사람 친화적인 오류를 만들어내요.

적극적으로 강제되지는 않지만 Configure 메서드를 실행해서 **부작용(side effect)**이 생기면 안 돼요. 특히 파일을 만들거나, 네트워크 연결을 만들거나 하면 안 돼요. Configure의 목적은 오로지 내부 상태를 설정하고 구성을 최대한 검증하는 것이에요.

Configure가 실행됐다고 해서 PostProcess가 실행될 거란 뜻은 아니에요. 예를 들어 packer validate는 구성을 검증하기 위해 Configure를 실행하지만, 실제로 빌드를 실행하지는 않아요.

"PostProcess" 메서드

PostProcess 메서드가 진짜 작업이 일어나는 곳이에요. PostProcess는 하나의 packer.Artifact 구현을 받아 다른 것으로 변환하는 책임이 있어요. PostProcess 호출은 언제든 취소될 수 있어요. 취소는 context struct의 done 채널(<-ctx.Done())이 차단 해제될 때 트리거돼요.

여기서 "변환(transform)"은 기존의 packer.Artifact 값 자체를 실제로 수정한다는 뜻이 아니에요. 아티팩트의 내용을 가져와 그것으로 새 아티팩트를 만든다는 의미예요. 예를 들어 파일을 압축하는 "compress" post-processor를 만든다면, 원래 아티팩트의 Files()를 가져와 압축하고, 단일 파일(압축된 아카이브)을 담은 새 아티팩트를 만드는 변환이 될 거예요.

이 메서드의 결과 시그니처는 (Artifact, bool, bool, error)예요. 각 반환값을 설명하면:

  • Artifact - 오류가 없다면 새로 만들어진 아티팩트.
  • bool - keep이 true이면 입력 아티팩트를 강제로 유지해요. 기본적으로 사용자가 보통 중간 아티팩트를 원하지 않기 때문에 Packer는 모든 입력 아티팩트를 삭제해요. 하지만 일부 post-processor는 이전 아티팩트가 존재하는 것에 의존해요. 이 값이 true이면 packer가 아티팩트를 계속 유지하도록 강제해요.
  • bool - forceOverride가 true이면 keep_input_artifact에 대한 사용자 입력은 무시되고, keep에 설정된 값에 따라 아티팩트가 유지되거나 폐기돼요.
  • error - 어떤 방식으로든 오류가 있으면 nil이 아니에요. 이 경우 다른 두 반환값은 무시돼요.