Skip to content

Split View: Helm 아키텍처 내부 분석: Helm 3의 설계 철학과 구조

|

Helm 아키텍처 내부 분석: Helm 3의 설계 철학과 구조


1. Helm 3 아키텍처 개요

1.1 Helm 2에서 Helm 3로의 변화

Helm 2는 클러스터 내에 Tiller라는 서버 사이드 컴포넌트가 필요했습니다. Tiller는 클러스터 내에서 릴리스를 관리하고 차트를 배포하는 역할을 했지만, 보안상 심각한 문제가 있었습니다.

Tiller의 문제점:

  • 기본적으로 cluster-admin 권한으로 실행
  • gRPC 엔드포인트가 인증 없이 노출
  • 멀티테넌트 환경에서 권한 분리가 불가능
  • RBAC 우회의 원인

Helm 3에서는 Tiller가 완전히 제거되었습니다. 이제 Helm CLI가 직접 Kubernetes API 서버와 통신하며, kubeconfig의 인증 정보를 그대로 사용합니다.

1.2 클라이언트 전용 아키텍처

+------------------+       +---------------------+
|   Helm CLI       | ----> |  Kubernetes API     |
|  (클라이언트)     |       |  Server             |
+------------------+       +---------------------+
        |                           |
        v                           v
  +-----------+              +-----------+
  | Chart     |              | Release   |
  | Repository|              | Storage   |
  +-----------+              | (Secrets) |
                             +-----------+

Helm 3의 아키텍처는 단순합니다:

  1. Helm CLI: 차트 렌더링, API 호출, 릴리스 관리를 모두 클라이언트에서 수행
  2. Kubernetes API Server: 리소스 생성/수정/삭제의 실제 실행
  3. Release Storage: 릴리스 메타데이터를 Secret 또는 ConfigMap으로 저장

1.3 보안 모델 개선

# Helm 3는 kubeconfig의 컨텍스트를 그대로 사용
# RBAC이 자연스럽게 적용됨
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: helm-deployer
  namespace: production
rules:
  - apiGroups: ['', 'apps', 'batch']
    resources: ['deployments', 'services', 'configmaps', 'secrets']
    verbs: ['get', 'list', 'create', 'update', 'patch', 'delete']

Helm 3에서는 사용자의 kubeconfig 컨텍스트가 그대로 적용되므로, Kubernetes RBAC을 통한 세밀한 권한 제어가 가능합니다.


2. 릴리스 저장소(Release Storage)

2.1 Secret 기반 저장

Helm 3는 릴리스 정보를 기본적으로 Kubernetes Secret 리소스에 저장합니다.

# 릴리스 Secret 조회
kubectl get secrets -l owner=helm -n default

# Secret 이름 형식: sh.helm.release.v1.RELEASE_NAME.vREVISION
# 예: sh.helm.release.v1.my-app.v1

각 Secret에는 다음 정보가 gzip 압축 + base64 인코딩되어 저장됩니다:

  • 차트 메타데이터 (Chart.yaml 내용)
  • 렌더링된 매니페스트
  • values (사용자가 제공한 값 + 기본값)
  • 릴리스 상태 (deployed, failed, uninstalled 등)
  • 릴리스 노트

2.2 ConfigMap 기반 저장

Secret 대신 ConfigMap을 사용할 수도 있습니다:

# ConfigMap 드라이버 사용
export HELM_DRIVER=configmap
helm install my-app ./my-chart

2.3 저장 드라이버 비교

드라이버장점단점
secret (기본)etcd 암호화 적용 가능, RBAC으로 접근 제한Secret 리소스 크기 제한 (1MB)
configmapSecret 접근 권한 불필요데이터가 평문 저장
sql대규모 릴리스 관리외부 DB 필요, 설정 복잡
memory테스트용영구 저장 불가

3. 차트 구조(Chart Structure)

3.1 기본 디렉터리 구조

my-chart/
  Chart.yaml          # 차트 메타데이터 (필수)
  Chart.lock          # 의존성 잠금 파일
  values.yaml         # 기본 설정값 (필수)
  values.schema.json  # values 스키마 검증
  charts/             # 의존성 차트 (서브차트)
  crds/               # CRD 매니페스트
  templates/          # Go 템플릿 파일
    deployment.yaml
    service.yaml
    ingress.yaml
    _helpers.tpl      # 네임드 템플릿 헬퍼
    NOTES.txt         # 릴리스 노트 템플릿
    tests/            # 테스트 Pod 정의
      test-connection.yaml

3.2 Chart.yaml 상세

apiVersion: v2 # Helm 3에서는 반드시 v2
name: my-application
description: A Helm chart for my application
type: application # application 또는 library
version: 1.2.3 # 차트 버전 (SemVer)
appVersion: '2.0.0' # 애플리케이션 버전
kubeVersion: '>=1.25.0' # 지원 Kubernetes 버전 범위
home: https://example.com
sources:
  - https://github.com/example/my-app
maintainers:
  - name: developer
    email: dev@example.com
icon: https://example.com/icon.png
keywords:
  - app
  - web
annotations:
  artifacthub.io/changes: |
    - kind: added
      description: Initial release
dependencies:
  - name: postgresql
    version: '12.x.x'
    repository: 'https://charts.bitnami.com/bitnami'
    condition: postgresql.enabled
    tags:
      - database

3.3 CRD 디렉터리

crds/ 디렉터리에 있는 파일은 특별한 처리를 받습니다:

  • install 시에만 적용되며, upgrade나 rollback에서는 무시됩니다
  • 템플릿 렌더링이 적용되지 않습니다 (순수 YAML)
  • uninstall 시에도 삭제되지 않습니다 (데이터 보호)
# crds/my-crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: myresources.example.com
spec:
  group: example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
  scope: Namespaced
  names:
    plural: myresources
    singular: myresource
    kind: MyResource

4. 템플릿 엔진 기초

4.1 Go 템플릿 기본 문법

Helm은 Go의 text/template 패키지를 기반으로 하며, Sprig 라이브러리의 함수들을 추가로 제공합니다.

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: { { include "my-chart.fullname" . } }
  labels: { { - include "my-chart.labels" . | nindent 4 } }
spec:
  replicas: { { .Values.replicaCount } }
  selector:
    matchLabels: { { - include "my-chart.selectorLabels" . | nindent 6 } }
  template:
    metadata:
      labels: { { - include "my-chart.selectorLabels" . | nindent 8 } }
    spec:
      containers:
        - name: { { .Chart.Name } }
          image: '{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}'
          ports:
            - containerPort: { { .Values.service.port } }

4.2 빌트인 객체(Built-in Objects)

Helm 템플릿에서 사용할 수 있는 주요 빌트인 객체:

객체설명
.Release릴리스 정보 (Name, Namespace, Revision, IsUpgrade, IsInstall)
.Valuesvalues.yaml과 사용자 제공 값의 병합 결과
.ChartChart.yaml의 내용
.Capabilities클러스터 능력 정보 (API 버전, Kubernetes 버전)
.Template현재 템플릿 정보 (Name, BasePath)
.Files차트 내 파일 접근
# .Release 객체 활용 예시
metadata:
  annotations:
    helm.sh/release-name: {{ .Release.Name }}
    helm.sh/release-namespace: {{ .Release.Namespace }}
    helm.sh/revision: {{ .Release.Revision | quote }}

# .Capabilities 활용 예시
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
apiVersion: networking.k8s.io/v1
{{- else }}
apiVersion: networking.k8s.io/v1beta1
{{- end }}

4.3 Sprig 함수 예시

# 문자열 함수
name: {{ .Values.name | upper }}           # 대문자 변환
name: {{ .Values.name | lower }}           # 소문자 변환
name: {{ .Values.name | title }}           # 타이틀 케이스
name: {{ .Values.name | trim }}            # 공백 제거
name: {{ .Values.name | trunc 63 }}        # 63자로 자르기

# 기본값 설정
image: {{ .Values.image.tag | default "latest" }}

# 조건부 필수값
name: {{ required "A name is required" .Values.name }}

# 인코딩
data: {{ .Values.secret | b64enc }}

# 날짜
timestamp: {{ now | date "2006-01-02T15:04:05Z07:00" }}

5. 의존성 관리(Dependency Management)

5.1 Chart.yaml에서 의존성 선언

# Chart.yaml
dependencies:
  - name: redis
    version: '17.x.x'
    repository: 'https://charts.bitnami.com/bitnami'
    condition: redis.enabled
    tags:
      - cache
  - name: postgresql
    version: '12.x.x'
    repository: 'https://charts.bitnami.com/bitnami'
    condition: postgresql.enabled
    tags:
      - database
  - name: common
    version: '2.x.x'
    repository: 'https://charts.bitnami.com/bitnami'
    import-values:
      - child: image
        parent: defaultImage

5.2 Chart.lock 파일

# 의존성 다운로드 및 잠금 파일 생성
helm dependency update ./my-chart

# Chart.lock이 이미 있는 경우 정확한 버전으로 빌드
helm dependency build ./my-chart

Chart.lock 파일은 재현 가능한 빌드를 보장합니다:

# Chart.lock
dependencies:
  - name: redis
    repository: https://charts.bitnami.com/bitnami
    version: 17.15.2
  - name: postgresql
    repository: https://charts.bitnami.com/bitnami
    version: 12.10.0
digest: sha256:abc123def456...
generated: '2026-03-20T10:00:00.000000000Z'

5.3 condition과 tags

condition: 특정 값이 true일 때만 서브차트를 활성화

# values.yaml
redis:
  enabled: true
postgresql:
  enabled: false # PostgreSQL 서브차트 비활성화

tags: 태그 그룹 단위로 서브차트를 활성화/비활성화

# values.yaml
tags:
  cache: true # cache 태그가 붙은 모든 의존성 활성화
  database: false # database 태그가 붙은 모든 의존성 비활성화

condition은 tags보다 우선순위가 높습니다.

5.4 서브차트 값 오버라이드

# 부모 차트의 values.yaml에서 서브차트 값 오버라이드
redis:
  enabled: true
  architecture: standalone
  auth:
    enabled: false
  master:
    persistence:
      size: 1Gi

postgresql:
  enabled: true
  auth:
    postgresPassword: 'my-password'
    database: mydb

5.5 전역 값(Global Values)

# values.yaml
global:
  imageRegistry: myregistry.io
  imagePullSecrets:
    - name: my-pull-secret
  storageClass: fast-ssd

global 아래의 값은 모든 서브차트에서 접근 가능합니다.


6. OCI 레지스트리 지원

6.1 OCI 기반 차트 관리

Helm 3.8부터 OCI 레지스트리 지원이 GA(Generally Available)입니다:

# 레지스트리 로그인
helm registry login registry.example.com

# 차트를 OCI 아티팩트로 패키징 및 푸시
helm package ./my-chart
helm push my-chart-1.0.0.tgz oci://registry.example.com/charts

# OCI 레지스트리에서 차트 설치
helm install my-release oci://registry.example.com/charts/my-chart --version 1.0.0

# 차트 정보 조회
helm show chart oci://registry.example.com/charts/my-chart --version 1.0.0

7. 정리

Helm 3의 아키텍처는 보안, 단순성, 확장성을 핵심 설계 원칙으로 합니다:

  1. Tiller 제거: 클러스터 내 서버 컴포넌트 없이 CLI만으로 동작
  2. RBAC 네이티브: kubeconfig 기반 인증/인가로 Kubernetes RBAC과 자연스럽게 통합
  3. Secret 기반 릴리스 저장: etcd 암호화 지원, 릴리스 히스토리 관리
  4. 구조화된 차트: Chart.yaml, values.yaml, templates/ 등 명확한 관심사 분리
  5. OCI 레지스트리 지원: 컨테이너 이미지와 동일한 인프라에서 차트 관리

다음 글에서는 Helm 템플릿 엔진의 내부 동작을 더 깊이 분석합니다.

Helm Architecture Internals: Design Philosophy and Structure of Helm 3


1. Helm 3 Architecture Overview

1.1 Evolution from Helm 2 to Helm 3

Helm 2 required a server-side component called Tiller within the cluster. While Tiller managed releases and deployed charts, it had serious security issues.

Tiller's Problems:

  • Ran with cluster-admin privileges by default
  • gRPC endpoint exposed without authentication
  • Impossible to separate privileges in multi-tenant environments
  • Caused RBAC bypass

Helm 3 completely removed Tiller. Now the Helm CLI communicates directly with the Kubernetes API server, using the authentication credentials from kubeconfig.

1.2 Client-Only Architecture

+------------------+       +---------------------+
|   Helm CLI       | ----> |  Kubernetes API     |
|  (Client)        |       |  Server             |
+------------------+       +---------------------+
        |                           |
        v                           v
  +-----------+              +-----------+
  | Chart     |              | Release   |
  | Repository|              | Storage   |
  +-----------+              | (Secrets) |
                             +-----------+

Helm 3's architecture is simple:

  1. Helm CLI: Performs chart rendering, API calls, and release management entirely client-side
  2. Kubernetes API Server: Actual execution of resource creation/modification/deletion
  3. Release Storage: Stores release metadata as Secrets or ConfigMaps

1.3 Improved Security Model

# Helm 3 uses kubeconfig context directly
# RBAC is naturally applied
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: helm-deployer
  namespace: production
rules:
  - apiGroups: ['', 'apps', 'batch']
    resources: ['deployments', 'services', 'configmaps', 'secrets']
    verbs: ['get', 'list', 'create', 'update', 'patch', 'delete']

In Helm 3, the user's kubeconfig context is applied directly, enabling fine-grained permission control through Kubernetes RBAC.


2. Release Storage

2.1 Secret-Based Storage

Helm 3 stores release information in Kubernetes Secret resources by default.

# Query release Secrets
kubectl get secrets -l owner=helm -n default

# Secret name format: sh.helm.release.v1.RELEASE_NAME.vREVISION
# Example: sh.helm.release.v1.my-app.v1

Each Secret contains the following data, gzip-compressed and base64-encoded:

  • Chart metadata (Chart.yaml content)
  • Rendered manifests
  • Values (user-provided + defaults merged)
  • Release status (deployed, failed, uninstalled, etc.)
  • Release notes

2.2 ConfigMap-Based Storage

ConfigMap can be used instead of Secrets:

# Use ConfigMap driver
export HELM_DRIVER=configmap
helm install my-app ./my-chart

2.3 Storage Driver Comparison

DriverProsCons
secret (default)etcd encryption support, RBAC access controlSecret resource size limit (1MB)
configmapNo Secret access permissions neededData stored in plaintext
sqlLarge-scale release managementExternal DB required, complex setup
memoryTesting purposesNo persistent storage

3. Chart Structure

3.1 Basic Directory Structure

my-chart/
  Chart.yaml          # Chart metadata (required)
  Chart.lock          # Dependency lock file
  values.yaml         # Default configuration values (required)
  values.schema.json  # Values schema validation
  charts/             # Dependency charts (subcharts)
  crds/               # CRD manifests
  templates/          # Go template files
    deployment.yaml
    service.yaml
    ingress.yaml
    _helpers.tpl      # Named template helpers
    NOTES.txt         # Release notes template
    tests/            # Test Pod definitions
      test-connection.yaml

3.2 Chart.yaml Details

apiVersion: v2 # Must be v2 for Helm 3
name: my-application
description: A Helm chart for my application
type: application # application or library
version: 1.2.3 # Chart version (SemVer)
appVersion: '2.0.0' # Application version
kubeVersion: '>=1.25.0' # Supported Kubernetes version range
home: https://example.com
sources:
  - https://github.com/example/my-app
maintainers:
  - name: developer
    email: dev@example.com
icon: https://example.com/icon.png
keywords:
  - app
  - web
annotations:
  artifacthub.io/changes: |
    - kind: added
      description: Initial release
dependencies:
  - name: postgresql
    version: '12.x.x'
    repository: 'https://charts.bitnami.com/bitnami'
    condition: postgresql.enabled
    tags:
      - database

3.3 CRD Directory

Files in the crds/ directory receive special treatment:

  • Applied only during install; ignored during upgrade or rollback
  • Template rendering is not applied (plain YAML)
  • Not deleted during uninstall (data protection)
# crds/my-crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: myresources.example.com
spec:
  group: example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
  scope: Namespaced
  names:
    plural: myresources
    singular: myresource
    kind: MyResource

4. Template Engine Basics

4.1 Go Template Basic Syntax

Helm is based on Go's text/template package with additional Sprig library functions.

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: { { include "my-chart.fullname" . } }
  labels: { { - include "my-chart.labels" . | nindent 4 } }
spec:
  replicas: { { .Values.replicaCount } }
  selector:
    matchLabels: { { - include "my-chart.selectorLabels" . | nindent 6 } }
  template:
    metadata:
      labels: { { - include "my-chart.selectorLabels" . | nindent 8 } }
    spec:
      containers:
        - name: { { .Chart.Name } }
          image: '{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}'
          ports:
            - containerPort: { { .Values.service.port } }

4.2 Built-in Objects

Key built-in objects available in Helm templates:

ObjectDescription
.ReleaseRelease info (Name, Namespace, Revision, IsUpgrade, IsInstall)
.ValuesMerged result of values.yaml and user-provided values
.ChartContents of Chart.yaml
.CapabilitiesCluster capability info (API versions, Kubernetes version)
.TemplateCurrent template info (Name, BasePath)
.FilesAccess to chart files
# .Release object usage example
metadata:
  annotations:
    helm.sh/release-name: {{ .Release.Name }}
    helm.sh/release-namespace: {{ .Release.Namespace }}
    helm.sh/revision: {{ .Release.Revision | quote }}

# .Capabilities usage example
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
apiVersion: networking.k8s.io/v1
{{- else }}
apiVersion: networking.k8s.io/v1beta1
{{- end }}

4.3 Sprig Function Examples

# String functions
name: {{ .Values.name | upper }}           # Uppercase
name: {{ .Values.name | lower }}           # Lowercase
name: {{ .Values.name | title }}           # Title case
name: {{ .Values.name | trim }}            # Trim whitespace
name: {{ .Values.name | trunc 63 }}        # Truncate to 63 chars

# Default values
image: {{ .Values.image.tag | default "latest" }}

# Conditional required values
name: {{ required "A name is required" .Values.name }}

# Encoding
data: {{ .Values.secret | b64enc }}

# Date
timestamp: {{ now | date "2006-01-02T15:04:05Z07:00" }}

5. Dependency Management

5.1 Declaring Dependencies in Chart.yaml

# Chart.yaml
dependencies:
  - name: redis
    version: '17.x.x'
    repository: 'https://charts.bitnami.com/bitnami'
    condition: redis.enabled
    tags:
      - cache
  - name: postgresql
    version: '12.x.x'
    repository: 'https://charts.bitnami.com/bitnami'
    condition: postgresql.enabled
    tags:
      - database
  - name: common
    version: '2.x.x'
    repository: 'https://charts.bitnami.com/bitnami'
    import-values:
      - child: image
        parent: defaultImage

5.2 Chart.lock File

# Download dependencies and generate lock file
helm dependency update ./my-chart

# Build with exact versions from existing Chart.lock
helm dependency build ./my-chart

The Chart.lock file ensures reproducible builds:

# Chart.lock
dependencies:
  - name: redis
    repository: https://charts.bitnami.com/bitnami
    version: 17.15.2
  - name: postgresql
    repository: https://charts.bitnami.com/bitnami
    version: 12.10.0
digest: sha256:abc123def456...
generated: '2026-03-20T10:00:00.000000000Z'

5.3 condition and tags

condition: Enables subcharts only when a specific value is true

# values.yaml
redis:
  enabled: true
postgresql:
  enabled: false # Disable PostgreSQL subchart

tags: Enable/disable subcharts by tag group

# values.yaml
tags:
  cache: true # Enable all dependencies with cache tag
  database: false # Disable all dependencies with database tag

condition takes precedence over tags.

5.4 Subchart Value Overrides

# Override subchart values from parent values.yaml
redis:
  enabled: true
  architecture: standalone
  auth:
    enabled: false
  master:
    persistence:
      size: 1Gi

postgresql:
  enabled: true
  auth:
    postgresPassword: 'my-password'
    database: mydb

5.5 Global Values

# values.yaml
global:
  imageRegistry: myregistry.io
  imagePullSecrets:
    - name: my-pull-secret
  storageClass: fast-ssd

Values under global are accessible from all subcharts.


6. OCI Registry Support

6.1 OCI-Based Chart Management

OCI registry support is GA (Generally Available) since Helm 3.8:

# Login to registry
helm registry login registry.example.com

# Package and push chart as OCI artifact
helm package ./my-chart
helm push my-chart-1.0.0.tgz oci://registry.example.com/charts

# Install chart from OCI registry
helm install my-release oci://registry.example.com/charts/my-chart --version 1.0.0

# Show chart info
helm show chart oci://registry.example.com/charts/my-chart --version 1.0.0

7. Summary

Helm 3's architecture centers on security, simplicity, and extensibility:

  1. Tiller Removal: Operates with CLI only, no server component in the cluster
  2. RBAC Native: kubeconfig-based auth naturally integrates with Kubernetes RBAC
  3. Secret-Based Release Storage: etcd encryption support, release history management
  4. Structured Charts: Clear separation of concerns with Chart.yaml, values.yaml, templates/
  5. OCI Registry Support: Manage charts on the same infrastructure as container images

The next post will analyze the internals of the Helm template engine in greater depth.