shell-local post-processor
shell-local post-processor
shell-local post-processor는 post-processing 단계에서 스크립트를 로컬에서 실행해요. Shell local은 packer의 출력물과 변수로 어떤 작업을 실행하는 것을 자동화하는 편리한 방법을 제공해요.
출처: Packer 공식 문서
본문
기본 예시 (Basic example)
아래 예시는 완전히 동작하는 자족적인 빌드예요.
HCL2
source "file" "example" {
content = "example content"
}
build {
source "source.file.example" {
target = "./test_artifact.txt"
}
post-processor "shell-local" {
inline = ["echo foo"]
}
}
JSON
{
"builders": [
{
"type": "file",
"name": "example",
"target": "./test_artifact.txt",
"content": "example content"
}
],
"post-processors": [
{
"type": "shell-local",
"inline": ["echo foo"]
}
]
}
구성 레퍼런스 (Configuration Reference)
사용 가능한 구성 옵션의 레퍼런스가 아래에 정리돼 있어요. 필수 요소는 "inline" 또는 "script" 중 하나뿐이고, 다른 모든 옵션은 선택 사항이에요.
정확히 하나 는 필수예요:
-
command(string) - 실행할 단일 명령어. 임시 파일에 기록된 뒤 아래execute_command호출로 실행돼요. -
inline(문자열 배열) - 실행할 명령어들의 배열. 명령어들은 새줄로 연결되어 단일 파일이 되므로 모두 같은 컨텍스트에서 실행돼요. 그래서 한 명령어에서 디렉터리를 바꾸고 다음 명령어에서 그 디렉터리 안의 것을 사용하는 식으로 할 수 있어요. Inline 스크립트는 머신 안에서 간단한 작업을 해내는 가장 쉬운 방법이에요. -
script(string) - 실행할 스크립트의 경로. 절대 경로나 상대 경로일 수 있어요. 상대 경로라면 Packer가 실행될 때의 작업 디렉터리를 기준으로 해요. -
scripts(문자열 배열) - 실행할 스크립트들의 배열. 지정된 순서대로 실행돼요. 각 스크립트는 격리된 상태로 실행되므로, 한 스크립트의 변수 같은 상태는 다음 스크립트로 이어지지 않아요.
선택 매개변수:
-
env(문자열 맵) -execute_command전에 주입할 키/값 쌍의 맵. Packer는 기본적으로도 일부 환경 변수를 환경에 주입하는데, 이는 아래 절에서 다뤄요. 중복된env설정은environment_vars설정을 재정의해요. -
environment_vars(문자열 배열) -execute_command전에 주입할 키/값 쌍의 배열. 형식은key=value여야 해요. Packer는 기본적으로도 일부 환경 변수를 환경에 주입하는데, 이는 아래 절에서 다뤄요. -
env_var_format(string) - 여러분이 제공한environment_vars를 파싱할 때, 환경 변수를 올바르게 설정하도록 보장하는 문자열 템플릿이에요. Windows 호스트에서는 기본적으로set %s=%s &&이고, Unix에서는%s='%s'예요. 이 형식을 바꿔야 할 일은 아마 없을 테지만, 필요한 곳의 사용 예시는 아래에서 볼 수 있어요. -
execute_command(문자열 배열) - 스크립트를 실행하는 데 사용하는 명령어. 기본적으로 *nix 시스템에서는 다음과 같아요:
["/bin/sh", "-c", "{{.Vars}} {{.Script}}"]
Windows에서는 execute_command 기본값이 다음과 같아요:
["cmd", "/V", "/C", "{{.Vars}}", "call", "{{.Script}}"]
이 값은 템플릿 엔진으로 취급돼요. 사용 가능한 변수는 여러 가지가 있어요: Script는 실행할 스크립트의 경로이고, Vars는 설정된 경우 environment_vars의 목록이에요. 추가로 build 템플릿 함수를 사용해 생성된 데이터에 저장된 변수들에 접근할 수 있어요. 이 옵션을 설정하기로 했다면 배열의 첫 번째 요소가 사용하려는 셸 프로그램(예: "sh", "/usr/local/bin/zsh", 심지어 "powershell.exe")인지 확인하세요. 다만 셸 명령어 언어의 한 종류가 아닌 것은 명시적으로 지원되지 않고 Packer 내부의 가정 때문에 깨질 수 있어요. shell-local을 PowerShell이나 다른 Windows 명령어에 사용하려 한다면, 환경 변수가 여러분의 환경에 제대로 설정되지 않을 거라는 점도 알아두세요.
호환성을 위해 execute_command는 문자열 배열 대신 문자열도 받아들여요. 단일 문자열이나 요소가 하나뿐인 문자열 배열이 제공되면, Packer는 ["sh", "-c"] 배열에 여러분의 execute_command를 덧붙여 이전 동작을 재현해요. 예를 들어 "execute_command": "foo bar"로 설정하면 Packer가 실행하는 최종 execute_command는 ["sh", "-c", "foo bar"]예요. "execute_command": ["foo", "bar"]로 설정하면 최종 execute_command는 ["foo", "bar"]로 유지돼요.
다시 말하지만 이것은 호환성 수정으로만 제공되며, execute_command를 문자열 배열로 설정할 것을 강력히 권장해요.
-
inline_shebang(string) -inline으로 지정된 명령어를 실행할 때 사용할 shebang 값. 기본값은/bin/sh -e예요.inline을 쓰지 않는다면 이 구성은 효과가 없어요. 중요: 이 값을 직접 바꾸면-e플래그 같은 것을 꼭 포함하세요. 그렇지 않으면 개별 단계가 실패해도 provisioner가 실패로 처리되지 않아요. -
keep_input_artifact(boolean) - 대부분의 다른 post-processor와 달리 shell-local post-processor에서는 keep_input_artifact 옵션이 효과가 없어요. shell-local post-processor는 받은 아티팩트를 그저 앞으로 전달하기만 하므로, Packer는 shell-local에 대해 항상 입력 아티팩트를 유지해요. 생성한 파일(들)이 입력 아티팩트를 대체하기를 원한다면, shell-local 프로세서가 실행된 뒤에 artifice post-processor로 입력 아티팩트를 덮어쓰면 돼요. -
only_on(문자열 배열) -shell-local이 실행될 런타임 운영체제들의 배열. 이 옵션으로 특정 운영체제에서만shell-local을 실행할 수 있어요. 기본적으로only_on이 설정되지 않으면 shell-local은 항상 실행돼요. -
use_linux_pathing(bool) - Windows 호스트에만 관련돼요. Windows Subsystem for Linux 기능이 활성화된 Windows 환경에서 Packer를 실행 중이고, Cmd 스크립트가 아니라 bash 스크립트를 호출하고 싶다면 이 플래그를 true로 설정해야 해요. 그러면 Packer가 Windows 경로가 아니라 Linux 서브시스템 경로를 스크립트에 사용하도록 지시해요. (예:C:/path/to/your/file대신/mnt/c/path/to/your/file). 이 기능을 사용하는 방법에 대한 자세한 지침은 아래 예시를 참고해 주세요. Windows 호스트가 아니거나 bash 스크립트를 실행하기 위해 shell-local post-processor를 쓰지 않을 거라면 이 옵션은 무시해 주세요. 이 플래그를 true로 설정해도script를 제공할 때는 스크립트의 표준 Windows 경로를 제공해야 해요. 이것은 베타 기능이에요. -
valid_exit_codes(int 목록) - 스크립트의 유효한 종료 코드. 기본값은0이에요.
실행 명령어 (Execute Command)
많은 새 사용자에게 execute_command는 혼란스러워요. 하지만 여기엔 중요한 기능이 있어요: 명령어가 실행되는 방식을 사용자 정의하는 것. 가장 흔한 사용 사례는 sudo 비밀번호 프롬프트를 다루는 거예요. FreeBSD의 tcsh 같은 비-POSIX 셸을 쓴다면 이것도 사용자 정의해야 할 수 있어요.
Windows Linux Subsystem (The Windows Linux Subsystem)
shell-local post-processor는 로컬 운영체제의 네이티브 셸에서 명령어를 실행하도록 설계됐어요. Windows에서는 기본값이 Cmd라고 가정했어요. 하지만 post-processor 구성에서 execute_command와 use_linux_pathing 옵션을 수정하면 shell-local post-processor에서 Windows Linux Subsystem의 일부로 bash 스크립트를 실행하는 것도 가능해요.
아래 예시는 완전히 동작하는 테스트 구성이에요.
한 가지 제한은 "inline"과 "command" 옵션을 쓸 수 없다는 거예요. "script"나 "scripts" 옵션만 사용해 주세요.
이 기능은 기본 WSL도 여전히 베타이므로 이 기능도 베타 단계라는 점을 알아두세요. 그 결과 몇 가지 제한이 있어요. 예를 들어 Packer와 실행하려는 스크립트가 둘 다 C 드라이브에 있어야 동작할 가능성이 높아요.
HCL2
source "null" "example" {
communicator = "none"
}
build {
sources = [
"source.null.example"
]
post-processor "shell-local"{
environment_vars = ["PROVISIONERTEST=ProvisionerTest1"]
execute_command = ["bash", "-c", "{{.Vars}} {{.Script}}"]
use_linux_pathing = true
scripts = ["C:/Users/me/scripts/example_bash.sh"]
}
post-processor "shell-local"{
environment_vars = ["PROVISIONERTEST=ProvisionerTest2"]
execute_command = ["bash", "-c", "{{.Vars}} {{.Script}}"]
use_linux_pathing = true
script = "C:/Users/me/scripts/example_bash.sh"
}
}
JSON
{
"builders": [
{
"type": "null",
"communicator": "none"
}
],
"post-processors": [
{
"type": "shell-local",
"environment_vars": ["PROVISIONERTEST=ProvisionerTest1"],
"execute_command": ["bash", "-c", "{{.Vars}} {{.Script}}"],
"use_linux_pathing": true,
"scripts": ["C:/Users/me/scripts/example_bash.sh"]
},
{
"type": "shell-local",
"environment_vars": ["PROVISIONERTEST=ProvisionerTest2"],
"execute_command": ["bash", "-c", "{{.Vars}} {{.Script}}"],
"use_linux_pathing": true,
"script": "C:/Users/me/scripts/example_bash.sh"
}
]
}
기본 환경 변수 (Default Environmental Variables)
environment_vars 구성으로 커스텀 환경 변수를 지정할 수 있을 뿐 아니라, provisioner는 몇 가지 흔히 유용한 환경 변수도 자동으로 정의해요:
-
PACKER_BUILD_NAME은 Packer가 실행 중인 빌드의 이름으로 설정돼요. Packer가 여러 빌드를 만들 때 공통 프로비저닝 스크립트에서 그것들을 조금 구분하고 싶을 때 가장 유용해요. -
PACKER_BUILDER_TYPE은 스크립트가 실행 중인 머신을 만드는 데 사용된 builder의 타입이에요. 특정 builder로 만든 시스템에서 스크립트의 일부만 실행하고 싶을 때 유용해요.
스크립트 안전하게 작성하기 (Safely Writing A Script)
inline 옵션을 쓰든, 직접 script나 scripts를 전달하든, shell-local post-processor가 어떻게 동작하는지 몇 가지를 이해하는 게 안전하고 쉽게 실행하는 데 중요해요. 이 이해는 작업 시간을 크게 아껴줄 거예요.
builder당 한 번 (Once Per Builder)
전달하는 shell-local 스크립트(들)은 builder당 한 번 실행돼요. 즉 amazon-ebs builder와 docker builder가 있다면 스크립트가 두 번 실행돼요. builder가 3개면 각 builder마다 총 3번 실행돼요.
빌드 아티팩트와 상호작용 (Interacting with Build Artifacts)
빌드 아티팩트와 상호작용하려면 manifest post-processor를 사용하면 돼요. 이 post-processor는 각 builder가 실행된 후 builder가 만든 파일 목록을 json 파일에 기록해요.
예를 들어 file builder의 파일을 tarball로 패키징하고 싶다면 이렇게 쓸 수 있어요:
JSON
{
"builders": [
{
"content": "Lorem ipsum dolor sit amet",
"target": "dummy_artifact",
"type": "file"
}
],
"post-processors": [
[
{
"output": "manifest.json",
"strip_path": true,
"type": "manifest"
},
{
"inline": [
"jq \".builds[].files[].name\" manifest.json | xargs tar cfz artifacts.tgz"
],
"type": "shell-local"
}
]
]
}
HCL2
source "file" "example" {
content = "Lorem ipsum dolor sit amet"
target = "dummy_artifact.txt"
}
build {
sources = [
"source.file.example"
]
post-processor "manifest" {
output = "manifest.json"
strip_path = true
}
post-processor "shell-local" {
inline = [
"jq \".builds[].files[].name\" manifest.json | xargs tar cfz artifacts.tgz"
]
}
}
이 예시는 jq 도구로 manifest 파일에서 모든 파일 이름을 추출해 tar에 전달해요.
항상 의도적으로 종료하기 (Always Exit Intentionally)
어떤 post-processor든 실패하면 packer build는 멈추고 모든 중간 아티팩트를 정리해요.
셸 스크립트의 경우 스크립트가 반드시 0 코드로 종료해야 한다는 뜻이에요. 필요할 때 exit 0을 하는 데 매우 주의해야 해요.
사용 예시 (Usage Examples)
Windows 호스트 (Windows Host)
Windows에서 .cmd 파일을 실행하는 예시:
HCL2
post-processor "shell-local" {
environment_vars = ["SHELLLOCALTEST=ShellTest1"]
scripts = ["./scripts/test_cmd.cmd"]
}
JSON
{
"type": "shell-local",
"environment_vars": ["SHELLLOCALTEST=ShellTest1"],
"scripts": ["./scripts/test_cmd.cmd"]
}
test_cmd.cmd의 내용:
echo %SHELLLOCALTEST%
Windows에서 inline 명령어를 실행하는 예시: 필요한 사용자 정의는 tempfile_extension이에요.
HCL2
post-processor "shell-local" {
environment_vars = ["SHELLLOCALTEST=ShellTest2"],
tempfile_extension = ".cmd",
inline = ["echo %SHELLLOCALTEST%"]
}
JSON
{
"type": "shell-local",
"environment_vars": ["SHELLLOCALTEST=ShellTest2"],
"tempfile_extension": ".cmd",
"inline": ["echo %SHELLLOCALTEST%"]
}
WSL을 사용해 Windows에서 bash 명령어를 실행하는 예시: 필요한 사용자 정의는 use_linux_pathing과 execute_command예요.
HCL2
post-processor "shell-local" {
environment_vars = ["SHELLLOCALTEST=ShellTest3"],
execute_command = ["bash", "-c", "{{.Vars}} {{.Script}}"]
use_linux_pathing = true
script = "./scripts/example_bash.sh"
}
JSON
{
"type": "shell-local",
"environment_vars": ["SHELLLOCALTEST=ShellTest3"],
"execute_command": ["bash", "-c", "{{.Vars}} {{.Script}}"],
"use_linux_pathing": true,
"script": "./scripts/example_bash.sh"
}
example_bash.sh의 내용:
#!/bin/bash
echo $SHELLLOCALTEST
Windows에서 PowerShell 스크립트를 실행하는 예시: 필요한 사용자 정의는 env_var_format과 execute_command예요.
HCL2
post-processor "shell-local" {
environment_vars = ["SHELLLOCALTEST=ShellTest4"]
execute_command = ["powershell.exe", "{{.Vars}} {{.Script}}"]
env_var_format = "$env:%s=\"%s\"; "
script = "./scripts/example_ps.ps1"
}
JSON
{
"type": "shell-local",
"environment_vars": ["SHELLLOCALTEST=ShellTest4"],
"execute_command": ["powershell.exe", "{{.Vars}} {{.Script}}"],
"env_var_format": "$env:%s=\"%s\"; ",
"script": "./scripts/example_ps.ps1"
}
Windows에서 PowerShell 스크립트를 "inline"으로 실행하는 예시: 필요한 사용자 정의는 env_var_format, tempfile_extension, execute_command예요.
HCL2
post-processor "shell-local" {
tempfile_extension = ".ps1"
environment_vars = ["SHELLLOCALTEST=ShellTest5"]
execute_command = ["powershell.exe", "{{.Vars}} {{.Script}}"]
env_var_format = "$env:%s=\"%s\"; "
inline = ["write-output $env:SHELLLOCALTEST"]
}
JSON
{
"type": "shell-local",
"tempfile_extension": ".ps1",
"environment_vars": ["SHELLLOCALTEST=ShellTest5"],
"execute_command": ["powershell.exe", "{{.Vars}} {{.Script}}"],
"env_var_format": "$env:%s=\"%s\"; ",
"inline": ["write-output $env:SHELLLOCALTEST"]
}
Unix 호스트 (Unix Host)
Unix에서 Shell 스크립트를 실행하는 예시:
HCL2
post-processor "shell-local" {
environment_vars = ["PROVISIONERTEST=ProvisionerTest1"]
scripts = ["./scripts/example_bash.sh"]
}
JSON
{
"type": "shell-local",
"environment_vars": ["PROVISIONERTEST=ProvisionerTest1"],
"scripts": ["./scripts/example_bash.sh"]
}
Unix에서 bash "inline"을 실행하는 예시:
HCL2
post-processor "shell-local" {
environment_vars = ["PROVISIONERTEST=ProvisionerTest2"]
inline = ["echo hello", "echo $PROVISIONERTEST"]
}
JSON
{
"type": "shell-local",
"environment_vars": ["PROVISIONERTEST=ProvisionerTest2"],
"inline": ["echo hello", "echo $PROVISIONERTEST"]
}
Unix에서 Python 스크립트를 실행하는 예시:
HCL2
post-processor "shell-local" {
script = "hello.py"
environment_vars = ["HELLO_USER=packeruser"]
execute_command = [
"/bin/sh",
"-c",
"{{.Vars}} /usr/local/bin/python {{.Script}}"
]
}
JSON
{
"type": "shell-local",
"script": "hello.py",
"environment_vars": ["HELLO_USER=packeruser"],
"execute_command": [
"/bin/sh",
"-c",
"{{.Vars}} /usr/local/bin/python {{.Script}}"
]
}
여기서 "hello.py"는 다음을 담고 있어요:
import os
print('Hello, %s!' % os.getenv("HELLO_USER"))