Split View: Helm 아키텍처 내부 분석: Helm 3의 설계 철학과 구조
Helm 아키텍처 내부 분석: Helm 3의 설계 철학과 구조
- 1. Helm 3 아키텍처 개요
- 2. 릴리스 저장소(Release Storage)
- 3. 차트 구조(Chart Structure)
- 4. 템플릿 엔진 기초
- 5. 의존성 관리(Dependency Management)
- 6. OCI 레지스트리 지원
- 7. 정리
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의 아키텍처는 단순합니다:
- Helm CLI: 차트 렌더링, API 호출, 릴리스 관리를 모두 클라이언트에서 수행
- Kubernetes API Server: 리소스 생성/수정/삭제의 실제 실행
- 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) |
| configmap | Secret 접근 권한 불필요 | 데이터가 평문 저장 |
| 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) |
.Values | values.yaml과 사용자 제공 값의 병합 결과 |
.Chart | Chart.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의 아키텍처는 보안, 단순성, 확장성을 핵심 설계 원칙으로 합니다:
- Tiller 제거: 클러스터 내 서버 컴포넌트 없이 CLI만으로 동작
- RBAC 네이티브: kubeconfig 기반 인증/인가로 Kubernetes RBAC과 자연스럽게 통합
- Secret 기반 릴리스 저장: etcd 암호화 지원, 릴리스 히스토리 관리
- 구조화된 차트: Chart.yaml, values.yaml, templates/ 등 명확한 관심사 분리
- OCI 레지스트리 지원: 컨테이너 이미지와 동일한 인프라에서 차트 관리
다음 글에서는 Helm 템플릿 엔진의 내부 동작을 더 깊이 분석합니다.
Helm Architecture Internals: Design Philosophy and Structure of Helm 3
- 1. Helm 3 Architecture Overview
- 2. Release Storage
- 3. Chart Structure
- 4. Template Engine Basics
- 5. Dependency Management
- 6. OCI Registry Support
- 7. Summary
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:
- Helm CLI: Performs chart rendering, API calls, and release management entirely client-side
- Kubernetes API Server: Actual execution of resource creation/modification/deletion
- 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
| Driver | Pros | Cons |
|---|---|---|
| secret (default) | etcd encryption support, RBAC access control | Secret resource size limit (1MB) |
| configmap | No Secret access permissions needed | Data stored in plaintext |
| sql | Large-scale release management | External DB required, complex setup |
| memory | Testing purposes | No 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:
| Object | Description |
|---|---|
.Release | Release info (Name, Namespace, Revision, IsUpgrade, IsInstall) |
.Values | Merged result of values.yaml and user-provided values |
.Chart | Contents of Chart.yaml |
.Capabilities | Cluster capability info (API versions, Kubernetes version) |
.Template | Current template info (Name, BasePath) |
.Files | Access 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:
- Tiller Removal: Operates with CLI only, no server component in the cluster
- RBAC Native: kubeconfig-based auth naturally integrates with Kubernetes RBAC
- Secret-Based Release Storage: etcd encryption support, release history management
- Structured Charts: Clear separation of concerns with Chart.yaml, values.yaml, templates/
- 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.