shell-local 프로비저너
shell-local 프로비저너
shell-local 프로비저너는 Packer가 실행되고 있는 머신에서 셸 스크립트를 실행하는 공식 프로비저너예요. Packer로 프로비저닝하는 원격 또는 게스트 머신이 아니라, 빌드 서버·데스크톱·기타 로컬 머신에서 셸 스크립트를 실행하고 싶을 때 사용해요.
출처: Packer 공식 문서
본문
원격 셸 프로비저너는 원격 머신에서 셸 스크립트를 실행하는 프로비저너예요.
기본 예시 (Basic Example)
아래 예시는 완전히 동작해요. HCL2와 JSON:
source "file" "example" {
content = "example content"
}
build {
source "source.file.example" {
target = "./test_artifact.txt"
}
provisioner "shell-local" {
inline = ["echo foo"]
}
}
{
"builders": [
{
"type": "file",
"name": "example",
"target": "./test_artifact.txt",
"content": "example content"
}
],
"provisioners": [
{
"type": "shell-local",
"inline": ["echo foo"]
}
]
}
구성 참조 (Configuration Reference)
사용 가능한 구성 옵션의 참조는 아래에 나열돼 있어요. 유일하게 필수인 요소는 command예요.
다음 중 정확히 하나가 필요해요:
-
command(string) - 실행할 단일 명령이에요. 임시 파일에 쓰여진 뒤 아래의execute_command호출로 실행돼요. AWS, Azure, Google Compute, OpenStack에서 Windows VM을 빌드 중이고 WinRM으로 인스턴스에 연결할 때 Packer가 사용하는 생성된 비밀번호에 접근하고 싶다면, 템플릿 변수{{.WinRMPassword}}를 사용해 환경 변수로 설정할 수 있어요. -
inline(array of strings) - 실행할 명령들의 배열이에요. 명령들은 개행으로 연결되어 하나의 파일이 되므로 모두 같은 문맥 안에서 실행돼요. 이 덕분에 한 명령에서 디렉터리를 바꾸고 다음 명령에서 그 디렉터리의 무언가를 사용하는 식으로 이어갈 수 있어요. 인라인 스크립트는 Packer가 실행되는 머신 안에서 간단한 작업을 처리하는 가장 쉬운 방법이에요. -
script(string) - 실행할 스크립트의 경로예요. 경로는 절대 또는 상대일 수 있어요. 상대 경로라면 Packer가 실행될 때의 작업 디렉터리를 기준으로 해요. -
scripts(array of strings) - 실행할 스크립트들의 배열이에요. 스크립트들은 지정된 순서대로 실행돼요. 각 스크립트는 격리된 상태로 실행되기 때문에, 한 스크립트의 변수 같은 상태가 다음 스크립트로 이어지지 않아요.
선택 파라미터:
-
env(map of strings) -execute_command이전에 주입할 키/값 쌍의 맵이에요. Packer는 기본적으로 몇몇 환경 변수를 환경에 주입하기도 하는데, 이는 아래 섹션에서 다뤄요. 중복되는env설정은environment_vars설정을 오버라이드해요. -
environment_vars(array of strings) -execute_command이전에 주입할 키/값 쌍의 배열이에요. 형식은key=value여야 해요. Packer는 기본적으로 몇몇 환경 변수를 환경에 주입하기도 하는데, 이는 아래 섹션에서 다뤄요. AWS, Azure, Google Compute, OpenStack에서 Windows VM을 빌드 중이고 WinRM으로 인스턴스에 연결할 때 Packer가 사용하는 생성된 비밀번호에 접근하고 싶다면, 템플릿 변수{{.WinRMPassword}}를 사용해 환경 변수로 설정할 수 있어요. 예를 들어:"environment_vars": "WINRMPASS={{.WinRMPassword}}" -
env_var_format(string) - 제공한environment_vars를 파싱할 때 환경 변수를 올바르게 설정하기 위해 사용하는 문자열 템플릿이에요. Windows 호스트에서는 기본적으로 이 형식이%s=%s &&로, Unix에서는%s='%s'로 설정돼요. 이 형식을 바꿀 필요는 대부분 없겠지만, 필요한 곳의 사용 예시는 아래에서 볼 수 있어요. -
execute_command(array of strings) - 스크립트를 실행하는 데 사용하는 명령이에요. Unix에서는 기본적으로["/bin/sh", "-c", "{{.Vars}}", "{{.Script}}"], Windows에서는["cmd", "/c", "{{.Vars}}", "{{.Script}}"]예요. 템플릿 엔진으로 처리돼요. 두 가지 변수를 사용할 수 있어요: 실행할 스크립트의 경로인Script와, 구성되어 있으면 환경 변수 목록인Vars예요.이 옵션을 설정하기로 했다면, 배열의 첫 요소가 사용하려는 셸 프로그램(예:
"sh")이고 나중 요소 중 하나는{{.Script}}임을 확인하세요.이 옵션은 매우 유연하게 쓸 수 있어요. 자신만의 셸 프로그램, 예를 들어
"/usr/local/bin/zsh"나 심지어"powershell.exe"를 제공할 수도 있어요. 하지만 큰 힘에는 큰 책임이 따르죠 - 이런 명령들은 공식적으로 지원되지 않고, 기본 셸과 다른 셸을 사용하면 환경 변수 같은 것이 동작하지 않을 수 있어요.역호환성을 위해
{{.Command}}를 사용할 수도 있지만,{{.Script}}와 같은 방식으로 디코딩돼요. 명확성을 위해{{.Script}}를 사용하는 것을 권장해요. 단일 명령만 실행하도록 설정해도 Packer는 그것을 임시 파일에 쓰고 스크립트로 실행하거든요.AWS, Azure, Google Compute, OpenStack에서 Windows VM을 빌드 중이고 WinRM으로 인스턴스에 연결할 때 Packer가 사용하는 생성된 비밀번호에 접근하고 싶다면, 템플릿 변수
{{.WinRMPassword}}를 사용해 환경 변수로 설정할 수 있어요. -
inline_shebang(string) -inline으로 지정한 명령을 실행할 때 사용할 shebang 값이에요. 기본적으로/bin/sh -e예요.inline을 사용하지 않는다면 이 구성은 아무 효과가 없어요. 중요: 이 값을 사용자 지정한다면 반드시-e플래그 같은 것을 포함하세요. 그렇지 않으면 개별 단계가 실패해도 프로비저너가 실패하지 않아요. -
only_on(array of strings) -shell-local이 실행될 런타임 운영 체제들의 배열이에요.only_on이 설정된 특정 운영 체제에서만 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 프로비저너를 사용할 의도가 없다면 이 옵션은 무시하세요. -
valid_exit_codes(list of ints) - 스크립트의 유효한 종료 코드예요. 기본적으로0이에요.
모든 프로비저너에 공통인 파라미터:
pause_before(duration) - 실행 전에 해당 시간만큼 대기해요.max_retries(int) - 실패 시 프로비저너가 재시도할 최대 횟수. 기본값은 0. 0이면 오류를 재시도하지 않아요.only(array of string) - 나열된 빌더에 대해서만 이름으로 프로비저너를 실행해요.override(object) - 특정 빌더에 다른 설정으로 빌더를 오버라이드해요. 예:
HCL2에서:
source "null" "example1" {
communicator = "none"
}
source "null" "example2" {
communicator = "none"
}
build {
sources = ["source.null.example1", "source.null.example2"]
provisioner "shell-local" {
inline = ["echo not overridden"]
override = {
example1 = {
inline = ["echo yes overridden"]
}
}
}
}
JSON에서:
{
"builders": [
{
"type": "null",
"name": "example1",
"communicator": "none"
},
{
"type": "null",
"name": "example2",
"communicator": "none"
}
],
"provisioners": [
{
"type": "shell-local",
"inline": ["echo not overridden"],
"override": {
"example1": {
"inline": ["echo yes overridden"]
}
}
}
]
}
timeout(duration) - 프로비저너가 예를 들어1h10m1s나10m보다 오래 걸리면 타임아웃되어 실패해요.
실행 명령 (Execute Command)
많은 새 사용자에게 execute_command는 헷갈리기 쉬워요. 하지만 명령이 어떻게 실행되는지를 사용자 지정한다는 중요한 기능을 제공해요. 가장 흔한 사용 사례는 sudo 비밀번호 프롬프트를 다루는 것이에요. FreeBSD의 tcsh 같은 비POSIX 셸을 사용한다면 이것도 사용자 지정해야 할 수 있어요.
Windows Linux 하위 시스템 (The Windows Linux Subsystem)
shell-local 프로비저너는 로컬 운영 체제의 네이티브 셸에서 명령을 실행할 수 있게 하자는 취지로 설계됐어요. Windows의 경우 기본값에서 이것이 Cmd라고 가정했어요. 하지만 프로비저너 구성에서 execute_command와 use_linux_pathing 옵션을 수정하면 Windows Linux 하위 시스템의 일부로 bash 스크립트를 실행하는 것도 가능해요.
아래 예시는 완전히 동작하는 테스트 구성이에요.
이 기능의 한 가지 제한은 "inline"과 "command" 옵션을 사용할 수 없다는 거예요. "script"나 "scripts" 옵션만 사용하세요.
WSL은 베타 기능이므로 이 도구가 기대한 대로 동작한다는 보장은 없다는 점을 알아두세요. HCL2와 JSON:
source "null" "example" {
communicator = "none"
}
build {
sources = [
"source.null.example"
]
provisioner "shell-local"{
environment_vars = ["PROVISIONERTEST=ProvisionerTest1"]
execute_command = ["bash", "-c", "{{.Vars}} {{.Script}}"]
use_linux_pathing = true
scripts = ["C:/Users/me/scripts/example_bash.sh"]
}
provisioner "shell-local"{
environment_vars = ["PROVISIONERTEST=ProvisionerTest2"]
execute_command = ["bash", "-c", "{{.Vars}} {{.Script}}"]
use_linux_pathing = true
script = "C:/Users/me/scripts/example_bash.sh"
}
}
{
"builders": [
{
"type": "null",
"communicator": "none"
}
],
"provisioners": [
{
"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 구성으로 사용자 지정 환경 변수를 지정할 수 있는 것 외에도, 이 프로비저너는 흔히 유용한 몇몇 환경 변수를 자동으로 정의해요:
PACKER_BUILD_NAME- Packer가 실행 중인 빌드의 이름으로 설정돼요. Packer가 여러 빌드를 만들고 공통 프로비저닝 스크립트에서 그것들을 조금 구분하고 싶을 때 가장 유용해요.PACKER_BUILDER_TYPE- 스크립트가 실행되고 있는 머신을 만드는 데 사용된 빌더의 타입이에요. 특정 빌더로 빌드된 시스템에서만 스크립트의 일부 부분을 실행하고 싶을 때 유용해요.PACKER_HTTP_ADDR- 파일 전송을 위한 HTTP 서버를 제공하는 빌더(hyperv, parallels, qemu, virtualbox, vmware 등)를 사용한다면 이것이 주소로 설정돼요. 프로비저너에서 이 주소를 사용해 큰 파일을 HTTP로 다운로드할 수 있어요. 기본 파일 프로비저너를 사용할 때 속도가 느리다면 유용할 수 있어요. winrm 커뮤니케이터를 사용하는 파일 프로비저너가 이런 어려움을 겪을 수 있어요.
스크립트 안전하게 작성하기 (Safely Writing A Script)
inline 옵션을 쓰든 직접 스크립트나 scripts를 넘기든, shell-local 프로비저너가 어떻게 동작하는지 몇 가지 이해하면 안전하고 쉽게 실행할 수 있어요. 이 이해는 과정에서 많은 시간을 절약해 줘요.
빌더당 한 번 (Once Per Builder)
넘겨준 shell-local 스크립트는 빌더당 한 번 실행돼요. amazon-ebs 빌더와 docker 빌더가 있으면 스크립트가 두 번 실행된다는 뜻이에요. 빌더가 3개면 각 빌더에 대해 3번 실행돼요.
항상 의도적으로 종료하기 (Always Exit Intentionally)
어떤 프로비저너가 실패하면 packer 빌드는 멈추고 모든 중간 아티팩트가 정리돼요.
셸 스크립트라면, 스크립트가 코드 0으로 종료해야 한다는 뜻이에요. 필요할 때 반드시 exit 0을 하도록 각별히 주의하세요.
사용 예시 (Usage Examples)
Windows 호스트 (Windows Host)
Windows에서 .cmd 파일을 실행하는 예시. HCL2와 JSON:
provisioner "shell-local" {
environment_vars = ["SHELLLOCALTEST=ShellTest1"]
scripts = ["./scripts/test_cmd.cmd"]
}
{
"type": "shell-local",
"environment_vars": ["SHELLLOCALTEST=ShellTest1"],
"scripts": ["./scripts/test_cmd.cmd"]
}
"test_cmd.cmd"의 내용:
echo %SHELLLOCALTEST%
Windows에서 인라인 명령을 실행하는 예시. 필요한 사용자 지정: tempfile_extension. HCL2와 JSON:
provisioner "shell-local" {
environment_vars = ["SHELLLOCALTEST=ShellTest2"]
tempfile_extension = ".cmd"
inline = [echo "%SHELLLOCALTEST%"]
}
{
"type": "shell-local",
"environment_vars": ["SHELLLOCALTEST=ShellTest2"],
"tempfile_extension": ".cmd",
"inline": ["echo %SHELLLOCALTEST%"]
}
WSL을 사용해 Windows에서 bash 명령을 실행하는 예시. 필요한 사용자 지정: use_linux_pathing과 execute_command. HCL2와 JSON:
provisioner "shell-local" {
environment_vars = ["SHELLLOCALTEST=ShellTest3"]
execute_command = ["bash", "-c", "{{.Vars}} {{.Script}}"]
use_linux_pathing = true
script = "./scripts/example_bash.sh"
}
{
"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와 JSON:
provisioner "shell-local" {
environment_vars = ["SHELLLOCALTEST=ShellTest4"]
execute_command = ["powershell.exe", "{{.Vars}} {{.Script}}"]
env_var_format = "$env:%s=\"%s\"; "
script = "./scripts/example_ps.ps1"
}
{
"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와 JSON:
provisioner "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"]
}
{
"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에서 셸 스크립트를 실행하는 예시. HCL2와 JSON:
provisioner "shell-local" {
environment_vars = ["PROVISIONERTEST=ProvisionerTest1"]
scripts = ["./scripts/example_bash.sh"]
}
{
"type": "shell-local",
"environment_vars": ["PROVISIONERTEST=ProvisionerTest1"],
"scripts": ["./scripts/example_bash.sh"]
}
Unix에서 셸 스크립트를 "inline"으로 실행하는 예시. HCL2와 JSON:
provisioner "shell-local" {
environment_vars = ["PROVISIONERTEST=ProvisionerTest2"]
inline = ["echo hello", "echo $PROVISIONERTEST"]
}
{
"type": "shell-local",
"environment_vars": ["PROVISIONERTEST=ProvisionerTest2"],
"inline": ["echo hello", "echo $PROVISIONERTEST"]
}
Unix에서 Python 스크립트를 실행하는 예시. HCL2와 JSON:
provisioner "shell-local" {
script = "hello.py"
environment_vars = ["HELLO_USER=packeruser"]
execute_command = [
"/bin/sh",
"-c",
"{{.Vars}} /usr/local/bin/python {{.Script}}"
]
}
{
"type": "shell-local",
"script": "hello.py",
"environment_vars": ["HELLO_USER=packeruser"],
"execute_command": [
"/bin/sh",
"-c",
"{{.Vars}} /usr/local/bin/python {{.Script}}"
]
}
Where "hello.py" contains:
import os
print('Hello, %s!' % os.getenv("HELLO_USER"))