잡 스펙의 `template` 블록
잡 스펙의 template 블록
template 블록은 템플릿 렌더러 인스턴스를 만든다 (인스턴스화) 해요. 이것은 환경 변수, Consul 데이터, Vault 시크릿 또는 일반 구성으로 채워진 구성 파일을 Nomad 태스크 안에 보내는 편리한 방법을 만들어요.
출처: 문서
본문
| 배치 | job -> group -> task -> **template** |
|---|
job "docs" {
group "example" {
task "server" {
template {
source = "local/redis.conf.tpl"
destination = "local/redis.conf"
change_mode = "signal"
change_signal = "SIGINT"
}
}
}
}
Nomad는 Go template과 Consul Template이라는 도구를 활용하는데, 후자는 Consul과 Vault에서 데이터를 가져오는 데 사용할 수 있는 새로운 함수 집합을 추가해요. Nomad 템플릿은 Nomad의 런타임 환경 변수, 노드 속성과 메타데이터, Nomad 서비스 등록, Nomad 변수를 참조할 수 있어요. 템플릿은 워크로드에 환경 변수를 제공하는 데도 사용할 수 있어요.
API 템플릿 함수의 전체 목록은 Consul Template 문서를 참고하세요. Go 템플릿 소개는 Learn Go Template Syntax 가이드를 참고하세요.
매개변수 (Parameters)
-
change_mode(string: "restart")- 렌더링된 템플릿이 변경될 때 Nomad가 취해야 할 동작. Nomad는 항상 템플릿의 새 내용을 지정된 대상(destination)에 써요. 다음 가능한 값들은 템플릿을 디스크에 쓴 후 Nomad의 동작을 설명해요. -
change_signal(string: "")-"SIGUSR1"또는"SIGINT"같은 문자열로 태스크에 보낼 신호.change_mode가signal이면 이 옵션은 필수예요. -
change_script([ChangeScript](https://developer.hashicorp.com/nomad/docs/job-specification/change_script): nil)- 템플릿 변경 시 트리거되는 스크립트를 구성.change_mode가script이면 이 옵션은 필수예요. -
data(string: "")- 실행할 원시 템플릿.source또는data중 하나는 반드시 지정해야 하지만 둘 다는 안 돼요. 작은 템플릿에 유용하지만, 큰 템플릿에는source를 권장해요. -
destination(string: <required>)- 결과 템플릿이 렌더링되어야 하는 위치. 태스크 작업 디렉터리를 기준으로 해요. 파일시스템 격리가 없는 드라이버(예:raw_exec) 또는 태스크 작업 디렉터리에 chroot를 만드는 드라이버(예:exec)만NOMAD_ALLOC_DIR,NOMAD_TASK_DIR,NOMAD_SECRETS_DIR밖에 템플릿을 렌더링할 수 있어요.destination이 태스크 드라이버와 어떻게 상호작용하는지에 대한 자세한 내용은 Filesystem internals 문서를 참고하세요. 가능하면NOMAD_SECRETS_DIR은noexec로 마운트되므로 렌더링된 템플릿은 자체 실행 스크립트로 사용할 수 없다는 점에 유의하세요. -
env(bool: false)- 템플릿을 태스크의 환경 변수로 다시 읽어야 함을 지정(예시). 변경 시 환경을 업데이트하려면change_mode를restart로 설정해야 해요.change_mode가signal일 때env를 설정하면 검증 오류가 반환돼요.change_mode가noop일 때env를 설정하는 것은 허용되지만 태스크의 환경 변수를 업데이트하지는 않아요. -
error_on_missing_key(bool: false)- 맵에 존재하지 않는 맵 키를 인덱싱하려고 할 때 템플릿이 어떻게 동작할지 지정.true이면 템플릿 엔진이 오류를 반환하고 태스크가 실패해요.false이면 템플릿 엔진이 아무것도 하지 않고 템플릿 실행을 계속해요. 출력되면 인덱스 연산의 결과는 문자열 "".
-
left_delimiter(string: "{{")- 템플릿에 사용할 왼쪽 구분자. 기본값은 "{{"이며 일부 템플릿의 경우 출력 파일 자체와 충돌하지 않는 다른 구분자를 사용하는 것이 더 쉬울 수 있어요. -
perms(string: "644")- 렌더링된 템플릿의 권한. 파일 권한은 Unix 파일 권한rwxrwxrwx의 8진수로 주어져요. -
uid(int: nil)- 렌더링된 템플릿 소유자의 사용자 ID. 음수이거나 지정되지 않으면(nil) Nomad 에이전트 사용자의 ID가 사용돼요.
주의: Unix 기반 시스템에서만 동작해요. docker나 podman 같은 컨테이너화된 드라이버를 사용할 때는 컨테이너 안의 그룹과 사용자가 호스트 시스템과 다른 ID를 가질 수 있으므로 주의하세요. 이 기능은 Docker Desktop에서도 동작하지 않아요.
gid(int: nil)- 렌더링된 템플릿 소유자의 그룹 ID. 음수이거나 지정되지 않으면(nil) Nomad 에이전트 그룹의 ID가 사용돼요.
주의: Unix 기반 시스템에서만 동작해요. docker나 podman 같은 컨테이너화된 드라이버를 사용할 때는 컨테이너 안의 그룹과 사용자가 호스트 시스템과 다른 ID를 가질 수 있으므로 주의하세요. 이 기능은 Docker Desktop에서도 동작하지 않아요.
-
once(bool: false)- 클라이언트가 템플릿이 렌더링될 때까지 기다린 후 템플릿에 지정된 의존성을 더 이상 감시하지 않음을 지정. 업데이트할 필요가 없는 템플릿에 유용해요. once 모드는 이 템플릿의 대기/정지(quiescence) 타이머를 암시적으로 비활성화하고 변경 모드/신호/스크립트 설정을 무시해요. -
right_delimiter(string: "}}")- 템플릿에 사용할 오른쪽 구분자. 기본값은 "}}"이며 일부 템플릿의 경우 출력 파일 자체와 충돌하지 않는 다른 구분자를 사용하는 것이 더 쉬울 수 있어요. -
source(string: "")- 렌더링할 템플릿의 경로.source또는data중 하나는 반드시 지정해야 하지만 둘 다는 안 돼요. 이 소스는artifact리소스를 사용해 가져올 수 있어요. 템플릿은 태스크가 시작되기 전에 태스크 작업 디렉터리에 존재해야 해요. 예를 들어 소스가 Docker 컨테이너 안에 있는 템플릿을 참조하는 것은 불가능해요. -
splay(string: "5s")- 변경 모드를 호출하기 전에 0ms와 주어진 splay 값 사이의 무작위 시간을 기다림."30s"또는"1h"같은 레이블 접미사로 지정되며, 모든 태스크 인스턴스가 동시에 재시작되는 thundering herd 문제를 방지하는 데 자주 사용돼요. -
wait(Code: nil)- 템플릿을 렌더링하기 전에 Consul 클러스터가 일관된 상태에 도달할 때까지 기다리는 최소·최대 시간. Consul에 대한 네트워크 연결이 저하된 시스템에서 활성화하면 템플릿이 렌더링되는 횟수가 줄어들므로 유용해요. 이 설정은client.template.wait_bounds로 덮어쓸 수 있어요. 템플릿 구성의min이client.template.wait_bounds.min보다 낮거나max가client.template.wait_bounds.max보다 크면 클라이언트의 범위가 적용되고 템플릿wait가 템플릿 엔진으로 보내지기 전에 조정돼요.
wait {
min = "5s"
max = "10s"
}
vault_grace(string: "15s")- Deprecated
예시 (Examples)
다음 예시는 template 블록만 보여줘요. template 블록은 위에 나열된 배치에서만 유효하다는 점을 기억하세요.
인라인 템플릿 (Inline template)
이 예시는 인라인 템플릿을 사용해 파일을 디스크에 렌더링해요. 이 파일은 Consul의 여러 키의 변경을 감시해요:
template {
data = "---\nkey: {{ key \"service/my-key\" }}"
destination = "local/file.yml"
}
여러 줄 템플릿에는 heredoc을 사용할 수도 있어요. 예:
template {
data = <<EOH
---
bind_port: {{ env "NOMAD_PORT_db" }}
scratch_dir: {{ env "NOMAD_TASK_DIR" }}
node_id: {{ env "node.unique.id" }}
service_key: {{ key "service/my-key" }}
EOH
destination = "local/file.yml"
}
원격 템플릿 (Remote template)
이 예시는 artifact 블록을 사용해 템플릿 엔진에 전달하기 전에 입력 템플릿을 다운로드해요:
artifact {
source = "https://example.com/file.yml.tpl"
destination = "local/file.yml.tpl"
}
template {
source = "local/file.yml.tpl"
destination = "local/file.yml"
}
태스크 meta 값 (Task meta values)
태스크의 meta 구성에서 값을 렌더링하려면 meta 변수 이름의 환경 변수 형태를 사용하세요.
meta {
mykey = "some_value"
}
template {
data = <<EOH
{{ env "NOMAD_META_mykey" }}
EOH
}
노드 변수 (Node variables)
env 함수를 사용해 템플릿 안에서 노드의 속성과 메타데이터에 접근하세요. 여기의 meta. 구문은 노드 meta 필드에만 적용된다는 점에 유의하세요.
template {
data = <<EOH
---
node_dc: {{ env "node.datacenter" }}
node_cores: {{ env "attr.cpu.numcores" }}
meta_key: {{ env "meta.node_meta_key" }}
EOH
destination = "local/file.yml"
}
환경 변수 (Environment variables)
템플릿을 사용해 태스크의 환경 변수를 만들 수 있어요. 이 템플릿들은 템플릿이 쓰여진 후 KEY=value 쌍으로 파싱된다는 점을 제외하면 다른 템플릿과 똑같이 동작해요. 그 키-값 쌍들은 태스크의 환경에 포함돼요.
예를 들어 다음 템플릿 블록은:
template {
data = <<EOH
# Lines starting with a # are ignored
# Empty lines are also ignored
LOG_LEVEL="{{key "service/geo-api/log-verbosity"}}"
API_KEY="{{with secret "secret/geo-api-key"}}{{.Data.value}}{{end}}"
EOH
destination = "secrets/file.env"
env = true
}
그러면 태스크의 환경은 다음과 같은 환경 변수를 갖게 돼요:
LOG_LEVEL=DEBUG
API_KEY=12345678-1234-1234-1234-1234-123456789abc
이렇게 하면 Nomad 템플릿의 익숙한 모든 기능과 의미론을 유지하면서 12factor app 스타일 환경 변수 기반 구성을 사용할 수 있어요.
시크릿이나 인증서는 새 줄, 따옴표, 백슬래시 같은 다양한 문자를 포함할 수 있어 올바르게 인용하거나 이스케이프하기 어려울 수 있어요.
템플릿화된 변수가 특수 문자를 포함할 수 있을 때마다 toJSON 함수를 사용해 Nomad가 올바르게 파싱하게 하세요.
CERT_PEM={{ file "path/to/cert.pem" | toJSON }}
파서가 JSON 문자열을 읽으므로 $CERT_PEM 환경 변수는 파일 내용과 동일해요.
마찬가지로 따옴표나 #를 포함할 수 있는 비밀번호를 평가할 때는 toJSON 함수를 사용해 Nomad가 비밀번호를 태스크에 변경되지 않은 채 전달하게 하세요.
# Passwords may contain any character including special characters like:
# \"'#
# Use toJSON to ensure Nomad passes them to the environment unchanged.
{{ with secret "secrets/data/application/backend" }}
DB_PASSWD={{ .Data.data.DB_PASSWD | toJSON }}
{{ end }}
자세한 내용은 go-envparser's README를 참고하세요.
템플릿 대상 (Template destinations)
템플릿은 태스크 작업 디렉터리로 렌더링돼요. 파일시스템 격리가 없는 드라이버(raw_exec 등)나 태스크 작업 디렉터리에 chroot를 만드는 드라이버(exec 등)는 태스크의 임의 경로에 템플릿을 렌더링할 수 있어요. 하지만 docker 같은 태스크 드라이버는 NOMAD_ALLOC_DIR, NOMAD_TASK_DIR, NOMAD_SECRETS_DIR로 렌더링된 템플릿에만 접근할 수 있어요. 이 제한을 우회하려면 템플릿 destination에서 태스크의 다른 위치로 마운트를 만들 수 있어요.
task "task" {
driver = "docker"
config {
image = "redis:6.0"
mount {
type = "bind"
source = "local"
target = "/etc/redis.d"
}
}
template {
destination = "local/redis.conf"
}
}
의존성 (Dependencies)
Vault, Consul 또는 Nomad에서 읽는 템플릿의 경우 읽는 각 항목을 "의존성(dependency)"이라고 해요. 모든 template 블록은 같은 내부 러너를 공유하며, 같은 항목을 요청하는 의존성을 중복 제거해요. 주어진 태스크에 대해 많은 수의 의존성을 두는 것은 피해야 해요. 각 의존성은 업스트림 서버에 적어도 하나의 동시 요청(가능하면 블로킹 쿼리)이 필요하기 때문이에요. 태스크에 128개가 넘는 의존성이 있으면 Nomad 클라이언트 로그에 "watching this many dependencies could DDoS your servers"을 보고하는 warn 레벨 로그가 나타나요. 여기서 서버는 조회되는 Vault, Consul 또는 Nomad 클러스터를 가리켜요.
Nomad 통합 (Nomad integration)
Nomad 서비스 (Nomad services)
Nomad 서비스 등록은 nomadService와 nomadServices 함수로 조회할 수 있어요. 요청은 template 블록을 포함하는 잡과 같은 네임스페이스에 연결돼요.
template {
data = <<EOF
# Configuration for a single NGINX upstream service.
upstream my_app {
{{- range nomadService "my-app" }}
server {{ .Address }}:{{ .Port }};{{- end }}
}
# Configuration for all services registered in Nomad as an NGINX upstream
# service.
{{ range nomadServices }}
# Configuration for service {{ .Name }}.
upstream {{ .Name | toLower }} {
{{- range nomadService .Name }}
server {{ .Address}}:{{ .Port }};{{- end }}
}
{{ end -}}
EOF
destination = "local/nginx.conf"
}
Nomad 서비스로 간단한 로드 밸런싱 (Simple load balancing with Nomad services)
nomadService 함수는 이제 rendezvous hashing으로 서비스 인스턴스를 선택해 간단한 로드 밸런싱을 지원해요. 간단한 로드 밸런싱을 활성화하려면 nomadService 함수에 3개의 인자가 필요해요.
- 선택할 서비스 수
- 해싱 키(고유해야 하지만 요청자마다 일관적이어야 함)
- 서비스 이름
NOMAD_ALLOC_ID를 해싱 키로 사용하면 선택된 인스턴스는 할당 동안 대부분 안정적으로 유지돼요. 템플릿이 실행될 때마다 nomadService는 각 할당에 대해 같은 인스턴스 집합을 반환해요 — 서비스의 N 인스턴스가 추가되거나 제거되지 않는 한 그렇고, 그 경우 선택된 인스턴스가 교체될 확률은 1/N이에요. 이것은 구성 파일을 렌더링할 때 더 일관된 출력을 유지하는 데 도움이 되며, Nomad 태스크의 재시작과 신호 전송을 줄여줘요.
template {
data = <<EOH
# Configuration for 1 redis instances, as assigned via rendezvous hashing.
{{$allocID := env "NOMAD_ALLOC_ID" -}}
{{range nomadService 1 $allocID "redis"}}
server {{ .Address }}:{{ .Port }};
{{- end}}
EOH
}
Nomad 변수 (Nomad variables)
경고
Nomad 통합을 사용하면 Nomad 서버에 대한 암시적 의존성이 생겨요. Nomad 서버를 사용할 수 없으면(예: 클라이언트 연결이 끊긴 경우) 몇 번의 재시도 후 실행 중인 할당이 중지돼요. 자세한 내용과 재시도 동작 조정은 template.nomad_retry 클라이언트 구성 옵션을 참고하세요.
Nomad 변수는 nomadVar, nomadVarList, nomadVarListSafe, nomadVarExists 함수로 조회할 수 있어요.
nomadVarList와 nomadVarListSafe
이 함수들은 태스크가 workload identity를 기반으로 접근할 수 있는 Nomad 변수의 경로를 나열하는 데 사용할 수 있어요. 경로 목록을 필터링할 선택적 접두사를 매개변수로 제공할 수 있어요.
목록 함수는 NomadVarMeta 타입의 슬라이스를 반환해요:
type NomadVarMeta struct {
Namespace, Path string
CreateIndex, ModifyIndex uint64
CreateTime, ModifyTime nanoTime
}
nanoTime 타입은 epoch 이후의 Unix 나노초를 포함해요. 그 문자열 메서드는 형식화된 날짜/시간 값으로 출력해요. 또한 추가 조작을 위해 Go time.Time을 가져오는 데 사용할 수 있는 Time 메서드도 있어요.
NomadVarMeta 객체는 문자열로 사용될 때 Path 값을 출력해요. 예를 들어 이 두 template 블록은 동일한 출력을 만들어요.
template {
data = <<EOH
{{ range nomadVarList }}
{{ . }}
{{ end }}
EOH
}
template {
data = <<EOH
{{ range nomadVarList }}
{{ .Path }}
{{ end }}
EOH
}
nomadVarList 함수의 선택적 매개변수로 접두사를 제공해 경로 목록을 필터링할 수 있어요.
template {
data = <<EOH
{{ range nomadVarList "path/to/filter" }}
{{ . }}
{{ end }}
EOH
}
기본적으로 nomadVarList는 태스크와 같은 네임스페이스에 있는 변수를 나열해요. 경로 필터는 @ 문자로 구분된 접미사를 추가해 네임스페이스를 변경할 수 있어요:
template {
data = <<EOH
{{ range nomadVarList "path/to/filter@example_namespace" }}
{{ . }}
{{ end }}
EOH
}
nomadVarListSafe 함수는 nomadVarList와 동일하게 동작하지만, 변수 목록 조회가 비어 있거나 빈 데이터를 반환하면 템플릿 렌더링을 거부해요.
nomadVar
이 함수들은 태스크가 태스크의 workload identity를 기반으로 변수에 대한 접근 권한이 있다고 가정하고 Nomad 변수를 읽는 데 사용할 수 있어요. 경로가 존재하지 않거나 호출자가 접근 권한이 없으면 template 렌더러는 경로가 사용 가능해질 때까지 블로킹해요. 블로킹을 피하려면 nomadVar 호출을 nomadVarExists로 감싸세요.
nomadVar 함수는 string에서 NomadVarItem 구조체로의 맵을 반환해요. 맵의 각 구성원은 변수의 Items 컬렉션의 한 구성원에 해당해요. 여기서 중요한 주의 사항은 NomadVarItem이 Consul Template에 의해 텍스트로 렌더링될 때만 string이라는 점이에요. 즉 문자열을 기대하는 파이프라인에서 사용할 수 없어요. .Value를 호출해 값을 문자열을 기대하는 템플릿 함수로 파이프하세요.
예를 들어 경로 nomad/jobs/redis에 변수가 있고 Items 컬렉션에 단일 키/값 쌍(maxconns:15)이 있다고 가정하면:
template {
data = <<EOH
{{ with nomadVar "nomad/jobs/redis" }}{{ .maxconns }}{{ end }}
EOH
}
다음을 렌더링해요:
15
각 맵 값에는 변수 메타데이터를 가져오고 부모 변수에 대한 링크를 얻는 헬퍼 메서드도 포함돼요:
Keys: 이NomadVarItems맵에 대한 정렬된 키 목록을 생성.Values: 키 정렬 목록을 생성.Tuples: 키-정렬된 K,V 튜플 구조체 목록을 생성하며, 키와 값 각각에.K와.V접근자가 있음.Metadata: 이 컬렉션의 부모 메타데이터를NomadVarMeta로 반환.Parent:NomadVarMeta를 참조하는 Metadata 필드와 이NomadVarItems객체를 참조하는 Items 필드가 있는 부모 객체를 반환.
예를 들어 경로 nomad/jobs/redis에 변수가 있다고 가정하면 일부 메타데이터를 다음과 같이 렌더링할 수 있어요:
template {
data = <<EOH
{{ with nomadVar "nomad/jobs/redis" }}
Path: {{ .Metadata.Path }}
Namespace: {{ .Metadata.Namespace }}
CreateTime: {{ .Metadata.CreateTime }}
ModifyTime: {{ .Metadata.ModifyTime }}
{{ end }}
EOH
}
기본적으로 nomadVar 함수는 태스크와 같은 네임스페이스의 변수를 읽어요. 경로 필터는 @ 문자로 구분된 접미사를 추가해 네임스페이스를 변경할 수 있어요.
template {
data = <<EOH
{{ with nomadVar "nomad/jobs/redis@example_namespace" }}{{ .maxconns }}{{ end }}
EOH
}
nomadVarExists
이 함수는 태스크가 태스크의 workload identity를 기반으로 변수에 대한 접근 권한이 있다고 가정하고 제공된 경로에 Nomad 변수가 존재하는지 확인하는 데 사용할 수 있어요. 변수가 존재하면 true를, 아니면 false를 반환해요. nomadVar와 달리 이 함수는 변수가 존재하지 않아도 블로킹하지 않으며, 흐름 제어에 유용할 수 있어요.
예:
template {
data = <<EOH
{{ if nomadVarExists "app/beta_active" }}
# ...
{{ else }}
# ...
{{ end }}
EOH
}
Consul 통합 (Consul integration)
Consul 통합이 필요한 템플릿을 가진 태스크는 Consul 토큰이 필요해요. 서버를 구성해 태스크 워크로드 아이덴티티를 제공하고 그룹 또는 태스크 레벨에 consul 블록을 두어 태스크에 Consul 토큰이 있도록 보장하거나, client.template.use_client_consul_token = true를 사용해 Nomad 클라이언트의 토큰으로 폴백할 수 있어요.
경고
Consul 통합을 사용하면 Consul에 대한 암시적 의존성이 생겨요. Consul을 사용할 수 없으면 몇 번의 재시도 후 실행 중인 할당이 중지돼요. 자세한 내용과 재시도 동작 조정은 template.consul_retry 클라이언트 구성 옵션을 참고하세요.
Consul KV
Consul KV 값은 key 함수를 사용해 키 경로에서 단일 값을 가져올 수 있어요. ls 함수는 경로의 모든 키를 가져오는 데 사용할 수 있어요. 깊이 중첩된 경로에는 tree 함수를 사용하세요.
template {
data = <<EOF
# Read single key from Consul KV.
APP_NAME = "{{key "app/name"}}"
# Read all keys in the path `app/environment` from Consul KV.
{{range ls "app/environment"}}
{{.Key}}={{.Value}}
{{end}}
EOF
destination = "local/env"
env = true
}
Consul 서비스 (Consul services)
Consul 서비스 카탈로그는 service와 services 함수로 조회할 수 있어요. Connect 가능 서비스에는 connect 함수를 사용하세요.
template {
data = <<EOF
# Configuration for a single upstream service.
upstream my_app {
{{- range service "my-app" }}
server {{ .Address }}:{{ .Port }};{{- end }}
}
# Configuration for all services in the catalog.
{{ range services }}
# Configuration for service {{ .Name }}.
upstream {{ .Name | toLower }} {
{{- range service .Name }}
server {{ .Address}}:{{ .Port }};{{- end }}
}
{{ end -}}
EOF
destination = "local/nginx.conf"
}
Vault 통합 (Vault integration)
Vault 통합이 필요한 템플릿을 가진 태스크는 Vault 토큰이 필요해요. 서버를 구성해 워크로드 아이덴티티를 제공하고 잡, 그룹 또는 태스크 레벨에 vault 블록을 두어 태스크에 Vault 토큰이 있도록 보장할 수 있어요.
경고
Vault 통합을 사용하면 Vault에 대한 암시적 의존성이 생겨요. Vault를 사용할 수 없으면 몇 번의 재시도 후 실행 중인 할당이 중지돼요. 자세한 내용과 재시도 동작 조정은 template.vault_retry 클라이언트 구성 옵션을 참고하세요.
PKI 인증서 (PKI certificate)
Vault는 시크릿을 관리하는 인기 있는 오픈소스 도구예요. 암호화된 KV 저장소 역할 외에도 Vault는 PKI/TLS 인증서 같은 동적 시크릿을 생성할 수 있어요.
Vault로 PKI 인증서를 생성할 때 인증서, 개인 키, 그리고 중간 인증서가 모두 같은 API 호출의 일부로 반환돼요. 대부분의 소프트웨어는 이 파일들이 시스템의 별도 파일에 배치되기를 요구해요.
개별 파일로 (As individual files)
템플릿의 경우 모든 의존성이 단일 목록으로 매핑돼요. 즉 같은 경로를 감시하는 여러 템플릿이 같은 데이터를 반환한다는 뜻이에요.
template {
data = <<EOH
{{ with pkiCert "pki/issue/foo" "common_name=foo.service.consul" "ip_sans=127.0.0.1" }}
{{- .Cert -}}
{{ end }}
EOH
destination = "${NOMAD_SECRETS_DIR}/certificate.crt"
change_mode = "restart"
}
template {
data = <<EOH
{{ with pkiCert "pki/issue/foo" "common_name=foo.service.consul" "ip_sans=127.0.0.1" }}
{{- .CA -}}
{{ end }}
EOH
destination = "${NOMAD_SECRETS_DIR}/ca.crt"
change_mode = "restart"
}
template {
data = <<EOH
{{ with pkiCert "pki/issue/foo" "common_name=foo.service.consul" "ip_sans=127.0.0.1" }}
{{- .Key -}}
{{ end }}
EOH
destination = "${NOMAD_SECRETS_DIR}/private_key.key"
change_mode = "restart"
}
이것들은 서로 다른 세 개의 입력 템플릿이지만, Nomad 잡에서 실행하면 단일 호출로 압축되어 결과 데이터를 공유해요.
PEM 형식 파일로 (As a PEM formatted file)
이 예시는 Vault에서 PKI 인증서를 PEM 형식으로 가져와 요소를 번들로 연결하고 애플리케이션의 시크릿 디렉터리에 저장해요.
template {
data = <<EOH
{{ with pkiCert "pki/issue/foo" "common_name=foo.service.consul" "ip_sans=127.0.0.1" "format=pem" }}
{{ .Cert }}
{{ .CA }}
{{ .Key }}{{ end }}
EOH
destination = "${NOMAD_SECRETS_DIR}/bundle.pem"
change_mode = "restart"
}
Vault KV API v1 (Vault KV API v1)
Vault KV API v1에서 경로는 secret/로 시작하고 응답은 원시 키/값 데이터를 반환해요. 이 시크릿은 vault kv put secret/aws/s3 aws_access_key_id=somekeyid로 설정됐어요.
template {
data = <<EOF
AWS_ACCESS_KEY_ID = "{{with secret "secret/aws/s3"}}{{.Data.aws_access_key_id}}{{end}}"
EOF
}
시크릿 이름에 - 문자가 포함되면 인덱스로 접근해야 한다는 점에 유의하세요. 이 시크릿은 vault kv put secret/app db-password=somepassword로 설정됐어요.
template {
data = <<EOF
DB_PASSWORD = "{{with secret "secret/app"}}{{index .Data "db-password"}}{{end}}"
EOF
}
Vault KV API v2 (Vault KV API v2)
Vault KV API v2에서 경로는 secret/data/로 시작하고 응답은 키/값 데이터 외에 메타데이터를 반환해요. 이 시크릿은 vault kv put secret/aws/s3 aws_access_key_id=somekeyid로 설정됐어요.
template {
data = <<EOF
AWS_ACCESS_KEY_ID = "{{with secret "secret/data/aws/s3"}}{{.Data.data.aws_access_key_id}}{{end}}"
EOF
}
경로와 필드 접근자 문자열 모두에 data가 추가된 것에 주목하세요. 또한 Vault v2 API를 사용할 때 Nomad 잡에 적용되는 Vault 정책은 secret/...가 아닌 secret/data/... 아래의 read 권한을 부여해야 해요.
KV API v1처럼 시크릿 이름에 - 문자가 포함되면 인덱스로 접근해야 해요. 이 시크릿은 vault kv put secret/app db-password=somepassword로 설정됐어요.
template {
data = <<EOF
DB_PASSWORD = "{{with secret "secret/data/app"}}{{index .Data.data "db-password"}}{{end}}"
EOF
}
클라이언트 구성 (Client configuration)
template 블록에는 다음 클라이언트 구성 옵션이 있어요:
-
function_denylist([]string: ["plugin", "executeTemplate", "writeToFile"])- 잡 스펙에서 허용되지 않아야 하는 템플릿 렌더링 함수 목록. 기본적으로plugin함수는 호스트에서 루트로 임의의 명령을 실행할 수 있게 하므로(Nomad가 비루트 사용자로 실행되도록 구성되지 않은 한) 허용되지 않고,executeTemplate는 우발적이거나 악의적인 무한 재귀 실행을 막기 위해 허용되지 않으며,writeToFile는 허용되지 않아요. -
disable_file_sandbox(bool: false)- 템플릿이file함수를 통해 클라이언트 호스트의 임의 파일에 접근할 수 있게 허용. 기본적으로 템플릿은 태스크 작업 디렉터리 안의 파일에만 접근할 수 있어요.