Skip to content

필사 모드: Helm 템플릿 엔진 심층 분석: Go 템플릿, Sprig, 네임드 템플릿

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

1. Go 템플릿 내부 동작

1.1 파싱(Parsing) 단계

Helm의 템플릿 엔진은 Go 표준 라이브러리의 `text/template`을 기반으로 합니다. 템플릿 처리는 크게 **파싱**과 **실행** 두 단계로 나뉩니다.

파싱 단계에서는 템플릿 텍스트를 **AST(Abstract Syntax Tree)**로 변환합니다:

템플릿 텍스트 → 렉서(Lexer) → 토큰 스트림 → 파서(Parser) → AST

**렉서**는 템플릿에서 다음 요소를 인식합니다:

- 일반 텍스트 (그대로 출력)

- 액션 구분자: 여는 구분자와 닫는 구분자

- 파이프라인, 변수, 함수 호출

1.2 실행(Execution) 단계

실행 단계에서는 AST를 순회하면서 데이터 컨텍스트(dot `.`)를 적용합니다:

AST + 데이터 컨텍스트 → 렌더링된 텍스트 출력

**파이프라인(Pipeline)** 개념이 핵심입니다:

파이프라인: 왼쪽 출력이 오른쪽 입력으로 전달

name: { { .Values.name | lower | trunc 63 | trimSuffix "-" } }

위 파이프라인의 실행 순서:

1. .Values.name 평가 → "My-Application-Name"

2. lower 적용 → "my-application-name"

3. trunc 63 적용 → "my-application-name" (63자 이하이므로 그대로)

4. trimSuffix "-" 적용 → "my-application-name"

1.3 공백 제어(Whitespace Control)

하이픈(-)으로 공백 제거

metadata:

labels:

{{- include "my-chart.labels" . | nindent 4 }}

#^ 왼쪽 공백/줄바꿈 제거

{{ include "my-chart.labels" . | nindent 4 -}}

^ 오른쪽 공백/줄바꿈 제거

{{- include "my-chart.labels" . | nindent 4 -}}

#^ 양쪽 모두 제거

2. Sprig 라이브러리 함수

2.1 문자열 함수

기본 문자열 조작

upper: {{ "hello" | upper }} # HELLO

lower: {{ "HELLO" | lower }} # hello

title: {{ "hello world" | title }} # Hello World

untitle: {{ "Hello World" | untitle }}# hello world

trim: {{ " hello " | trim }} # hello

trimAll: {{ "**hello**" | trimAll "*" }} # hello

부분 문자열

substr: {{ substr 0 5 "hello world" }} # hello

trunc: {{ "hello" | trunc 3 }} # hel

contains: {{ contains "lo" "hello" }} # true

hasPrefix: {{ hasPrefix "he" "hello" }} # true

hasSuffix: {{ hasSuffix "lo" "hello" }} # true

치환

replace: {{ "hello world" | replace " " "-" }} # hello-world

snakecase: {{ "HelloWorld" | snakecase }} # hello_world

camelcase: {{ "hello_world" | camelcase }} # HelloWorld

kebabcase: {{ "HelloWorld" | kebabcase }} # hello-world

정규 표현식

regexMatch: {{ regexMatch "^[a-z]+$" "hello" }} # true

regexReplaceAll: {{ regexReplaceAll "[0-9]+" "abc123" "X" }} # abcX

랜덤 문자열

randAlphaNum: {{ randAlphaNum 10 }} # 10자 랜덤 영숫자

2.2 수학 함수

기본 산술

add: { { add 1 2 } } # 3

sub: { { sub 10 3 } } # 7

mul: { { mul 2 3 } } # 6

div: { { div 10 3 } } # 3

mod: { { mod 10 3 } } # 1

max: { { max 1 2 3 } } # 3

min: { { min 1 2 3 } } # 1

반올림

ceil: { { ceil 1.1 } } # 2

floor: { { floor 1.9 } } # 1

round: { { round 1.5 0 } } # 2

2.3 날짜 함수

현재 시간

now: { { now } }

date: { { now | date "2006-01-02" } }

dateInZone: { { dateInZone "2006-01-02" (now) "UTC" } }

htmlDate: { { now | htmlDate } }

날짜 계산

dateModify: { { now | dateModify "+24h" | date "2006-01-02" } }

ago: { { ago (now) } }

2.4 암호화 함수

해싱

sha256sum: {{ "hello" | sha256sum }}

sha1sum: {{ "hello" | sha1sum }}

Base64

b64enc: {{ "hello" | b64enc }} # aGVsbG8=

b64dec: {{ "aGVsbG8=" | b64dec }} # hello

비밀번호 생성

genPassword: {{ randAlphaNum 16 }}

derivePassword: {{ derivePassword 1 "long" "password" "user" "example.com" }}

UUID

uuid: {{ uuidv4 }}

2.5 리스트 함수

리스트 생성과 조작

list: { { list "a" "b" "c" } }

first: { { first (list "a" "b" "c") } } # a

last: { { last (list "a" "b" "c") } } # c

rest: { { rest (list "a" "b" "c") } } # [b c]

initial: { { initial (list "a" "b" "c") } } # [a b]

append: { { append (list "a" "b") "c" } } # [a b c]

prepend: { { prepend (list "b" "c") "a" } } # [a b c]

concat: { { concat (list "a") (list "b") } } # [a b]

has: { { has "b" (list "a" "b" "c") } } # true

without: { { without (list "a" "b" "c") "b" } } # [a c]

uniq: { { uniq (list "a" "b" "a") } } # [a b]

sortAlpha: { { sortAlpha (list "c" "a" "b") } } # [a b c]

2.6 딕셔너리 함수

딕셔너리 생성과 조작

{{- $myDict := dict "key1" "value1" "key2" "value2" }}

get: {{ get $myDict "key1" }} # value1

set: {{ set $myDict "key3" "value3" }}

hasKey: {{ hasKey $myDict "key1" }} # true

keys: {{ keys $myDict }} # [key1 key2]

values: {{ values $myDict }} # [value1 value2]

pluck: {{ pluck "key1" $myDict }} # [value1]

merge: 여러 딕셔너리 병합

{{- $merged := merge (dict "a" "1") (dict "b" "2") }}

3. 네임드 템플릿(Named Templates)

3.1 define과 template

templates/_helpers.tpl

{{- define "my-chart.name" -}}

{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}

{{- end }}

{{- define "my-chart.fullname" -}}

{{- if .Values.fullnameOverride }}

{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}

{{- else }}

{{- $name := default .Chart.Name .Values.nameOverride }}

{{- if contains $name .Release.Name }}

{{- .Release.Name | trunc 63 | trimSuffix "-" }}

{{- else }}

{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}

{{- end }}

{{- end }}

{{- end }}

3.2 template vs include

`template`과 `include`의 핵심 차이:

template: 결과를 직접 출력 (파이프라인 사용 불가)

{{ template "my-chart.name" . }}

include: 결과를 문자열로 반환 (파이프라인 사용 가능)

{{ include "my-chart.labels" . | nindent 4 }}

**include를 사용하는 것이 권장됩니다.** 파이프라인을 통해 후처리(들여쓰기 등)가 가능하기 때문입니다.

3.3 \_helpers.tpl 관례

templates/_helpers.tpl - 표준 헬퍼 패턴

차트 이름

{{- define "my-chart.name" -}}

{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}

{{- end }}

전체 이름

{{- define "my-chart.fullname" -}}

{{- $name := default .Chart.Name .Values.nameOverride }}

{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}

{{- end }}

차트 버전 레이블

{{- define "my-chart.chart" -}}

{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}

{{- end }}

공통 레이블

{{- define "my-chart.labels" -}}

helm.sh/chart: {{ include "my-chart.chart" . }}

{{ include "my-chart.selectorLabels" . }}

{{- if .Chart.AppVersion }}

app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}

{{- end }}

app.kubernetes.io/managed-by: {{ .Release.Service }}

{{- end }}

셀렉터 레이블

{{- define "my-chart.selectorLabels" -}}

app.kubernetes.io/name: {{ include "my-chart.name" . }}

app.kubernetes.io/instance: {{ .Release.Name }}

{{- end }}

ServiceAccount 이름

{{- define "my-chart.serviceAccountName" -}}

{{- if .Values.serviceAccount.create }}

{{- default (include "my-chart.fullname" .) .Values.serviceAccount.name }}

{{- else }}

{{- default "default" .Values.serviceAccount.name }}

{{- end }}

{{- end }}

4. 플로우 컨트롤

4.1 if/else

기본 조건문

{{- if .Values.ingress.enabled }}

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

name: {{ include "my-chart.fullname" . }}

spec:

rules:

- host: {{ .Values.ingress.host }}

{{- end }}

if/else if/else

{{- if eq .Values.service.type "NodePort" }}

nodePort: {{ .Values.service.nodePort }}

{{- else if eq .Values.service.type "LoadBalancer" }}

loadBalancerIP: {{ .Values.service.loadBalancerIP }}

{{- else }}

ClusterIP - no extra config

{{- end }}

**Falsy 값**: false, 0, 빈 문자열 "", nil, 빈 컬렉션(list, map, tuple, dict)

4.2 with

`with`는 스코프를 변경합니다:

{{- with .Values.nodeSelector }}

nodeSelector:

{{- toYaml . | nindent 2 }}

{{- end }}

주의: with 블록 내에서 상위 스코프 접근 시 $를 사용

{{- with .Values.container }}

name: {{ $.Chart.Name }}

image: {{ .image }}

port: {{ .port }}

{{- end }}

4.3 range

리스트 반복

{{- range .Values.extraEnvVars }}

- name: {{ .name }}

value: {{ .value | quote }}

{{- end }}

맵 반복

{{- range $key, $value := .Values.configData }}

{{ $key }}: {{ $value | quote }}

{{- end }}

인덱스와 함께 반복

{{- range $index, $item := .Values.items }}

item-{{ $index }}: {{ $item }}

{{- end }}

고정 횟수 반복

{{- range until 5 }}

- replica-{{ . }}

{{- end }}

5. 고급 템플릿 패턴

5.1 toYaml과 nindent

toYaml: Go 객체를 YAML 문자열로 변환

nindent: n칸 들여쓰기 추가 (줄바꿈 포함)

spec:

template:

spec:

{{- with .Values.tolerations }}

tolerations:

{{- toYaml . | nindent 8 }}

{{- end }}

{{- with .Values.affinity }}

affinity:

{{- toYaml . | nindent 8 }}

{{- end }}

5.2 tpl 함수

`tpl`은 문자열을 템플릿으로 렌더링합니다:

values.yaml

configTemplate: |

server.name={{ .Release.Name }}

server.namespace={{ .Release.Namespace }}

templates/configmap.yaml

apiVersion: v1

kind: ConfigMap

metadata:

name: { { include "my-chart.fullname" . } }

data:

config.properties: |

{{ tpl .Values.configTemplate . | nindent 4 }}

5.3 lookup 함수

실행 시점에 클러스터 리소스를 조회합니다:

기존 Secret이 있으면 재사용, 없으면 생성

{{- $secret := lookup "v1" "Secret" .Release.Namespace "my-secret" }}

{{- if $secret }}

password: {{ index $secret.data "password" }}

{{- else }}

password: {{ randAlphaNum 16 | b64enc }}

{{- end }}

주의: `helm template` 명령에서는 lookup이 항상 빈 결과를 반환합니다.

5.4 .Files 객체

파일 내용을 ConfigMap에 포함

apiVersion: v1

kind: ConfigMap

metadata:

name: {{ include "my-chart.fullname" . }}-config

data:

{{- range $path, $_ := .Files.Glob "config/**" }}

{{ base $path }}: |

{{ $.Files.Get $path | nindent 4 }}

{{- end }}

바이너리 파일 (base64)

binaryData:

{{- range $path, $_ := .Files.Glob "binaries/**" }}

{{ base $path }}: {{ $.Files.Get $path | b64enc }}

{{- end }}

6. 라이브러리 차트(Library Charts)

6.1 라이브러리 차트란

라이브러리 차트는 `type: library`로 선언된 차트로, 자체 리소스를 렌더링하지 않고 네임드 템플릿만 제공합니다.

Chart.yaml

apiVersion: v2

name: my-library

type: library

version: 1.0.0

6.2 라이브러리 차트 활용

부모 차트의 Chart.yaml

dependencies:

- name: my-library

version: '1.x.x'

repository: 'https://charts.example.com'

부모 차트의 템플릿에서 라이브러리 함수 사용

metadata:

labels: { { - include "my-library.standardLabels" . | nindent 4 } }

라이브러리 차트의 장점:

- 여러 차트에서 공통 템플릿 로직을 공유

- 표준 레이블, 어노테이션, 리소스 패턴을 중앙에서 관리

- DRY(Don't Repeat Yourself) 원칙 준수

7. 디버깅

7.1 helm template

로컬에서 템플릿 렌더링 (클러스터 불필요)

helm template my-release ./my-chart

특정 values 파일 적용

helm template my-release ./my-chart -f production-values.yaml

특정 템플릿만 렌더링

helm template my-release ./my-chart -s templates/deployment.yaml

디버그 출력

helm template my-release ./my-chart --debug

7.2 helm lint

차트 문법 검사

helm lint ./my-chart

엄격 모드 (경고도 오류로 처리)

helm lint ./my-chart --strict

values 파일과 함께 검증

helm lint ./my-chart -f production-values.yaml

8. 정리

Helm 템플릿 엔진은 단순한 문자열 치환을 넘어 **강력한 프로그래밍 모델**을 제공합니다:

1. **Go 템플릿 기반**: 파싱-실행 2단계 파이프라인 아키텍처

2. **Sprig 라이브러리**: 100개 이상의 유틸리티 함수 제공

3. **네임드 템플릿**: define/include로 재사용 가능한 템플릿 모듈화

4. **플로우 컨트롤**: if/with/range로 조건부 및 반복 렌더링

5. **라이브러리 차트**: 공통 로직의 중앙 관리와 공유

다음 글에서는 Helm 릴리스의 전체 생명주기(install, upgrade, rollback)를 분석합니다.

현재 단락 (1/236)

Helm의 템플릿 엔진은 Go 표준 라이브러리의 `text/template`을 기반으로 합니다. 템플릿 처리는 크게 **파싱**과 **실행** 두 단계로 나뉩니다.

작성 글자: 0원문 글자: 8,263작성 단락: 0/236