JSON 템플릿용 변수 참조
JSON 템플릿용 변수 참조 (variables reference for JSON templates)
이 주제에서는 JSON으로 작성된 Packer 템플릿에서 사용자 변수(user variables)를 사용하는 방법을 설명해요.
참고: 이 페이지는 옛 스타일의 JSON Packer 템플릿에 대한 내용이에요. JSON 템플릿은 여전히 Packer 코어에서 지원되지만, Packer 코어에 추가된 새 기능은 JSON 템플릿에는 구현되지 않을 수 있어요. Packer를 최대한 잘 사용하려면 편한 때에 HCL 템플릿으로 전환하는 것을 권장해요. 템플릿 업그레이드를 돕기 위해
hcl2_upgrade명령을 작성해 두었어요.
출처: Packer 공식 문서
본문
설명 (Description)
variables 블록을 사용하면 커맨드 라인, 환경 변수, Vault 또는 파일의 변수로 템플릿을 더 구성할 수 있어요. 이를 통해 템플릿을 파라미터화해서 비밀 토큰, 환경별 데이터, 기타 정보를 템플릿 밖에 둘 수 있어요. 이렇게 하면 템플릿의 이식성이 최대화돼요.
사용자 변수를 사용하려면 configuration templates가 어떻게 동작하는지 알아야 해요. 아직 잘 모르겠다면 먼저 그 페이지를 읽어보세요.
사용법 (Usage)
사용자 변수를 설정하려면 템플릿 안의 variables 섹션 안에 정의하거나, 커맨드 라인의 -var 또는 -var-file 플래그를 사용해 정의해야 해요.
사용자 변수가 빈 문자열을 기본값으로 하길 원하더라도, 명시적으로 정의하는 것이 가장 좋아요. 이렇게 명시적으로 선언하면 새로 시작하는 사람들이 템플릿에서 변수로 무엇을 수정할 수 있는지 이해하는 데 걸리는 시간이 줄어들어요.
variables 섹션은 사용자 변수 이름을 기본값에 매핑하는 키/값 매핑이에요. 기본값은 빈 문자열일 수 있어요. 예시는 아래와 같아요.
{
"variables": {
"aws_access_key": "",
"aws_secret_key": ""
},
"builders": [
{
"type": "amazon-ebs",
"access_key": "{{user `aws_access_key`}}",
"secret_key": "{{user `aws_secret_key`}}"
// ...
}
]
}
위 예시에서 템플릿은 aws_access_key와 aws_secret_key 두 사용자 변수를 정의해요. 두 변수는 빈 값으로 기본 설정되죠. 나중에 이 변수들은 Amazon builder의 실제 키를 설정하기 위해 정의한 builder 안에서 사용돼요.
기본값이 null이면 사용자 변수가 필수가 돼요. 즉 사용자가 이 변수의 값을 지정해야 하며, 그렇지 않으면 템플릿 검증이 실패해요.
사용자 변수는 {{user 'variable'}} 형태로 {{user}} 함수를 호출해 사용해요. 이 함수는 템플릿 안의 모든 값과 타입에서 사용할 수 있어요. builders, provisioners에서, 그리고 variables 섹션 밖 어디서든요. 사용자 변수는 템플릿의 나머지 부분 전체에서 전역적으로 사용할 수 있어요.
환경 변수 (Environment variables)
환경 변수는 사용자 변수를 사용해 템플릿 안에서 사용할 수 있어요. env 함수는 사용자 변수의 기본값 안에서만 사용할 수 있어서, 사용자 변수를 환경 변수로 기본 설정할 수 있게 해줘요. 예시는 아래와 같아요.
{
"variables": {
"my_secret": "{{env `MY_SECRET`}}"
}
}
이렇게 하면 "my_secret"이 "MY_SECRET" 환경 변수의 값(그 변수가 없으면 빈 문자열)으로 기본 설정돼요.
왜 다른 곳에서는 환경 변수를 사용할 수 없나요? 사용자 변수는 템플릿에 대한 구성 가능한 입력의 단일 출처(single source)예요. 템플릿 어디서나 환경 변수를 사용할 수 있게 하면 템플릿의 가능한 입력에 대해 사용자를 혼란스럽게 할 것이라고 생각했어요. 환경 변수를 사용자 변수의 기본값 안에서만 허용함으로써, 사용자 변수는 사용자가 packer inspect로 쉽게 발견할 수 있는 템플릿에 대한 유일한 입력 출처로 남아요.
왜 home 변수에는 ~를 사용할 수 없나요? ~는 변수 확장 중에 셸이 평가하는 특수 변수예요. Packer는 셸 안에서 실행되지 않으므로 ~를 확장하지 않아요.
Consul 키 (Consul keys)
Consul 키는 consul_key 함수를 사용해 템플릿 안에서 사용할 수 있어요. 이 함수는 위의 환경 변수와 비슷한 이유로 사용자 변수의 기본값 안에서만 사용할 수 있어요.
{
"variables": {
"soft_versions": "{{ consul_key `my_image/softs_versions/next` }}"
}
}
이렇게 하면 soft_versions이 consul의 my_image/softs_versions/next 키 값으로 기본 설정돼요. consul 설정(address, tokens 등)은 문서에 명시된 대로 환경 변수로 지정해야 해요.
Vault 변수 (Vault variables)
Packer는 HashiCorp Vault에서 읽은 시크릿을 사용자 변수로 템플릿에 추가할 수 있어요. vault 함수는 사용자 변수의 기본값에서만 사용할 수 있어요. 이렇게 하면 사용자 변수를 Vault 시크릿으로 기본 설정할 수 있어요. Vault secrets 엔진과의 통합에 대한 자세한 내용은 Vault 문서를 참고하세요.
v2 kv 엔진 사용 예시:
vault kv put secret/hello foo=world로 vault에 값을 저장했다면, 다음 템플릿 엔진으로 접근할 수 있어요.
{
"variables": {
"my_secret": "{{ vault `/secret/data/hello` `foo`}}"
}
}
그러면 "my_secret"에 "world"가 할당돼요.
v1 kv 엔진 사용 예시:
다음 명령으로 vault에 값을 저장했다면,
vault secrets enable -version=1 -path=secrets kv
vault kv put secrets/hello foo=world
다음 템플릿 엔진으로 접근할 수 있어요.
{
"variables": {
"VAULT_SECRETY_SECRET": "{{ vault `secrets/hello` `foo`}}"
}
}
이 예시는 Vault 경로 secret/data/foo에 접근해 bar 키에 저장된 값을 반환하고, 이를 "my_secret"으로 저장해요. (원문 설명 그대로 유지)
이 기능이 동작하려면 VAULT_TOKEN과 VAULT_ADDR 환경 변수를 유효한 값으로 설정해야 해요. 우리가 사용하는 api 도구는 환경 변수를 통해 Vault 클라이언트를 더 커스텀하게 구성할 수 있게 해줘요.
사용 가능한 환경 변수의 전체 목록은 다음과 같아요.
"VAULT_ADDR"
"VAULT_AGENT_ADDR"
"VAULT_CACERT"
"VAULT_CAPATH"
"VAULT_CLIENT_CERT"
"VAULT_CLIENT_KEY"
"VAULT_CLIENT_TIMEOUT"
"VAULT_SKIP_VERIFY"
"VAULT_NAMESPACE"
"VAULT_TLS_SERVER_NAME"
"VAULT_WRAP_TTL"
"VAULT_MAX_RETRIES"
"VAULT_TOKEN"
"VAULT_MFA"
"VAULT_RATE_LIMIT"
이 변수 각각의 사용 방법에 대한 자세한 문서는 여기에서 찾을 수 있어요.
AWS Secrets Manager 변수
AWS Secrets Manager에서 시크릿을 읽어 사용자 변수로 템플릿 안에서 사용할 수 있어요. aws_secretsmanager 함수는 사용자 변수의 기본값 안에서만 사용할 수 있어서, 사용자 변수를 AWS Secrets Manager 시크릿으로 기본 설정할 수 있어요.
평문 시크릿 (Plaintext secrets)
{
"variables": {
"password": "{{ aws_secretsmanager `globalpassword` }}"
}
}
위 예시에서는 globalpassword 시크릿이 키-값 쌍이 아닌 단일 비-JSON 문자열 값으로 저장되어 있다고 가정해요. aws_secretsmanager 함수는 이 값을 원시 문자열로 반환해요.
단일 키 시크릿 (Single key secrets)
{
"variables": {
"password": "{{ aws_secretsmanager `sample/app/password` }}"
}
}
위 예시에서는 sample/app/password에 키가 하나만 저장되어 있다고 가정해요. 여러 키가 저장되어 있다면 아래처럼 가져오려는 특정 키를 표시해야 해요.
다중 키 시크릿 (Multiple key secrets)
{
"variables": {
"db_password": "{{ aws_secretsmanager `sample/app/passwords` `db` }}",
"api_key": "{{ aws_secretsmanager `sample/app/passwords` `api_key` }}"
}
}
이 함수를 사용하려면 다음 방법 중 하나로 유효한 AWS 자격 증명을 설정해야 해요.
- 환경 변수 (Environment Variables)
- CLI 설정 파일 (CLI Configuration Files)
- 컨테이너 자격 증명 (Container Credentials)
- 인스턴스 프로필 자격 증명 (Instance Profile Credentials)
배열 값 사용하기 (Use array values)
일부 템플릿은 배열 값을 요구해요. 이런 경우에도 템플릿 변수를 사용할 수 있어요. 예를 들어 amazon-ebs builder에는 AMI를 복사할 지역들의 배열을 받는 ami_regions라는 구성 파라미터가 있어요. 이를 콤마(,)로 구분된 지역 목록인 변수를 사용해 파라미터화할 수 있어요. 예:
{
"variables": {
"destination_regions": "us-west-1,us-west-2"
},
"builders": [
{
"ami_name": "packer-qs-{{timestamp}}",
"instance_type": "t2.micro",
"region": "us-east-1",
"source_ami_filter": {
"filters": {
"name": "*ubuntu-xenial-16.04-amd64-server-*",
"root-device-type": "ebs",
"virtualization-type": "hvm"
},
"most_recent": true,
"owners": ["099720109477"]
},
"ami_regions": "{{user `destination_regions`}}",
"ssh_username": "ubuntu",
"type": "amazon-ebs"
}
]
}
변수 설정하기 (Set variables)
템플릿 안에서 사용자 변수를 정의하고 사용하는 방법을 다뤘으니, 다음으로 중요한 점은 실제로 이 변수를 설정하는 방법이에요. Packer는 사용자 변수를 설정하는 두 가지 방법을 제공해요: 커맨드 라인 또는 파일에서요.
CLI
커맨드 라인에서 사용자 변수를 설정하려면 -var 플래그를 packer build(및 일부 다른 명령)의 파라미터로 사용해요. 위의 예시를 계속 이어서, 아래 명령으로 템플릿을 빌드할 수 있어요. 이 명령은 가독성을 위해 여러 줄로 나뉘어 있지만, 물론 한 줄로도 쓸 수 있어요.
$ packer build \
-var 'aws_access_key=foo' \
-var 'aws_secret_key=bar' \
template.json
보다시피 -var 플래그는 여러 변수를 설정하기 위해 여러 번 지정할 수 있어요. 또한 커맨드 라인에서 나중에 설정된 변수는 같은 이름의 이전에 설정된 변수를 덮어써요.
경고: cmd.exe에서 Packer를 호출한다면 변수를 작은따옴표가 아니라 큰따옴표로 감싸야 해요. 예:
packer build -var "aws_secret_key=foo" template.json
파일에서 (From file)
변수는 외부 JSON 파일에서도 설정할 수 있어요. -var-file 플래그는 변수를 값에 매핑하는 키/값 매핑이 담긴 파일을 읽어 그 변수들을 설정해요. JSON 파일 예시는 다음과 같아요.
{
"aws_access_key": "foo",
"aws_secret_key": "bar"
}
키가 변수이고 값이 변수 값인 단일 JSON 객체예요. 이 파일이 variables.json에 있다고 가정하면, 다음 명령으로 템플릿을 빌드할 수 있어요.
On Linux :
$ packer build -var-file=variables.json template.json
On Windows :
packer build -var-file variables.json template.json
-var-file 플래그는 여러 번 지정할 수 있으며 여러 파일의 변수가 읽히고 적용돼요. 예상대로 나중에 지정된 파일에서 읽은 변수가 이전에 설정된 변수를 덮어써요.
-var와 -var-file 플래그를 함께 결합하는 것도 예상대로 동작해요. 명령에서 나중에 설정된 변수가 이전에 설정된 변수를 덮어써요. 예를 들어 위의 variables.json 파일과 함께 다음 명령을 실행하면:
$ packer build \
-var 'aws_access_key=bar' \
-var-file=variables.json \
-var 'aws_secret_key=baz' \
template.json
다음 변수들이 결과로 나와요.
| 변수 | 값 |
|---|---|
aws_access_key |
foo |
aws_secret_key |
baz |
민감한 변수 (Sensitive variables)
민감한 변수를 설정하기 위해 환경을 사용한다면, 그 변수가 Packer 로그에 출력되는 것을 원하지 않을 거예요. Packer 템플릿 안의 "sensitive-variables" 목록에 그 변수들을 추가하면 민감한 변수가 로그에 출력되지 않도록 보장할 수 있어요.
{
"variables": {
"my_secret": "{{env `MY_SECRET`}}",
"not_a_secret": "plaintext",
"foo": "bar"
},
"sensitive-variables": ["my_secret", "foo"],
...
}
위 코드 조각은 "sensitive-variables"를 설정하지 않았을 때와 정확히 동일하게 동작해요. 단, Packer UI와 로그가 "bar"의 모든 인스턴스와 "my_secret"의 값을 <sensitive>로 대체한다는 점만 달라요. 이렇게 하면 실수로 시크릿을 평문으로 로그에 출력하지 않는다는 확신을 가질 수 있어요.
레시피 (Recipes)
변수 값에 따라 provisioner 단계를 조건부로 만들기
Packer 템플릿에는 변수 값에 따라 provisioner 단계를 조건부로 만드는 특정 문법이 없어요. 하지만 실행하는 명령 안에서 변수를 참조해 이 작업을 할 수 있어요. 예를 들어 do_nexpose_scan 변수가 비어 있지 않을 때만 shell-local provisioner가 실행되도록 만드는 방법은 다음과 같아요.
{
"type": "shell-local",
"command": "if [ ! -z \"{{user `do_nexpose_scan`}}\" ]; then python -u trigger_nexpose_scan.py; fi"
}
HOME 변수 사용하기 (Using HOME Variable)
$HOME 변수를 사용하려면 Packer에 home 변수를 만들 수 있어요.
{
"variables": {
"home": "{{env `HOME`}}"
}
}
그러면 이 변수를 템플릿의 나머지 부분에서 사용할 수 있어요. 예:
{
"builders": [
{
"type": "google",
"account_file": "{{ user `home` }}/.secrets/gcp-{{ user `env` }}.json"
}
]
}