k8s - 헬름 차트 문법!

차트 문법

kube3

차트는 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 함수와 똑같다.
단순 문자열, 포멧 문자열을 출력할 수 있다.

# 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 함수로 접근할 수 있다.
restfirst 를 제외한 요소 리스트를 반환한다(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.yamlnameOverride 이 공백이기 때문에 .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
```

카테고리:

업데이트: