k8s - 헬름 차트 문법!
차트 문법
차트는 values.yaml
, Chart.yaml
, template
으로 구성되는데
template
폴더에는 위에서 말한것 처럼 deployment.yaml
파일뿐 아니라 k8s
어플리케이션 배포에 필요한 Service
, ConfigMap
등 다양한 리소스가 정의된 yaml
파일이 정의되어 있다.
Chart
객체의 경우apiVersion: v2
,name: mychart
이런식으로 정의되어 있다 하더라도 시작 문자가 대문자여야 한다.
Chart.AppVersion
,Chart.Version
```
apiVersion: v1
kind: ConfigMap
metadata:
name: built-in-object
data:
.Release: ______________________________________
.Release.Name: {{ .Release.Name }}
.Release.Namespace: {{ .Release.Namespace }}
.Release.IsUpgrade: "{{ .Release.IsUpgrade }}"
.Release.IsInstall: "{{ .Release.IsInstall }}"
.Release.Revision: "{{ .Release.Revision }}"
.Release.Service: {{ .Release.Service }}
.Values: ______________________________________
.Values.replicaCount: "{{ .Values.replicaCount }}"
.Values.image.repository: {{ .Values.image.repository }}
.Values.image.pullPolicy: {{ .Values.image.pullPolicy }}
.Values.service.type: {{ .Values.service.type }}
.Values.service.port: "{{ .Values.service.port }}"
.Chart: ______________________________________
.Chart.Name: {{ .Chart.Name }}
.Chart.Description: {{ .Chart.Description }}
.Chart.Type: {{ .Chart.Type }}
.Chart.Version: {{ .Chart.Version }}
.Chart.AppVersion: {{ .Chart.AppVersion }}
.Template: ______________________________________
.Template.BasePath: {{ .Template.BasePath }}
.Template.Name: {{ .Template.Name }}
```
출력 결과는 아래와 같다.
helm install mychart . -n default
helm get manifest mychart
# Source: mychart/templates/cm-object.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: built-in-object
data:
.Release: ______________________________________
.Release.Name: mychart
.Release.Namespace: default
.Release.IsUpgrade: "false"
.Release.IsInstall: "true"
.Release.Revision: "1"
.Release.Service: Helm
.Values: ______________________________________
.Values.replicaCount: "1"
.Values.image.repository: nginx
.Values.image.pullPolicy: IfNotPresent
.Values.service.type: ClusterIP
.Values.service.port: "80"
.Chart: ______________________________________
.Chart.Name: mychart
.Chart.Description: A Helm chart for Kubernetes
.Chart.Type: application
.Chart.Version: 0.1.0
.Chart.AppVersion: 1.16.0
.Template: ______________________________________
.Template.BasePath: mychart/templates
.Template.Name: mychart/templates/cm-object.yaml
Release
객체는 인스턴스의 정보를 가지며 아래 6가지 객체를 가지고 있음
Release.Name
: 릴리스 이름Release.Namespace
: 릴리스될 네임스페이스 (manifest에서 오버라이드하지 않은 경우)Release.IsUpgrade
: 현재 작업이 업그레이드 또는 롤백인 경우 true 로 설정된다.Release.IsInstall
: 현재 작업이 설치일 경우 true 로 설정.Release.Revision
: 이 릴리스의 리비전 번호. 설치시에는 이 값이 1이며 업그레이드나 롤백을 수행할 때마다 증가한다.Release.Service
: 현재 템플릿을 렌더링하는 서비스. Helm 에서는 항상 Helm 이다.
values.yaml
{{ .Values.replicaCount }}
{{ .Values.configMapData.log.level }}
변수삽입 문법을 사용하여 values.yaml
파일안의 값들을 template
폴더안의 각종 리소스의 변수로 지정할 수 있다.
일반적으로 dev
, prod
2개의 환경으로 자주 운영하며
values_dev.yaml
values_prod.yaml
위와같이 value
파일을 나누어서 운영한다.
-f
, -set
옵션을 사용하여 values
의 변수 우선순위를 지정할 수 있다.
helm install mychart . -f values_dev.yaml -set configMapData.log.level=debug
values 우선순위
-set [key=value]
->-f [yaml file]
->values.yaml
우측으로 갈수록 우선순위가 낮다.
values.yaml
위에value_dev.yaml
을 덮어씌우고,-set
속성으로 정의한key-value
를 덮어씌우는 형식
template
template
폴더안에 deployment.yaml
, service.yaml
파일을 보면
각종 조건문, 함수, 파이프라인 구문이 존재한다.
해당 구분 문법에 대해 알아본다.
function & pipeline
# values.yaml
func:
enabled: true
위와같은 values
파일이 있을때 아래와 같이 quote
, upper
함수를 사용하여 변환된 문자열로 출력할 수 있다.
{{ quote .Values.func.enabled }} # "true"
{{ .Values.func.enabled | quote }} # "true"
{{ .Values.func.enabled | upper | quote }} # "TRUE"
upper
는 입력받은 문자열을 대문자로
quote
는 입력받은 자료형에 ""
를 씌어 문자열로 반환한다.
yaml
문법상 문자열에""
가 별도로 필요하지 않으나 가독성을 위해 사용하는 것은 권장한다.
if, else if, else, end
{{ if 조건문 }}
{{ else if 조건문 }}
{{ else }}
{{ end }}
조건문에서 사용할만한 함수는 다음과 같다.
and, or, ne, not, eq, ge, le, lt, default, empty
if
문에서 false
로 판단하는 자료형 값은 다음과 같다.
Null
Number: 0
String: ""
List: []
Object: {}
Boolean: false
# values.yaml
dev:
env: dev
log: info
qa:
env: qa
log: info
prod:
env: prod
위 파일이 있을때 아래와 같이 if
문을 사용해 속성값을 분기처리할 수 있다.
print:
{{- if eq .Values.dev.env "dev" }}
log: debug
{{- else if .Values.dev.env }}
log: {{ .Values.dev.log }}
{{ -else }}
log: error
{{- end }}
typeis
if
문에서 객체 type
을 체크하는데 사용하는 함수
# values.yaml
v1: "text"
v2: true
{{- if typeis 'string' .Values.v1 }}
type1: .Values.v1 # text
{{- end }}
{{- if typeis 'bool' .Values.v2 }}
type2: .Values.v2 # true
{{- end}}
주의사항은
String
,Boolean
이 아닌string
,bool
이라는 것
공백, 개행 삭제
구문 시작시 -
가 들어가는 이유는 구문이 있는부분에 공백
, 개행
이 들어가기 때문
만약 -
를 넣지 않을경우 아래와 같이 출력된다.
print:
log: debug
yaml
특성상 앞에 들여쓰기 공백으로 인해 오류가 발생할 수 있음으로 자주 사용한다.
with + 지역변수
부모객체를 가져와 지역변수처럼 사용하는 개념으로
변수명이 복잡하거나 객체의 depth 가 깊을경우 가독성처리를 위해 사용한다.
{{- with .Values.dev }}
env: {{ .env }}
log: {{ .log }}
{{- end}}
with
의 단점은 scope(.)
를 통한 변수참조가 with
에 정의된 객체안에서만 참조 가능하다는 점
외부 객체 참조 불가 문제를 해결하기 위해 지역변수 문법이 있다.
지역변수는 $
키워드를 사용하여 scope
에 영향을 받지 않기 때문에
내부에서 밖에 정의해둔 변수를 참조할 수 있다.
{{ $locname := .Relase.Name }}
{{- with .Values.dev }}
env: {{ .env }}
log: {{ .log }}
name: {{ $locname }}
{{- end}}
range + 지역변수
반복문 역할을 하는 함수
# values.yaml
list:
- a
- b
- c
위와같이 리스트 객체를 정의해 놓았을 때 아래와 같이 사용 가능하다.
```
range:
{{- range .Values.list }}
- {{ . }}
{{- end }}
# 출력값
range:
- a
- b
- c
```
range
에 개별편의를 위해 자체적인 지역변수 를 제공하는데.
# value.yaml
list:
- a
- b
- c
map:
env: dev
log: info
위와같은 파일이 있을 때 아래처럼 리스트나 맵 형식의 객체에서
range
와 지역변수 문법을 사용하면 $index
, $key
, $value
지역변수를 사용해 반복문 처리를 쉽게 할 수 있다.
```
range:
index:
{{- range $index, $value := .Values.list }}
{{ $index }}: {{ $value }}
{{- end}}
map:
{{- range $key, $value := .Values.map }}
{{ $key }}: {{ $value | quote }}
{{- end }}
# 출력값
range:
index:
0: a
1: b
2: c
map:
env: "dev"
log: "info"
```
print, printf
타 언어의 print
함수와 똑같다.
단순 문자열
, 포멧 문자열
을 출력할 수 있다.
# values.yaml
test:
v1: hello
v2: world
test: {{ print "hello world" }} # hello world
value: {{ printf "%s %s" .Values.test.v1 .Values.test.v2 }} # hello world
tenary - 삼항연산자
tenary
함수와 파이프라인
을 같이 사용하여 삼항연산자처럼 사용할 수 있다.
파이프라인
으로부터 넘어온 값이 true
면 첫번째 인자를, false
면 두번째 인자를 출력한다.
# values.yaml
case:
v1: true
v2: false
case1: {{ .Values.case.v1 | tenary "1" "2" }} # "1"
case2: {{ .Values.case.v2 | tenary "1" "2" }} # "2"
default
파이프라인으로 넘어온 값이 false
라면 default
의 인자값을 출력한다.
# values.yaml
v1: ""
data: {{ .Values.v1 | default "hello" }} # hello
공백문자열은 false
로 취급하기 때문에 default
의 "hello"
인자값을 출력한다.
위에서 다루었던 조건문이 판단하는
false
자료형을 참고하여 효율적으로 사용가능
coalesce
다중인자를 가지는 함수로 Null
이 아닌 가장 앞의 인자를 반환하는 함수
# values.yaml
d1:
d2:
d3: "hello world"
위와같은 values
파일이 있을때 아래와 같이 coalesce
를 사용할 수 있다.
c1: {{ coalesce .Values.d1 .Values.d2 "text" }} # text
c2: {{ coalesce .Values.d1 .Values.d2 .Values.d3 }} # hello world
required
helm 명령을 통해 랜더링할 때 객체에 대한 정보가 없을경우 랜더링을 멈추고 에러를 반환시키고 싶다면 required
함수를 사용
{{ required "A valid foo is required!" .Values.foo }}
toYaml, indent, nindent
values
에 정의된 객체를 yaml
에 붙여넣는것은 생각보다 불친절하다.
# values.yaml
data:
- a
- b
- c
일단 바로 해당 객체를 바로 yaml
에 삽입할 수 없고 toYaml
함수를 거쳐 컨버팅을 해야한다.
my:
data:
{{ .Values.data | toYaml}}
컨버팅후 삽입해도 에러가 나는데 아래와 같이 구성되기 때문이다.
my:
data:
- a
- b
- c
리스트 요소 앞에 공백 4개로 들여쓰기를 지정해야 하기에 아래처럼 수정해야 한다.
my:
data:
{{ .Values.data | toYaml | indent 4 }}
하지만 구문앞에 띄어쓰기가 하나도 없어 yaml
가독성이 안좋아지기 때문에
nindent (new line+indent)
를 사용을 권장한다.
my:
data:
{{- .Values.data | toYaml | nindent 4 }}
앞의 -
가 공백 뿐 아니라 개행까지 없애주기 때문에 구문이 어디에 위치해 있던지 상관없다.
randAlphaNum, randAlpha, randNumeric, randAscii
랜덤한 문자열을 출력하기 위한 함수
r1: {{ randAlphaNum 5 }} # 0ad2v # 0-9a-zA-Z
r2: {{ randAlpha 5 }} # Avbke # a-zA-Z
r3: {{ randNumeric 5 }} # 61242 # 0-9
r4: {{ randAscii 5 }} # a^ve! # ASCII
trim, trimePrefix, trimSuffix
문자열 앞의 공백, prefix
, suffix
를 생략해주는 함수
t1: {{ time " hello " }} # hello
t1: {{ timePrefix "-" "-hello" }} # hello
t1: {{ timeSuffix "-" "hello-" }} # hello
trunc, replace, contains, b64enc
문자열 조작을 위한 함수
s1: {{ trunc 5 "hello world" }} # "hello"
s2: {{ "hello world" | replace " " "-" }} # hello-world
s3: {{ contains "hell" "hello world" }} # true
s4: {{ b64enc 5 "hello" }} # aGVsbG8=
dict, get
dict
함수를 사용하면 dictionary
기능을 하는 객체를 생성 가능하다.
key
기반으로 value
를 가져오려면 get
함수 사용
```
{{- $myDict := dict "key1" "value1" "key2" "value2"}}
dict: {{ get $myDict "key1" }} # value1
```
split, splitList, join
split
문자열을 잘라 맵 객체로 만드는 함수
splitList
문자열을 잘라 배열 객체로 만드는 함수
join
배열을 문자열로 붙이는 함수
```
{{- $map := split "/" "test/common/word" }}
# {
# _0: "test"
# _1: "common"
# _2: "word"
# }
{{- $list := splitList "/" "test/common/word" }}
# ["test", "common", "world"]
str: {{ join "/" $list}} # test/common/world
```
first, rest, last
리스트 내부 요소는 first
, rest
, last
함수로 접근할 수 있다.
rest
는 first
를 제외한 요소 리스트를 반환한다(last
포함).
```
{{- $list := splitList "/" "test/common/word" }}
first: {{ first $list}} # test
rest: {{ rest $list | join "/"}} # common/world
last: {{ last $list }} # world
```
index
리스트객체나 맵객체에서 index 함수를 사용하면 인덱스값이나 키값을 사용하여 내부 요소를 가져올수 있다.
colors:
- "blue"
- "red"
- "green"
my:
name:
first: ko
위와같은 value
파일이 있을경우 아래와 같이 index
함수 사용 가능
colors: {{ index .Values.colors 0 }} # blue
firstName2: {{ index .Values "my" "name" "first"}} # ko
사실
.Values.my.name.first
으로 바로 접근이 가능하지만 특수한 상황에서 index 함수 사용 가능
regexMatch
정규표현식 체크 함수
```
match1: {{ regexMatch ".*\\.ya?ml$" "config.yaml" }} # true
match2: {{ regexMatch ".*\\.ya?ml$" "test.yml" }} # true
```
include
include
는 정의한 템플릿을 가져오기위해 사용하는 함수로
첫번째 인자에는 가져오고싶은 템플릿 이름
두번째 인자에는 해당 템플릿에 적용할 객체를 넣는다.(만약 적용할 객체가 없다면 .
사용)
include1: {{ include "mychart.include" (dict "key1" "value1") | indent2 }}
mychart.include
템플릿에 dict
객체를 넘겨서 가져온다.
```
# _helper.tpl
{{- define "mychart.include" -}}
v1: {{ .key1 }}
v2: {{ get ".key1" }}
{{- end}}
```
만약 템플릿이 위 같이 정의되어 있다면 include1
은 아래와 같은 구조를 가지게 된다.
include1:
v1: "value1"
v2: "value1"
tpl
템플릿 구문을 해석하는 함수, 템플릿 구문 문자열
을 스크립트로 실행한 결과를 반환해준다.
values.yaml
에는 템플릿 구문을 사용하지 못한다.
```
# values.yaml
template: "{{ .Values.name }}"
name: "Tom"
```
t1: {{ .Values.template }} # "{{ .Values.name }}"
t2: {{ tpl .Values.template . }} # Tom
values.yaml
에 정의된 템플릿 구문을 그대로 출력해버린다.
하지만 템플릿 문자열을 해석하는 tpl
함수를 사용하면 템플릿 구문 문자열
을 번역, 실행한 결과를 반환해준다.
사용자 정의 객체 _helpers.tpl
사용자가 직접 객체를 정의하려면 template/_helpers.tpl
(template 확장자) 에 정의하면 된다.
define
으로 시작해서 end
로 끝난다.
{{/*
Expand the name of the chart.
*/}}
{{- define "mychart.name" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
values.yaml
의 nameOverride
이 공백이기 때문에 .Chart.Name
객체를 사용, 쿠버네티스 최대 문자열 길이 63, 그리고 마지막 "-"
문자열을 삭제한다는 내용이다.
mychart.name
애는 Chart.yaml
파일에 따라 mychart
문자열로 초기화된다.
{{/*
Common labels
*/}}
{{- define "mychart.labels" -}}
helm.sh/chart: {{ include "mychart.name" . }}
{{ include "mychart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "mychart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "mychart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
mychart.labels
는 여러줄의 yaml
형식의 문자열로 초기화된다.
define
구문 안에 include
를 삽입해서 아래 정의해두었던 mychart.selectorLabels
객체를 그대로 가져오고 기타 속성들을 .Chart
, .Release
객체에서 가져온다.
향후 helm template mychart .
명령어로 service
리소스에서 사용하는 mychart.labels
객체를 확인해보면 아래와 같이 출력된다.
helm.sh/chart: mychart
app.kubernetes.io/name: mychart
app.kubernetes.io/instance: mychart
app.kubernetes.io/version:"1.16.0"
app.kubernetes.io/managed-by: Helm
template/service.yaml
파일에서 _helpers.tpl
에 정의된 객체를 사용하는 방식,
최종적으로 출력된 service.yaml
은 아래 참고
```
apiVersion: v1
kind: Service
metadata:
name: {{ include "mychart.name" . }}
labels:
{{- include "mychart.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "mychart.selectorLabels" . | nindent 4 }}
# Source: mychart/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: mychart
labels:
helm.sh/chart: mychart
app.kubernetes.io/name: mychart
app.kubernetes.io/instance: mychart
app.kubernetes.io/version: "1.16.0"
app.kubernetes.io/managed-by: Helm
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: mychart
app.kubernetes.io/instance: mychart
```