Container Orchestration/Kubernetes

Helm Chart Template 문법

Somaz 2024. 11. 15. 12:23
728x90
반응형

Overview

오늘은 Helm Chart Template 문법에 대해서 알아보자.

 

 


 

Helm Chart Template 문법

Helm에서 YAML 파일은 차트 템플릿 및 값에 사용되어 Kubernetes 리소스에 대한 구성을 정의한다. Helm의 템플릿 구문은 Go 템플릿을 사용하여 동적 구성을 활성화하므로 Helm 차트 템플릿 내에서 표준 YAML과 Go 템플릿의 조합을 찾을 수 있다. 주요 구문 패턴은 다음과 같다.

 

 

Standard YAML Syntax

Helm 차트는 들여쓰기 및 `key:value` 쌍을 사용하여 구성 설정을 정의하는 YAML로 작성된다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: my-app

 

 

Go Templating Syntax (`{{ ... }}`)

 

Helm은 `{{ ... }}` 로 구분된 YAML 파일 내 동적 값에 Go 템플릿을 사용한다.

metadata:
  name: {{ .Release.Name }}-service

 

 

Values Reference (`.Values`)

`.Values` 는 사용자 제공 구성을 위해 `values.yaml` 파일에 액세스 한다.

spec:
  replicas: {{ .Values.replicaCount }}

 

 

 

Release and Chart Metadata (`.Release, .Chart`)

`.Release: .Release.Name`, .`Release.Namespace`, .`Release.Revision` 등 릴리스에 대한 세부 정보를 제공한다.
`.Chart: .Chart.Name`, .`Chart.Version` 과 같이 `Chart.yaml` 파일에 정의된 메타데이터에 액세스한다.

metadata:
  labels:
    chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"

 

 

 

Conditional Logic (`if/else Statements`)

조건문을 사용하면 지정된 조건에 따라 섹션을 렌더링할 수 있다.

{{ if .Values.service.enabled }}
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-service
{{ end }}

 

 

Loops (`range`)

`ragne` 는 목록이나 맵을 반복하며 여러 리소스를 동적으로 생성하는 데 유용하다.

ports:
{{- range .Values.ports }}
  - port: {{ . }}
{{- end }}

 

 

Default Values (default function)

`default` 는 값이 설정되지 않은 경우 대체 값을 제공한다.

replicas: {{ .Values.replicaCount | default 1 }}

 

 

 

Defining and Using Templates

Helm을 사용하면 define 및 template을 사용하여 차트 내에서 재사용 가능한 템플릿을 정의할 수 있다.

 

정의

{{- define "mychart.labels" -}}
app: {{ .Chart.Name }}
release: {{ .Release.Name }}
{{- end }}

 

사용

metadata:
  labels:
    {{ include "mychart.labels" . | nindent 4 }}

 

 

 

Pipeline Operators (`|`)

파이프(`|`)는 한 함수에서 다른 함수로 출력을 보낸다. 일반적으로 `quote`, `default` 또는 `toYaml` 과 같은 함수에 사용

host: {{ .Values.hostname | quote }}
  • 여기서는 `.Values.hostname` 값에 `quote` 를 적용하여 문자열로 포맷한다.

 

 

사용 에시

host: "example.com"

 

 

`toYaml` 은 데이터 구조(예: 배열, 사전 또는 중첩 객체)를 YAML 형식의 문자열로 변환합니다. 이는 복잡한 데이터 구조를 YAML 구문으로 직접 변환하는 데 특히 유용하다.

env:
{{ .Values.envVars | toYaml | indent 4 }}
  • 여기서 `.Values.envVars` 는 복잡한 데이터 구조이며 list or dictionary 일 수 있다. `toYaml` 은 이를 YAML 형식의 텍스트로 변환하고, `indent 4` 는 상위 구조 내에서 올바르게 정렬한다.

 

 

사용예시

envVars:
  - name: ENV_VAR1
    value: "value1"
  - name: ENV_VAR2
    value: "value2"

 

결과값

env:
  - name: ENV_VAR1
    value: "value1"
  - name: ENV_VAR2
    value: "value2"

 

 

Indentation and Nesting (`indent, nindent`)

`indent` 및 `nindent`'는 특히 중첩된 YAML 구조에서 간격을 관리하는 데 도움이 된다.

  • `indent`: 시작 부분에 줄 바꿈을 추가하지 않고 텍스트의 각 줄 앞에 지정된 수의 공백을 추가한다.
  • `nindent`: indent 와 비슷하지만 시작 부분에 줄 바꿈이 포함되어 있어 YAML 내에서 새 섹션을 시작하는 데 유용하다.
metadata:
  labels:
    {{- include "mychart.labels" . | nindent 6 }}
  • `nindent 6` 은 출력의 각 줄 시작 부분에 6개의 공백을 추가하고 줄 바꿈으로 시작하여 labels 내에서 정렬한다.

 

env:
{{ .Values.envVars | toYaml | indent 4 }}
  • `.Values.envVars` 의 YAML 구조를 4칸 들여쓰기한다.

 

Use `include`

`include` 기능을 사용하면 차트 내에서 다른 템플릿을 호출하거나 재사용할 수 있다.

`include` 는 포함할 템플릿 이름과 템플릿의 컨텍스트(또는 범위)라는 두 가지 인수를 사용한다.

{{ include "template-name" . }}

 

 

일단 정의되면 `include` 를 사용하여 템플릿을 차트의 어느 곳에나 포함할 수 있다.

metadata:
  labels:
    {{ include "mychart.labels" . | nindent 4 }}

 

 

정의는 `templates/_helpers.tpl` 에 정의하면 된다. `helm create` 로 생성하면, 기본적으로 정의된다.

{{/*
Expand the name of the chart.
*/}}
{{- define "somaz.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "somaz.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 }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "somaz.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "somaz.labels" -}}
helm.sh/chart: {{ include "somaz.chart" . }}
{{ include "somaz.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "somaz.selectorLabels" -}}
app.kubernetes.io/name: {{ include "somaz.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "somaz.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "somaz.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

 

 

 


 

Helm Chart Template 활용

이제 deployment template 을 예시로 활용해서 template 작성후해보자.

아래와 같이 작성이 가능하다.

 

 

`templates/deployment.yaml`

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "somaz.fullname" . }}
  labels:
    {{- include "somaz.labels" . | nindent 4 }}
spec:
  revisionHistoryLimit: {{ .Values.revisionHistoryLimit }}
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "somaz.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      {{- with .Values.podAnnotations }}
      annotations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      labels:
        {{- include "somaz.selectorLabels" . | nindent 8 }}
    spec:
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      serviceAccountName: {{ include "somaz.serviceAccountName" . }}
      securityContext:
        {{- toYaml .Values.podSecurityContext | nindent 8 }}
      volumes:
        {{- range $persistentVolumeClaim := .Values.persistentVolumeClaims }}
        - name: {{ $persistentVolumeClaim.type }}
          persistentVolumeClaim:
            claimName: {{ $persistentVolumeClaim.name }}
        {{- end}}
      containers:
        - name: {{ .Chart.Name }}
          securityContext:
            {{- toYaml .Values.securityContext | nindent 12 }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          env:
          {{- range $key, $value := .Values.envConfig }}
            - name: {{ $key }}
              value: {{ $value | quote }}
          {{- end }}
          envFrom:
          {{- range $config := .Values.configs }}
            - configMapRef:
                name: {{ $config.name }}
          {{- end}}
          ports:
            - name: http
              containerPort: {{ .Values.service.targetPort }}
              protocol: TCP
          volumeMounts:
            {{- range $persistentVolumeClaim := .Values.persistentVolumeClaims }}
            - name: {{ $persistentVolumeClaim.type }}
              mountPath: {{ $persistentVolumeClaim.mountPath }}
            {{- end}}
          livenessProbe:
            {{- toYaml .Values.livenessProbe | nindent 12 }}
          readinessProbe:
            {{- toYaml .Values.readinessProbe | nindent 12 }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
      {{- end }}

 

 

 

`somaz.values.yaml`

# Default values for somaz.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 1

image:
  repository: harbor.somaz.link/somaz/game
  pullPolicy: IfNotPresent
  # Overrides the image tag whose default is the chart appVersion.
  tag: "362aa67"

imagePullSecretCreate:
  enabled: true
  name: harbor-robot-secret
  dockerconfigjson: # dockerconfigjson

imagePullSecrets:
  - name: harbor-robot-secret

nameOverride: "somaz"
fullnameOverride: "somaz"

serviceAccount:
  # Specifies whether a service account should be created
  create: true
  # Annotations to add to the service account
  annotations: {}
  # The name of the service account to use.
  # If not set and create is true, a name is generated using the fullname template
  name: ""

podAnnotations: {}

podSecurityContext: {}
  # fsGroup: 2000

securityContext: {}
  # capabilities:
  #   drop:
  #   - ALL
  # readOnlyRootFilesystem: true
  # runAsNonRoot: true
  # runAsUser: 1000

service:
  type: ClusterIP
  port: 80
  targetPort: 3000

ingress:
  name: somaz-ingress
  enabled: true
  className: ""
  annotations: 
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: clouddns-issuer # your clusterissuer
  hosts:
    - host: dev1-game.somaz.link
      paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: somaz-svc
              port: 80    
  tls: 
   - secretName: dev1-game-tls
     hosts:
       - dev1-game.somaz.link

resources: {}
  # We usually recommend not to specify default resources and to leave this as a conscious
  # choice for the user. This also increases chances charts run on environments with little
  # resources, such as Minikube. If you do want to specify resources, uncomment the following
  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
  # limits:
  #   cpu: 100m
  #   memory: 128Mi
  # requests:
  #   cpu: 100m
  #   memory: 128Mi

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 100
  targetCPUUtilizationPercentage: 80
  # targetMemoryUtilizationPercentage: 80

rbac:
  enabled: true
  serviceAccountName: "" # Leave empty to use admin.fullname as default
  role:
    name: cert-job-manager-game
    resources:
      - apiGroups: ["cert-manager.io"]
        resources: ["certificaterequests"]
        verbs: ["get", "list", "watch", "delete"]
      - apiGroups: ["batch"]
        resources: ["jobs"]
        verbs: ["get", "list", "watch", "delete"]
  roleBinding:
    name: cert-job-manager-binding-game

nodeSelector: {}

tolerations: []

affinity: {}

revisionHistoryLimit: 1

namespace: somaz-dev1

envConfig:
  NODE_ENV: dev1

configs:
  - name: somaz-dev1-config
    namespace: somaz-dev1
    datas:
      SERVER_PORT: 3000
      REDIS_DB_HOST: dev1-redis.somaz.link
      REDIS_DB_PORT: 30480
      ADMIN_DB_HOST: dev1-db.somaz.link
      ADMIN_DB_PORT: 30737
      ADMIN_DB_NAME: admin
      ADMIN_DB_ID: somaz
      ADMIN_DB_PW: somaz94
      ADMIN_DB_SYNCHRONIZE: true

persistentVolumes:
  - name: somaz-dev1-data-pv
    type: data
    storage: 5Gi
    volumeMode: Filesystem
    accessModes:
      - ReadWriteOnce
    reclaimPolicy: Retain
    storageClassName: nfs-client
    path: /data/somaz/gamedata/dev1/data
    server: nfs-server.somaz.link

persistentVolumeClaims:
  - name: somaz-dev1-data-pv-claim
    accessModes:
      - ReadWriteOnce
    storageClassName: nfs-client
    storage: 5Gi
    type: data
    mountPath: /app/data

# AWS
certificate:
  enabled: true
  secretName: dev1-game-tls
  commonName: dev1-game.somaz.link
  duration: 2160h0m0s # 90d
  renewBefore: 720h0m0s # 30d
  dnsNames:
  - dev1-game.somaz.link
  issuerName: route53-issuer
  issuerKind: ClusterIssuer

# # GCP
# certificate:
#   enabled: true
#   secretName: dev1-game-tls
#   commonName: dev1-game.somaz.link
#   duration: 2160h0m0s # 90d
#   renewBefore: 720h0m0s # 30d
#   dnsNames:
#   - dev1-game.somaz.link
#   issuerName: clouddns-issuer
#   issuerKind: ClusterIssuer

certCleanup:
  enabled: false
  CronJobName: cert-cleanup-cronjob-game
  olderThanDays: 100
  YesterDays: 1

 

 

Create ImagePullSecret

아래와 같이 `helper template` 과 조합하여 `imagepullsecret` 을 쉽게 만들 수 있다.

 

`templates/_helpers.tpl`

{{- define "imagePullSecret" }}
{{- with .Values.imageCredentials }}
{{- printf "{\"auths\":{\"%s\":{\"username\":\"%s\",\"password\":\"%s\",\"email\":\"%s\",\"auth\":\"%s\"}}}" .registry .username .password .email (printf "%s:%s" .username .password | b64enc) | b64enc }}
{{- end }}
{{- end }}

 

`templates/imagePullSecret.yaml`

{{- if .Values.imageCredentials.enabled -}}
apiVersion: v1
kind: Secret
metadata:
  name: {{ .Values.imageCredentials.name }}
  labels:
    {{- include "somaz.labels" . | nindent 4 }}
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: {{ template "imagePullSecret" . }}
{{- end }}

 

 

`values.yaml`

imageCredentials:
  enabled: true
  registry: quay.io
  username: someone
  password: sillyness
  email: someone@host.com

 

 

 


Reference

https://helm.sh/ko/docs/chart_best_practices/templates/

https://github.com/somaz94/helm-chart-template

https://helm.sh/ko/docs/howto/charts_tips_and_tricks/

728x90
반응형