데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      이것만 공부하세요 - helm chart 만드는 법

      seungkyua 23.11.21
      8,482 19 3
      DEVOTEE 요약
      본 블로그는 Helm chart의 작성과 활용에 대한 방법을 설명하고 있다. 먼저, 환경을 만들고 Helm chart를 생성하는 방법을 보여주며, 주요 기능 중 하나인 yaml 구성과 관련한 예제를 제공한다. 그 후, 다양한 함수를 사용해 yaml을 수정하고 Helm template로 출력하는 과정을 단계별로 설명한다. 또한, 반복문, 컨디션문, 템플릿 선언 및 활용 등의 고급 기능에 대한 설명과 예시도 포함되어 있다. 이러한 과정을 거쳐 사용자 정의 Helm chart를 만들 수 있으며, 이를 통해 쿠버네티스 배포 과정을 자동화 할 수 있다.
      DEVOTEE 추천 블로그

      Helm chart의 작성과 활용에 대한 방법을 설명하고자 본 블로그를 작성한다.

      이것만 알면 간단한 누구나 helm chart 를 만들어 볼 수 있기를 바래보면서 간략한 실습 위주로 정리해 본다.


      helm chart 를 만들기 위해서는 여러 기능들을 알아야 하지만 그 중에서 가장 많이 쓰고 헷갈리는 기능에 대해서 살펴 본다.

      기본적으로 실습할 수 있는 환경을 먼저 만들고 하나씩 공부해 본다.

      $ helm create flow-control
      $ cd flow-control

      yaml 형태의 출력을 확인해 본다.

      $ helm template .
      ---
      # Source: flow-control/templates/serviceaccount.yaml
      apiVersion: v1
      kind: ServiceAccount
      metadata:
        name: release-name-flow-control
        labels:
          helm.sh/chart: flow-control-0.1.0
          app.kubernetes.io/name: flow-control
          app.kubernetes.io/instance: release-name
          app.kubernetes.io/version: "1.16.0"
          app.kubernetes.io/managed-by: Helm
      automountServiceAccountToken: true
      ---
      # Source: flow-control/templates/service.yaml
      apiVersion: v1
      kind: Service
      metadata:
        name: release-name-flow-control
        labels:
          helm.sh/chart: flow-control-0.1.0
          app.kubernetes.io/name: flow-control
          app.kubernetes.io/instance: release-name
          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: flow-control
          app.kubernetes.io/instance: release-name
      ---
      # Source: flow-control/templates/deployment.yaml
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: release-name-flow-control
        labels:
          helm.sh/chart: flow-control-0.1.0
          app.kubernetes.io/name: flow-control
          app.kubernetes.io/instance: release-name
          app.kubernetes.io/version: "1.16.0"
          app.kubernetes.io/managed-by: Helm
      spec:
        replicas: 1
        selector:
          matchLabels:
            app.kubernetes.io/name: flow-control
            app.kubernetes.io/instance: release-name
        template:
          metadata:
            labels:
              helm.sh/chart: flow-control-0.1.0
              app.kubernetes.io/name: flow-control
              app.kubernetes.io/instance: release-name
              app.kubernetes.io/version: "1.16.0"
              app.kubernetes.io/managed-by: Helm
          spec:
            serviceAccountName: release-name-flow-control
            securityContext:
              {}
            containers:
              - name: flow-control
                securityContext:
                  {}
                image: "nginx:1.16.0"
                imagePullPolicy: IfNotPresent
                ports:
                  - name: http
                    containerPort: 80
                    protocol: TCP
                livenessProbe:
                  httpGet:
                    path: /
                    port: http
                readinessProbe:
                  httpGet:
                    path: /
                    port: http
                resources:
                  {}
      ---
      # Source: flow-control/templates/tests/test-connection.yaml
      apiVersion: v1
      kind: Pod
      metadata:
        name: "release-name-flow-control-test-connection"
        labels:
          helm.sh/chart: flow-control-0.1.0
          app.kubernetes.io/name: flow-control
          app.kubernetes.io/instance: release-name
          app.kubernetes.io/version: "1.16.0"
          app.kubernetes.io/managed-by: Helm
        annotations:
          "helm.sh/hook": test
      spec:
        containers:
          - name: wget
            image: busybox
            command: ['wget']
            args: ['release-name-flow-control:80']
        restartPolicy: Never

      제대로 출력된다면 필요없는 파일은 삭제하고 초기화 하자.

      $ rm -rf template/*
      $ cat /dev/null > values.yaml


      0. yaml 은 들여 쓰기가 중요하다

      가장 간단한 configmap 을 만들어서 value 값을 출력한다.

      $ values.yaml
      ---
      favorite:
        drink: coffee
        food: pizza
      
      $ vi template/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
      data:
        myvalue: "Hello World"
        drink: {{ .Values.favorite.drink | default "tea" | quote }}
        food: {{ .Values.favorite.food | upper | quote }}
        {{ if eq .Values.favorite.drink "coffee" }}
          mug: "true"
        {{ end }}
      
      $ helm template .
      --- output ---
      Error: YAML parse error on flow-control/templates/configmap.yaml: error converting YAML to JSON: yaml: line 8: did not find expected key
      
      Use --debug flag to render out invalid YAML

      template 을 제너레이션하면 에러가 발생한다. 왜 그럴까?

      configmap.yaml 에 mug: "true" 가 2칸 들여써 있어서 발생하는 에러이다. 이런 에러를 조심하면서 아래 실습을 해보자.


      1. 조건문과 빈라인 없애기

      configmap.yaml 을 수정해서 제대로 yaml 을 생성해 보자.

      $ vi template/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
      data:
        myvalue: "Hello World"
        drink: {{ .Values.favorite.drink | default "tea" | quote }}
        food: {{ .Values.favorite.food | upper | quote }}
        {{ if eq .Values.favorite.drink "coffee" }}
        mug: "true"
        {{ end }}
      
      $ helm template .
      ---
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
      data:
        myvalue: "Hello World"
        drink: "coffee"
        food: "PIZZA"
                          --------------> 빈라인 발생한다.
        mug: "true"
                          --------------> 빈라인 발생한다.

      .Release.Name 은 helm 으로 설치할 때 파라미터로 넘기는 값인데 여기서는 설치가 아니므로

      기본 값인 release-name 으로 치환 되었고, .Values.favorite.drink 는 values.yaml 에 지정한 키값으로 그에 해당하는 밸류 값이 제너레이트 될 때 출력된다.

      함수의 연속적 사용은 | 라인으로 호출 가능하며 default 는 키에 대한 값이 없을 때, quote 는 값을 " 으로 묶을 때 upper 는 값을 대문자로 변환할 때 사용하는 내장 함수이다.

      비교 구문은 if eq 값1 값2 와 같이 사용할 수 있다.


      출력하지 않는 곳에서는 빈라인이 발생하는데 이 부분을 다음과 같이 없애 줄 수 있다.

      {{ if eq .Values.favorite.drink "coffee" }}mug: "true"{{ end }}

      하지만 가독성이 떨어지므로 {{- 와 같이 표현하면 빈라인이 없어지면서 윗라인에 나란히 붙는 것과 같은 효과를 낼 수 있다.

        {{- if eq .Values.favorite.drink "coffee" }}
        mug: "true"
        {{- end }}

      다시 yaml 을 생성해 보면 아래와 같이 빈라인이 없어졌음을 알 수 있다.

      $ helm template .
      ---
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
      data:
        myvalue: "Hello World"
        drink: "coffee"
        food: "PIZZA"
        mug: "true"

      조건문 대신 다음과 같이 with 를 사용하여 조건문과 키밸류 스쿱을 지정할 수 있다.

      ### configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
      data:
        myvalue: "Hello World"
        {{- with .Values.hobby }}
        sports: {{ .sports }}
        {{- end }}
      
      ### values.yaml
      hobby:
        sports: golf

      with 와 함께 사용한 키는 해당 키에 대한 값이 있을 때만 with ~ end 로 감싼 구문이 출력된다.

      또한 감싼 구문 안에서는 스쿱이 재정의되어 hobby 아래의 키인 sports 를 .sports 로 바로 사용할 수 있다.


      yaml 을 생성하면 다음과 같은 결과가 나온다.

      $ helm template .
      
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
      data:
        myvalue: "Hello World"
        sports: golf

      만약 values 에 sports 를 없애면 아래와 같이 출력되지 않는다.

      ### values.yaml
      hobby: {}
      
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
      data:
        myvalue: "Hello World"

      hobby 아래의 키는 키밸류인 디셔너리 타입을 넣기 때문에 아래의 값을 모두 없애기 위해서{} 빈 딕셔너리 값으로 지정했다.

      만약 아래의 값이 리스트라면 [] 와 같이 빈 리스트 값을 지정할 수 있다.


      with ~ end 로 감싼 구문에서 root 영역의 value 를 활용하고 싶을 수 있다. 이 때는 $ 를 붙혀서 영역을 최상위 root 로 접근할 수 있다.

      아래 예제에서 $.Release.Name 을 참고한다.

      ### configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
      data:
        myvalue: "Hello World"
        {{- with .Values.hobby }}
        sports: {{ .sports }}
        release: {{ $.Release.Name }}
        {{- end }}

      YAML 은 JSON 의 수퍼셋이기 때문에 JSON 으로 표현하여 가독성을 높혀줄 수 도 있다. pod 를 만들 때 yaml 에 args 를 추가할 수 있는데.

      이 때 JSON 을 쓰면 읽기에 편하다.

      args:
        - "--dirname"
        - "/foo"  

      위의 내용은 JSON 으로 아래와 같이 바꿀 수 있다.

      args: ["--dirname", "/foo"]


      2. range 함수를 이용한 반복문

      range 를 이용하여 반복문을 사용할 수 있다.

      ### values.yaml
      pizzaToppings:
        - mushrooms
        - cheese
        - peppers
        - onions
      
      ### configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
      data:
        toppings: |-
          {{- range .Values.pizzaToppings }}
          - {{ . | title | quote }}
          {{- end }}

      pizzaToppings 의 값은 리스트이다(- 기호가 값으로 붙었기 때문에 리스트임을 알 수 있다).

      리스트로 값을 가져와서 출력하기 때문에 아래와 같은 결과가 나온다.

      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
      data:
        toppings: |-
          - "Mushrooms"
          - "Cheese"
          - "Peppers"
          - "Onions"

      한가지 yaml 에서 toppings: 의 뒤에 따라온 |- 기호의 의미는 멀티 라인 스트링을 받겠다는 의미이다.


      tuple 을 사용하여 튜플로 만들어 쓸 수 도 있다.

      ### configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
      data:
        sizes: |-
          {{- range tuple "small" "medium" "large" }}
          - {{ . }}
          {{- end }}
      
      ### 출력 결과
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
      data:
        sizes: |-
          - small
          - medium
          - large

      $ 와 := 를 이용하여 변수를 지정할 수 있다. 아래는 리스트에서 받은 값을 index 변수와 value 변수로 받아서 활용하는 부분이다.

      ### values.yaml
      pizzaToppings:
        - mushrooms
        - cheese
        - peppers
        - onions
      
      ### configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
      data:
        toppings: |-
          {{- range $index, $value := .Values.pizzaToppings }}
          - {{ $index }}: {{ $value }}
          {{- end }}
      
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
      data:
        toppings: |-
          - 0: mushrooms
          - 1: cheese
          - 2: peppers
          - 3: onions

      map 값을 변수로 받아 처리할 수 도 있다.

      ### values.yaml
      favorite:
        drink: coffee
        food: pizza
      
      ### configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
      data:
        favorite: |-
          {{- range $key, $value := .Values.favorite }}
          {{ $key }}: {{ $value }}
          {{- end }}
      
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
      data:
        favorite: |-
          drink: coffee
          food: pizza


      3. template 선언 및 활용

      부분적으로 사용자 정의 template 을 만들어서 활용 할 수 있다.

      define - tempate 사용

      template 은 define 으로 선언하고 tempate 으로 활용할 수 있다.

      ### configmap.yaml
      {{- define "mychart.labels" }}
        labels:
          generator: helm
          date: {{ now | htmlDate }}
      {{- end }}
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
        {{- template "mychart.labels" }}
      data:
        myvalue: "Hello World"
      
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
        labels:
          generator: helm
          date: 2023-10-31
      data:
        myvalue: "Hello World"

      위의 예제는 labels 에 date: 날짜 를 추가로 넣는 부분을 named template 을 만들어서 사용한 예제이다.


      chat 를 만들 때 configmap.yaml 과 같이 쿠버네티스 리소스들은 template 디렉토리 아래에 위치 시킨다고 했다.

      이 디렉토리에 위치한 yaml 파일들은 자동으로 렌더링에 포함되는데 _ 로 시작하는 파일은 렌더링에서 제외한다.

      그래서 보통 define 으로 정의한 함수들은 _helper.tpl 파일을 만들어서 이곳에 위치 시킨다.


      define 으로 정의된 named template (함수) 은 template 으로 호출되기 전까지는 렌더링 되지 않는다.

      이제 이 함수를 _helper.tpl 파일로 옮겨서 렌더링 결과를 살펴보자.

      # _helper.tpl
      {{- define "mychart.labels" }}
        labels:
          generator: helm
          date: {{ now | htmlDate }}
      {{- end }}
      
      # configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
        {{- template "mychart.labels" }}
      data:
        myvalue: "Hello World"
      
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
        labels:
          generator: helm
          date: 2023-10-31
      data:
        myvalue: "Hello World"

      위의 예제에서 define 함수 내에서 .Values 와 같이 value 를 가져오는 것은 하지 않았다.

      아래와 같이 {{ .Chart.Name }} 을 사용한다면 위의 방식으로는 값을 표현할 수 없다.

      # _helper.tpl
      {{- define "mychart.labels" }}
        labels:
          generator: helm
          date: {{ now | htmlDate }}
          chart: {{ .Chart.Name }}
          version: {{ .Chart.Version }}
      {{- end }} 
      
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
        labels:
          generator: helm
          date: 2023-10-31
          chart:
          version:
      data:
        myvalue: "Hello World"

      이는 template 으로 호출할 때 뒤에 value 를 보내지 않아서 발생한 부분이다.

      즉 {{- template "mychart.labels" . }} 과 같이 마지막에 현재의 scope value 인 . 을 넘겨 주어야 제대로 된 값이 출력된다.

      # configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
        {{- template "mychart.labels" . }}
      data:
        myvalue: "Hello World"
      
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
        labels:
          generator: helm
          date: 2023-10-31
          chart: flow-control
          version: 0.1.0

      define - include 사용

      template 은 있는 그대로 output 을 보여주기 때문에 들여쓰기의 문제가 있을 수 있다.

      # _helper.tlp
      {{- define "mychart.app" -}}
      app_name: {{ .Chart.Name }}
      app_version: "{{ .Chart.Version }}"
      {{- end -}}
      
      # configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
        labels:
          {{ template "mychart.app" . }}
      data:
        myvalue: "Hello World"
      {{ template "mychart.app" . }}
      
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
        labels:
          app_name: flow-control
      app_version: "0.1.0"
      data:
        myvalue: "Hello World"
      app_name: flow-control
      app_version: "0.1.0"

      app_name 과 app_version 이 출력된 것을 보면 define 에 정의된 들여쓰기 대로 그대로 출력되어 원하는 대로 출력되지 않는다.


      include 와 nindent 를 사용하면 원하는 들여쓰기가 가능하다.

      # configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .Release.Name }}-configmap
        labels:
          {{ template "mychart.app" . }}
      data:
        myvalue: "Hello World"
      {{ template "mychart.app" . }}
      
      # Source: flow-control/templates/configmap.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: release-name-configmap
        labels:
          app_name: flow-control
          app_version: "0.1.0"
      data:
        myvalue: "Hello World"
        app_name: flow-control
        app_version: "0.1.0"

      끝으로 helm install 할 때 들여쓰기가 잘못되면 렌더링 오류가 나서 최종 결과를 볼 수 가 없다.

      이를 해결할 수 있는 옵션이 -disable-openapi-validation 이다.

      $ helm install --dry-run --disable-openapi-validation mychart ./

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      seungkyua 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기