본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 10. 30. · 16 Views
Helm 차트로 애플리케이션 패키징 완벽 가이드
Kubernetes 애플리케이션을 체계적으로 관리하고 배포하는 Helm 차트의 모든 것을 다룹니다. 차트 구조부터 템플릿 작성, 값 관리, 배포 전략까지 실무에 필요한 핵심 개념을 실전 예제와 함께 학습합니다.
목차
1. Helm 차트 기본 구조
시작하며
여러분이 Kubernetes에 애플리케이션을 배포할 때 수십 개의 YAML 파일을 일일이 관리하고 있나요? 환경마다 다른 설정값 때문에 파일을 복사해서 수정하고, 버전 관리도 제대로 안 되는 상황을 겪어본 적 있을 겁니다.
이런 문제는 실제 개발 현장에서 자주 발생합니다. 특히 개발, 스테이징, 프로덕션 환경이 따로 있거나, 여러 마이크로서비스를 운영할 때 YAML 파일 관리가 악몽이 됩니다.
하나의 설정을 바꾸려면 모든 파일을 찾아서 수정해야 하죠. 바로 이럴 때 필요한 것이 Helm 차트입니다.
Helm 차트는 Kubernetes 애플리케이션을 하나의 패키지로 묶어서 버전 관리하고, 재사용 가능하게 만들어줍니다. 마치 npm 패키지처럼 애플리케이션을 설치하고 업그레이드할 수 있게 해주죠.
개요
간단히 말해서, Helm 차트는 Kubernetes 리소스들을 정의하는 파일들의 집합입니다. 마치 요리 레시피처럼 애플리케이션 배포에 필요한 모든 재료와 순서를 담고 있습니다.
왜 차트 구조를 이해해야 할까요? 실무에서는 단순히 차트를 설치하는 것을 넘어서 커스터마이징이 필수입니다.
예를 들어, 회사의 보안 정책에 맞게 네트워크 설정을 변경하거나, 특정 환경에 맞는 리소스 할당을 조정해야 하는 경우가 많습니다. 기존에는 각 환경마다 별도의 YAML 파일 세트를 만들어 관리했다면, 이제는 하나의 차트에서 values 파일만 바꿔가며 모든 환경에 배포할 수 있습니다.
이는 관리 포인트를 획기적으로 줄여줍니다. Helm 차트의 핵심 특징은 크게 세 가지입니다.
첫째, 표준화된 디렉토리 구조로 누구나 쉽게 이해할 수 있습니다. 둘째, 템플릿 시스템으로 동적인 설정이 가능합니다.
셋째, 버전 관리와 롤백이 간편합니다. 이러한 특징들이 대규모 Kubernetes 환경에서 일관성과 안정성을 보장해줍니다.
코드 예제
# Helm 차트의 기본 디렉토리 구조
my-app-chart/
├── Chart.yaml # 차트 메타데이터 (이름, 버전 등)
├── values.yaml # 기본 설정값들
├── charts/ # 의존하는 서브차트들
├── templates/ # Kubernetes 매니페스트 템플릿
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── _helpers.tpl # 재사용 가능한 템플릿 헬퍼
│ └── NOTES.txt # 설치 후 안내 메시지
└── .helmignore # 패키징 시 제외할 파일 패턴
설명
이것이 하는 일: Helm 차트는 Kubernetes 애플리케이션을 배포하기 위한 모든 리소스와 설정을 체계적으로 조직화합니다. 마치 소프트웨어 패키지처럼 설치, 업그레이드, 삭제가 가능한 단위로 만들어줍니다.
첫 번째로, Chart.yaml 파일은 차트의 신원을 정의합니다. 이름, 버전, 설명, 유지보수자 정보 등 메타데이터를 담고 있어서 차트 저장소에서 검색하고 관리할 수 있게 해줍니다.
버전 관리가 중요한 이유는 롤백이나 업그레이드 시 정확한 시점을 특정할 수 있기 때문입니다. 그 다음으로, values.yaml이 실행되면서 모든 설정의 기본값을 제공합니다.
여기에 정의된 값들은 templates 디렉토리의 템플릿 파일들에서 참조됩니다. 환경별로 다른 values 파일을 사용하거나, helm install 시 --set 플래그로 특정 값만 오버라이드할 수 있습니다.
templates 디렉토리가 핵심입니다. 여기에는 실제 Kubernetes 매니페스트 파일들이 템플릿 형태로 저장됩니다.
Go 템플릿 문법을 사용해서 values.yaml의 값을 동적으로 주입하고, 조건부 로직도 구현할 수 있습니다. _helpers.tpl은 여러 템플릿에서 공통으로 사용하는 함수들을 정의하는 특별한 파일입니다.
여러분이 이 구조를 이해하면 어떤 Helm 차트든 빠르게 분석하고 수정할 수 있습니다. 실무에서는 공개 차트를 그대로 사용하기보다는 회사의 요구사항에 맞게 커스터마이징하는 경우가 많은데, 표준 구조 덕분에 학습 곡선이 낮습니다.
또한 차트를 직접 만들 때도 이 구조를 따르면 다른 팀원들이 쉽게 이해하고 유지보수할 수 있습니다.
실전 팁
💡 helm create my-chart 명령으로 표준 구조의 차트 스켈레톤을 자동 생성할 수 있습니다. 처음부터 직접 만들지 말고 이 명령으로 시작하면 베스트 프랙티스를 자동으로 따르게 됩니다.
💡 .helmignore 파일을 활용해서 개발용 파일들(예: README 초안, 테스트 스크립트)이 패키징에 포함되지 않도록 하세요. 차트 크기를 줄이고 보안 리스크도 감소시킵니다.
💡 templates 디렉토리에 NOTES.txt를 작성하면 helm install 후 사용자에게 유용한 정보를 표시할 수 있습니다. 접속 URL, 초기 비밀번호 확인 방법 등을 안내하면 사용자 경험이 크게 개선됩니다.
💡 복잡한 차트는 templates를 서브디렉토리로 구조화할 수 있습니다(예: templates/backend/, templates/frontend/). Helm은 자동으로 모든 하위 디렉토리를 스캔합니다.
💡 helm lint 명령으로 차트의 문법 오류와 베스트 프랙티스 위반 사항을 배포 전에 검사하세요. CI/CD 파이프라인에 통합하면 품질을 자동으로 보장할 수 있습니다.
2. Chart.yaml 작성법
시작하며
여러분이 차트를 만들었는데 버전 관리가 제대로 안 되거나, 차트 저장소에서 검색이 안 되는 경험을 해본 적 있나요? 혹은 의존성 차트의 버전 충돌로 배포가 실패하는 상황도 흔합니다.
이런 문제는 Chart.yaml 파일이 제대로 작성되지 않았을 때 발생합니다. Chart.yaml은 차트의 메타데이터를 정의하는 가장 중요한 파일이지만, 많은 개발자들이 단순히 이름과 버전만 채우고 넘어가는 경우가 많습니다.
바로 이럴 때 필요한 것이 체계적인 Chart.yaml 작성법입니다. 올바른 메타데이터는 차트의 검색성, 유지보수성, 의존성 관리를 크게 향상시킵니다.
특히 여러 팀이 차트 저장소를 공유할 때 그 차이가 극명하게 드러납니다.
개요
간단히 말해서, Chart.yaml은 차트의 신원증명서입니다. 이름, 버전, 설명 같은 필수 정보부터 유지보수자, 키워드, 의존성까지 차트에 대한 모든 메타데이터를 담습니다.
왜 이 파일이 중요할까요? 실무에서는 수십, 수백 개의 차트를 관리하게 됩니다.
예를 들어, 마이크로서비스 아키텍처에서 각 서비스마다 차트가 있고, 공통 인프라 컴포넌트들도 차트로 관리할 때, 정확한 메타데이터가 없으면 어떤 차트가 무엇을 하는지 찾기가 매우 어렵습니다. 기존에는 README 파일이나 별도의 문서로 이런 정보를 관리했다면, Chart.yaml에 표준화된 형식으로 정의하면 helm search나 Artifact Hub 같은 도구들이 자동으로 인덱싱하고 검색할 수 있게 됩니다.
Chart.yaml의 핵심 특징은 시맨틱 버저닝 지원, 다중 버전 API 스키마, 그리고 의존성 선언입니다. 버전 필드는 차트의 변경 이력을 추적하고, appVersion은 패키징된 애플리케이션의 실제 버전을 나타냅니다.
이 둘을 구분하는 것이 중요합니다.
코드 예제
apiVersion: v2 # Helm 3는 v2 사용
name: my-web-app # 차트 이름 (소문자, 하이픈 사용)
description: A Helm chart for deploying a web application with PostgreSQL
type: application # application 또는 library
version: 1.2.3 # 차트 자체의 버전 (SemVer 형식)
appVersion: "2.1.0" # 패키징된 앱의 버전
# 검색과 분류를 위한 메타데이터
keywords:
- web
- nodejs
- postgresql
home: https://github.com/myorg/my-web-app
sources:
- https://github.com/myorg/my-web-app-chart
maintainers:
- name: DevOps Team
email: devops@example.com
# 의존성 차트 선언
dependencies:
- name: postgresql
version: "~12.0.0" # 12.0.x 버전 허용
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled # values.yaml로 활성화 제어
설명
이것이 하는 일: Chart.yaml은 Helm에게 차트의 정체성과 요구사항을 알려줍니다. 설치나 업그레이드 시 이 파일을 먼저 읽어서 호환성을 확인하고 의존성을 해결합니다.
첫 번째로, apiVersion과 type 필드가 차트의 기본 형식을 정의합니다. apiVersion: v2는 Helm 3의 표준 형식이고, type은 application(배포 가능한 앱)과 library(재사용 가능한 템플릿 라이브러리) 중 하나를 선택합니다.
대부분의 경우 application을 사용하지만, 공통 템플릿을 만들 때는 library를 사용합니다. 그 다음으로, version과 appVersion이 실행됩니다.
version은 차트 자체의 버전으로, 템플릿이나 설정이 바뀔 때마다 증가시킵니다. appVersion은 차트가 배포하는 애플리케이션의 실제 버전입니다.
예를 들어, nginx 1.21을 배포하는 차트의 버전이 3.0.0일 수 있습니다. 이 구분이 중요한 이유는 같은 앱 버전을 다른 차트 버전으로 여러 번 패키징할 수 있기 때문입니다.
dependencies 섹션이 가장 강력한 기능입니다. 여기서 선언한 차트들은 helm dependency update 명령으로 자동으로 다운로드되어 charts/ 디렉토리에 저장됩니다.
version 필드에 ~12.0.0처럼 범위를 지정하면 마이너 버전 업데이트를 자동으로 받을 수 있습니다. condition 필드를 사용하면 values.yaml의 설정으로 의존성을 선택적으로 활성화할 수 있어, 하나의 차트로 다양한 구성을 지원할 수 있습니다.
keywords와 maintainers 필드는 검색과 협업에 필수적입니다. 키워드는 차트 저장소에서 검색할 때 사용되고, 유지보수자 정보는 문제가 생겼을 때 누구에게 연락할지 알려줍니다.
home과 sources는 문서와 소스 코드를 찾을 수 있게 해줍니다. 여러분이 이 파일을 제대로 작성하면 차트의 생명주기 관리가 자동화됩니다.
CI/CD 파이프라인에서 버전을 자동으로 증가시키고, 의존성을 자동으로 업데이트하고, 차트 저장소에 자동으로 게시하는 워크플로우를 구축할 수 있습니다. 또한 팀원들이 차트를 처음 봤을 때도 메타데이터만 보고 빠르게 이해할 수 있습니다.
실전 팁
💡 version은 항상 시맨틱 버저닝(MAJOR.MINOR.PATCH)을 따르세요. 호환성이 깨지는 변경은 MAJOR 증가, 새 기능은 MINOR, 버그 수정은 PATCH를 증가시킵니다.
💡 appVersion은 문자열로 작성하세요(인용부호 사용). 숫자로만 이루어진 버전도 "1.0"처럼 문자열로 써야 YAML 파싱 오류를 방지할 수 있습니다.
💡 dependencies의 버전 범위는 신중하게 설정하세요. ~(틸드)는 마이너 버전까지, ^(캐럿)는 메이저 버전까지 업데이트를 허용합니다. 프로덕션 환경에서는 정확한 버전 지정을 권장합니다.
💡 deprecated: true 필드로 차트가 더 이상 사용되지 않음을 표시할 수 있습니다. 사용자들에게 대체 차트로 마이그레이션하도록 유도할 때 유용합니다.
💡 annotations 필드로 커스텀 메타데이터를 추가할 수 있습니다. 예를 들어, 차트의 카테고리, 라이선스 정보, 변경 이력 URL 등을 자유롭게 정의하세요.
3. values.yaml 설계
시작하며
여러분이 개발, 스테이징, 프로덕션 환경마다 다른 설정값을 관리하느라 고생한 적 있나요? CPU 리소스, 레플리카 수, 데이터베이스 연결 정보 등 환경마다 달라지는 수십 개의 설정을 어떻게 추적하고 계신가요?
이런 문제는 설정 관리가 체계화되지 않았을 때 발생합니다. 하드코딩된 값들이 템플릿 곳곳에 흩어져 있으면 변경이 어렵고, 실수로 잘못된 값을 배포하는 사고도 일어납니다.
특히 보안에 민감한 값들이 평문으로 노출되는 경우도 많습니다. 바로 이럴 때 필요한 것이 체계적인 values.yaml 설계입니다.
values.yaml은 단순한 설정 파일이 아니라 차트의 인터페이스입니다. 잘 설계된 values 파일은 사용자가 복잡한 템플릿을 전혀 건드리지 않고도 원하는 대로 배포를 커스터마이징할 수 있게 해줍니다.
개요
간단히 말해서, values.yaml은 차트의 모든 설정 가능한 값들의 기본값을 정의하는 파일입니다. 템플릿에서 하드코딩하는 대신 이 파일의 값을 참조하도록 만들어서 유연성을 확보합니다.
왜 values 설계가 중요할까요? 실무에서는 같은 차트를 수십 번 다른 설정으로 배포합니다.
예를 들어, 동일한 웹 애플리케이션을 고객사마다 다른 도메인, 다른 리소스 할당, 다른 feature flag로 배포해야 할 때, values 파일만 교체해서 배포할 수 있다면 운영 효율이 극대적으로 높아집니다. 기존에는 환경 변수나 ConfigMap을 직접 수정했다면, 이제는 values.yaml에서 계층적으로 조직화된 설정을 한눈에 보고 관리할 수 있습니다.
더 나아가 values-dev.yaml, values-prod.yaml처럼 환경별 오버라이드 파일을 만들어서 차이점만 명시할 수 있습니다. values.yaml의 핵심 특징은 계층적 구조, 타입 안정성을 위한 주석, 그리고 합성(composition)입니다.
객체를 중첩해서 논리적 그룹을 만들고, 주석으로 각 값의 의미와 허용 범위를 문서화하고, 한 섹션의 값을 다른 섹션에서 참조할 수 있습니다. 이런 특징들이 대규모 차트를 관리 가능하게 만듭니다.
코드 예제
# 애플리케이션 이미지 설정
image:
repository: myapp/web
pullPolicy: IfNotPresent
tag: "" # 비어있으면 Chart.appVersion 사용
# 레플리카와 업데이트 전략
replicaCount: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
# 리소스 요청/제한 (프로덕션에서는 필수)
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
# 서비스 노출 설정
service:
type: ClusterIP
port: 80
targetPort: 8080
# Ingress 설정 (조건부 활성화)
ingress:
enabled: false
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: myapp.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: myapp-tls
hosts:
- myapp.example.com
# 환경 변수
env:
- name: NODE_ENV
value: production
- name: LOG_LEVEL
value: info
# PostgreSQL 서브차트 설정
postgresql:
enabled: true
auth:
username: appuser
database: appdb
primary:
persistence:
enabled: true
size: 10Gi
설명
이것이 하는 일: values.yaml은 차트의 기본 동작을 정의하고, 사용자에게 커스터마이징 포인트를 제공합니다. 템플릿은 이 파일의 값들을 읽어서 최종 Kubernetes 매니페스트를 생성합니다.
첫 번째로, image 섹션은 컨테이너 이미지 설정을 중앙화합니다. repository와 tag를 분리해서 정의하면 CI/CD에서 태그만 동적으로 주입할 수 있습니다.
tag가 빈 문자열이면 Chart.yaml의 appVersion을 사용하는 패턴은 Helm 커뮤니티의 베스트 프랙티스입니다. pullPolicy는 개발 환경에서는 Always, 프로덕션에서는 IfNotPresent를 사용하는 것이 일반적입니다.
그 다음으로, resources 섹션이 실행되면서 Pod의 CPU와 메모리를 제어합니다. requests는 스케줄링 보장을, limits는 상한선을 설정합니다.
이 값들을 제대로 설정하지 않으면 클러스터 리소스 부족이나 OOM kill 문제가 발생합니다. 프로덕션에서는 반드시 설정해야 하지만, 개발 환경에서는 유연성을 위해 생략할 수도 있습니다.
ingress 섹션이 가장 흥미롭습니다. enabled 플래그로 Ingress 리소스 생성을 제어할 수 있어서, 같은 차트로 내부 서비스와 외부 노출 서비스를 모두 배포할 수 있습니다.
annotations는 Ingress 컨트롤러별 설정을 주입하는 확장 포인트입니다. tls 섹션은 HTTPS 설정을 담는데, cert-manager와 통합하면 인증서를 자동으로 발급받을 수 있습니다.
postgresql 섹션은 서브차트 설정의 예입니다. 의존성 차트에 값을 전달할 때는 차트 이름을 키로 사용합니다.
enabled 플래그로 외부 데이터베이스를 사용할지, 번들된 PostgreSQL을 사용할지 선택할 수 있습니다. 이런 패턴을 사용하면 하나의 차트로 다양한 아키텍처를 지원할 수 있습니다.
여러분이 values를 잘 설계하면 차트의 재사용성이 극대화됩니다. 사용자들은 helm install my-release ./my-chart -f custom-values.yaml처럼 자신만의 values 파일을 제공하거나, --set image.tag=v2.0.0처럼 개별 값을 오버라이드할 수 있습니다.
또한 GitOps 워크플로우에서 values 파일을 Git으로 버전 관리하면 모든 배포 변경이 추적 가능해집니다.
실전 팁
💡 모든 설정 가능한 값에 주석을 달아서 의미와 기본값을 설명하세요. values.yaml은 차트의 가장 중요한 문서이기도 합니다. 타입, 허용 범위, 예시를 주석에 포함하면 사용자 경험이 크게 개선됩니다.
💡 보안에 민감한 값(비밀번호, API 키)은 values.yaml에 실제 값을 넣지 마세요. 대신 빈 문자열이나 플레이스홀더를 넣고, 주석으로 Kubernetes Secret을 사용하도록 안내하거나, Helm Secrets 플러그인 사용을 권장하세요.
💡 enabled 플래그 패턴을 활용해서 선택적 기능을 제어하세요. ingress.enabled, metrics.enabled, autoscaling.enabled처럼 불리언 값으로 리소스 생성을 제어하면 하나의 차트로 단순한 배포부터 복잡한 배포까지 지원할 수 있습니다.
💡 환경별 values 파일(values-dev.yaml, values-prod.yaml)을 만들 때는 전체 구조를 복사하지 말고 차이점만 정의하세요. Helm은 여러 values 파일을 자동으로 병합해줍니다: helm install -f values.yaml -f values-prod.yaml
💡 중첩 깊이를 3단계 이내로 유지하세요. 너무 깊은 계층은 템플릿에서 접근하기 어렵고, 오버라이드할 때도 복잡합니다. 필요하다면 평탄한 구조로 리팩토링하는 것을 고려하세요.
4. 템플릿 기본 문법
시작하며
여러분이 Kubernetes YAML 파일에 values.yaml의 값을 주입하려고 할 때 어떻게 해야 할지 막막했던 경험이 있나요? 혹은 조건에 따라 특정 리소스를 포함하거나 제외하고 싶은데 방법을 몰라서 고생한 적이 있을 겁니다.
이런 문제는 Helm의 템플릿 시스템을 이해하지 못했을 때 발생합니다. 단순히 YAML 파일에 변수만 넣는 것이 아니라, Go 템플릿 엔진의 강력한 기능을 활용해서 동적이고 조건부 매니페스트를 생성할 수 있다는 것을 모르는 경우가 많습니다.
바로 이럴 때 필요한 것이 Helm 템플릿 기본 문법입니다. 템플릿 문법을 마스터하면 정적인 YAML 파일의 한계를 넘어서 진정으로 재사용 가능하고 유연한 차트를 만들 수 있습니다.
실무에서 만나는 대부분의 복잡한 요구사항도 템플릿 문법으로 해결 가능합니다.
개요
간단히 말해서, Helm 템플릿은 Go 템플릿 엔진을 사용해서 YAML 파일에 동적인 값을 주입하고 로직을 구현하는 시스템입니다. {{ }}로 감싼 표현식이 실행 시점에 평가되어 최종 매니페스트를 생성합니다.
왜 템플릿 문법을 배워야 할까요? 실무에서는 단순한 값 치환을 넘어서 복잡한 요구사항을 다룹니다.
예를 들어, 환경에 따라 다른 리소스를 배포하거나, 조건부로 사이드카 컨테이너를 추가하거나, 여러 항목을 반복해서 생성해야 하는 경우가 많습니다. 템플릿 문법을 모르면 이런 요구사항을 충족시킬 수 없습니다.
기존에는 스크립트로 YAML을 생성하거나 Kustomize 같은 다른 도구를 조합했다면, Helm 템플릿 하나로 모든 것을 해결할 수 있습니다. 변수 참조, 파이프라인, 함수 호출, 조건문, 반복문 등 프로그래밍 언어에 가까운 표현력을 제공합니다.
Helm 템플릿의 핵심 특징은 세 가지입니다. 첫째, 내장 객체(.Values, .Chart, .Release 등)로 컨텍스트에 접근합니다.
둘째, 60개 이상의 내장 함수(default, quote, toYaml 등)로 데이터를 변환합니다. 셋째, 파이프라인(|)으로 함수를 체이닝해서 복잡한 변환을 가독성 있게 표현합니다.
이런 특징들이 선언적 YAML과 명령적 프로그래밍의 장점을 결합합니다.
코드 예제
apiVersion: apps/v1
kind: Deployment
metadata:
# .Release는 릴리스 정보, .Chart는 Chart.yaml 내용
name: {{ .Release.Name }}-{{ .Chart.Name }}
labels:
# toYaml 함수로 중첩 객체를 YAML로 변환
{{- include "myapp.labels" . | nindent 4 }}
spec:
# .Values는 values.yaml 내용
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ .Release.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
# default 함수: 값이 없으면 기본값 사용
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
# quote 함수: 문자열을 따옴표로 감싸기
imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
ports:
- containerPort: {{ .Values.service.targetPort }}
resources:
# toYaml 함수로 복잡한 객체를 그대로 출력
{{- toYaml .Values.resources | nindent 10 }}
설명
이것이 하는 일: Helm 템플릿 엔진은 templates/ 디렉토리의 파일들을 읽어서 {{ }} 표현식을 평가하고, 최종적으로 유효한 Kubernetes YAML 매니페스트를 생성합니다. 마치 웹 템플릿 엔진이 HTML을 생성하는 것과 비슷합니다.
첫 번째로, 내장 객체들이 컨텍스트를 제공합니다. .Values는 values.yaml의 모든 내용을 담은 객체입니다.
.Release는 릴리스 이름, 네임스페이스, 설치 시각 등의 정보를 담고 있습니다. .Chart는 Chart.yaml의 내용을 담습니다.
.Capabilities는 Kubernetes 클러스터의 버전과 API 지원 여부를 담습니다. 점(.)으로 중첩 필드에 접근할 수 있습니다: {{ .Values.image.repository }} 그 다음으로, 파이프라인이 실행되면서 값을 변환합니다.
{{ .Values.name | upper | quote }}처럼 | 기호로 함수를 체이닝할 수 있습니다. 왼쪽의 값이 오른쪽 함수의 마지막 인자로 전달됩니다.
이는 유닉스 파이프와 비슷한 개념으로, 여러 변환을 순차적으로 적용할 때 가독성이 좋습니다. 함수들이 핵심입니다.
default 함수는 값이 비어있거나 nil일 때 기본값을 제공합니다. quote 함수는 문자열을 따옴표로 감싸서 YAML 파싱 오류를 방지합니다.
toYaml 함수는 Go 객체를 YAML 형식으로 변환해서 복잡한 구조를 템플릿에 쉽게 주입할 수 있게 합니다. nindent 함수는 들여쓰기를 추가해서 생성된 YAML이 올바른 구조를 갖도록 합니다.
{{- 와 -}}의 하이픈은 공백 제어를 담당합니다. {{- 는 앞의 공백과 개행을 제거하고, -}}는 뒤의 공백과 개행을 제거합니다.
이것이 중요한 이유는 템플릿 로직이 불필요한 빈 줄을 생성하지 않도록 하기 위함입니다. YAML은 들여쓰기에 민감하기 때문에 공백 제어가 매우 중요합니다.
여러분이 이 문법을 익히면 거의 모든 배포 시나리오를 템플릿으로 표현할 수 있습니다. 복잡한 스크립트나 외부 도구 없이도 Helm 차트만으로 환경별 차이, 기능 토글, 동적 리소스 생성을 모두 처리할 수 있습니다.
또한 helm template 명령으로 실제 배포 전에 생성될 YAML을 미리 볼 수 있어 디버깅이 쉽습니다.
실전 팁
💡 항상 quote 함수를 사용해서 문자열 값을 감싸세요: {{ .Values.env | quote }}. YAML 파서는 true, false, yes, no, on, off 같은 단어를 불리언으로 해석할 수 있어서 예상치 못한 타입 변환이 일어날 수 있습니다.
💡 default 함수로 안전한 기본값을 제공하세요: {{ .Values.replicas | default 1 }}. 사용자가 값을 제공하지 않아도 차트가 정상 동작하도록 보장합니다.
💡 helm template 명령으로 생성될 YAML을 미리 확인하세요: helm template my-release ./my-chart. 실제 클러스터에 배포하기 전에 템플릿 오류와 생성 결과를 검증할 수 있습니다.
💡 복잡한 표현식은 include와 헬퍼 함수로 분리하세요. 템플릿이 복잡해지면 가독성이 떨어지므로 _helpers.tpl에 재사용 가능한 템플릿을 정의하고 include로 호출하는 것이 좋습니다.
💡 required 함수로 필수 값을 강제하세요: {{ required "image.repository is required" .Values.image.repository }}. 사용자가 필수 값을 제공하지 않으면 명확한 에러 메시지와 함께 설치를 중단시킵니다.
5. 헬퍼 함수 활용
시작하며
여러분이 여러 템플릿 파일에서 동일한 레이블 세트를 반복해서 작성하고 있나요? Deployment, Service, Ingress 모두에 같은 레이블을 붙여야 하는데 복사-붙여넣기를 하다 보니 실수로 불일치가 생긴 경험이 있을 겁니다.
이런 문제는 코드 중복이 발생했을 때 나타납니다. 템플릿에서도 프로그래밍의 DRY(Don't Repeat Yourself) 원칙이 똑같이 적용됩니다.
중복된 로직이 여러 곳에 흩어져 있으면 수정할 때 모든 곳을 찾아서 바꿔야 하고, 하나라도 놓치면 버그가 됩니다. 바로 이럴 때 필요한 것이 헬퍼 함수입니다.
_helpers.tpl 파일에 재사용 가능한 템플릿 조각들을 정의하고, include나 template 함수로 필요한 곳에서 호출할 수 있습니다. 이는 함수나 메서드를 정의해서 재사용하는 것과 동일한 개념입니다.
개요
간단히 말해서, 헬퍼 함수는 _helpers.tpl 파일에 정의된 명명된 템플릿 조각입니다. define 블록으로 정의하고, include나 template 함수로 다른 템플릿에서 호출할 수 있습니다.
왜 헬퍼 함수가 필요할까요? 실무에서는 Kubernetes 모범 사례로 일관된 레이블, 어노테이션, 셀렉터를 사용해야 합니다.
예를 들어, app.kubernetes.io/name, app.kubernetes.io/instance 같은 권장 레이블을 모든 리소스에 동일하게 적용해야 하는데, 이를 각 템플릿마다 복사하면 유지보수 악몽이 됩니다. 기존에는 복사-붙여넣기로 중복을 감수했다면, 헬퍼 함수를 사용하면 한 곳에서 정의하고 여러 곳에서 재사용할 수 있습니다.
수정이 필요할 때도 _helpers.tpl 파일 하나만 바꾸면 모든 템플릿에 자동으로 반영됩니다. 헬퍼 함수의 핵심 특징은 세 가지입니다.
첫째, define으로 템플릿을 정의하고 이름을 붙입니다. 둘째, include로 호출하면 결과를 문자열로 받아서 파이프라인으로 후처리할 수 있습니다.
셋째, 스코프(.)를 전달해서 헬퍼 내부에서 .Values 등에 접근할 수 있게 합니다. 이런 특징들이 템플릿을 모듈화하고 테스트 가능하게 만듭니다.
코드 예제
{{/* _helpers.tpl 파일 내용 */}}
{{/*
공통 레이블을 생성하는 헬퍼
사용법: {{ include "myapp.labels" . }}
*/}}
{{- define "myapp.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version }}
{{- end }}
{{/*
셀렉터 레이블 (레이블의 부분집합)
*/}}
{{- define "myapp.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
완전한 이름 생성 (이름 충돌 방지)
*/}}
{{- define "myapp.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{/* deployment.yaml에서 사용하는 예시 */}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 6 }}
설명
이것이 하는 일: 헬퍼 함수는 템플릿 코드의 재사용을 가능하게 합니다. 마치 프로그래밍에서 함수를 정의하고 호출하는 것처럼, 복잡하거나 반복되는 템플릿 로직을 모듈화합니다.
첫 번째로, define 블록이 헬퍼를 정의합니다. {{- define "myapp.labels" -}}로 시작해서 {{- end }}로 끝나는 블록 안의 내용이 하나의 템플릿이 됩니다.
이름은 "차트이름.함수이름" 형식을 권장하는데, 이는 여러 차트가 서브차트로 포함될 때 이름 충돌을 방지하기 위함입니다. 블록 안에서는 일반 템플릿 문법을 모두 사용할 수 있습니다.
그 다음으로, include 함수가 헬퍼를 호출합니다. {{ include "myapp.labels" .
}}처럼 사용하는데, 두 번째 인자인 점(.)은 현재 스코프를 헬퍼에 전달합니다. 이렇게 하면 헬퍼 내부에서 .Values, .Chart, .Release 등에 접근할 수 있습니다.
include를 사용하면 결과를 파이프라인으로 전달할 수 있어서 {{ include "myapp.labels" . | nindent 4 }}처럼 들여쓰기를 조정할 수 있습니다.
myapp.labels와 myapp.selectorLabels를 분리하는 패턴이 중요합니다. Kubernetes에서는 레이블과 셀렉터가 다른 목적을 가집니다.
레이블은 리소스를 분류하고 검색하는 메타데이터이고, 셀렉터는 Pod를 선택하는 불변 식별자입니다. selector.matchLabels는 Deployment 생성 후 변경할 수 없기 때문에 안정적인 레이블만 포함해야 합니다.
버전 같은 변경 가능한 레이블은 제외해야 합니다. myapp.fullname 헬퍼는 고급 패턴을 보여줍니다.
fullnameOverride 값이 있으면 사용하고, 없으면 릴리스 이름과 차트 이름을 조합합니다. trunc 63은 Kubernetes 이름 제한(63자)을 준수하고, trimSuffix "-"는 잘린 결과가 하이픈으로 끝나지 않도록 합니다.
이런 세심한 처리가 실제 프로덕션 차트에서는 필수적입니다. 여러분이 헬퍼 함수를 활용하면 차트의 품질이 크게 향상됩니다.
모든 리소스에 일관된 레이블이 적용되어 kubectl get all -l app.kubernetes.io/instance=my-release처럼 릴리스의 모든 리소스를 쉽게 찾을 수 있습니다. 또한 레이블 체계를 변경해야 할 때 _helpers.tpl 하나만 수정하면 되므로 유지보수가 간편합니다.
실전 팁
💡 주석을 {{/* ... */}} 형식으로 작성하세요. 이는 템플릿 주석으로, 최종 출력에 포함되지 않습니다. 각 헬퍼의 목적, 파라미터, 사용법을 문서화하면 팀원들이 쉽게 사용할 수 있습니다.
💡 include와 template의 차이를 이해하세요. template은 결과를 직접 출력하지만 파이프라인을 사용할 수 없습니다. include는 결과를 문자열로 반환해서 | nindent 같은 후처리가 가능합니다. 거의 모든 경우 include를 사용하는 것이 좋습니다.
💡 Kubernetes 권장 레이블을 따르세요. app.kubernetes.io/name, app.kubernetes.io/instance, app.kubernetes.io/version, app.kubernetes.io/component, app.kubernetes.io/managed-by 등이 표준입니다. 이를 헬퍼로 구현하면 모범 사례를 자동으로 따르게 됩니다.
💡 복잡한 로직은 여러 작은 헬퍼로 나누세요. 하나의 헬퍼가 너무 크면 이해하기 어렵습니다. myapp.labels, myapp.selectorLabels, myapp.annotations처럼 목적별로 분리하세요.
💡 helm create 명령으로 생성된 기본 _helpers.tpl을 참고하세요. Helm 커뮤니티의 베스트 프랙티스가 반영되어 있어서 좋은 시작점이 됩니다.
6. 조건문과 반복문
시작하며
여러분이 개발 환경에서는 Ingress를 생성하지 않고, 프로덕션에서만 생성하고 싶을 때 어떻게 하시나요? 혹은 ConfigMap에 환경 변수 리스트를 동적으로 추가해야 하는데, 개수가 가변적이라 어떻게 처리할지 막막했던 경험이 있을 겁니다.
이런 문제는 정적인 YAML로는 해결할 수 없습니다. 조건에 따라 리소스의 유무를 결정하거나, 동적인 개수의 항목을 생성하려면 프로그래밍 언어의 제어 구조가 필요합니다.
많은 초보자들이 여러 개의 차트를 만들거나 수동으로 파일을 편집하는 비효율적인 방법을 사용합니다. 바로 이럴 때 필요한 것이 Helm의 조건문과 반복문입니다.
if/else로 조건부 렌더링을 하고, range로 리스트나 맵을 순회하면서 반복적인 구조를 생성할 수 있습니다. 이는 프로그래밍 언어의 if문과 for문에 해당하는 강력한 기능입니다.
개요
간단히 말해서, 조건문(if/else/with)과 반복문(range)은 템플릿에 로직을 추가해서 동적인 YAML 생성을 가능하게 합니다. 값의 존재 여부나 내용에 따라 출력을 제어할 수 있습니다.
왜 제어 구조가 필요할까요? 실무에서는 하나의 차트로 다양한 시나리오를 지원해야 합니다.
예를 들어, 메트릭 수집이 활성화되었을 때만 Prometheus ServiceMonitor를 생성하거나, values.yaml에 정의된 환경 변수 목록을 모두 Pod에 주입하거나, 여러 Ingress 호스트를 동적으로 생성해야 하는 경우가 많습니다. 기존에는 환경마다 별도의 템플릿 파일을 만들었다면, 제어 구조를 사용하면 하나의 템플릿에서 모든 경우를 처리할 수 있습니다.
if문으로 선택적 렌더링을 하고, range로 리스트의 각 항목을 순회하면서 반복 구조를 생성합니다. 조건문과 반복문의 핵심 특징은 세 가지입니다.
첫째, if/else/else if로 다중 조건 분기를 구현합니다. 둘째, range로 리스트와 맵을 순회하며 각 항목에 접근합니다.
셋째, with로 스코프를 변경해서 중첩된 객체에 간결하게 접근합니다. 이런 특징들이 템플릿을 진정한 프로그램으로 만들어줍니다.
코드 예제
{{/* 조건문: Ingress 활성화 여부에 따라 리소스 생성 */}}
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "myapp.fullname" . }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "myapp.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}
---
{{/* 반복문: 환경 변수 리스트 동적 생성 */}}
env:
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
{{- if .Values.extraEnvVars }}
{{- range $key, $value := .Values.extraEnvVars }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
{{- end }}
설명
이것이 하는 일: 조건문과 반복문은 템플릿에 프로그래밍 로직을 추가해서 입력 데이터에 따라 다른 YAML을 생성합니다. 마치 프로그램이 조건과 반복으로 동작을 제어하는 것과 같습니다.
첫 번째로, if 조건문이 블록의 렌더링 여부를 결정합니다. {{- if .Values.ingress.enabled }}는 ingress.enabled 값이 true일 때만 다음 블록을 출력합니다.
거짓으로 판단되는 값은 false, 0, 빈 문자열, nil, 빈 리스트, 빈 맵입니다. else if와 else로 다중 분기도 가능합니다.
{{- end }}로 블록을 닫아야 합니다. 이를 활용하면 feature flag 패턴을 구현할 수 있습니다.
그 다음으로, range가 실행되면서 리스트나 맵을 순회합니다. {{- range .Values.ingress.hosts }}는 hosts 리스트의 각 항목을 반복합니다.
반복 블록 내에서 점(.)은 현재 항목을 가리킵니다. $.을 사용하면 루트 스코프에 접근할 수 있어서 $.Values처럼 최상위 값을 참조할 수 있습니다.
이는 중첩 루프에서 외부 값을 참조할 때 필수적입니다. with 문이 스코프를 변경합니다.
{{- with .Values.ingress.annotations }}는 annotations 객체로 스코프를 좁힙니다. 블록 내에서 점(.)은 이제 annotations 객체 자체를 가리키므로 .prometheus.io/scrape처럼 긴 경로 대신 간결하게 접근할 수 있습니다.
with는 값이 존재할 때만 블록을 실행하므로 nil 체크 역할도 합니다. range의 고급 형태로 $key, $value := 문법이 있습니다.
{{- range $key, $value := .Values.extraEnvVars }}는 맵을 순회하면서 키와 값을 각각 변수에 할당합니다. 이렇게 하면 맵의 키를 환경 변수 이름으로, 값을 환경 변수 값으로 사용하는 패턴을 구현할 수 있습니다.
리스트에서도 인덱스와 값을 동시에 받을 수 있습니다: range $index, $item := .Values.items 여러분이 이런 제어 구조를 활용하면 차트의 유연성이 극대화됩니다. 사용자는 values.yaml에서 간단한 플래그나 리스트를 조정하는 것만으로 완전히 다른 배포 구성을 얻을 수 있습니다.
또한 복잡한 기업 환경의 요구사항(다중 환경, 다중 테넌트, feature toggle 등)도 하나의 차트로 지원할 수 있게 됩니다.
실전 팁
💡 if 조건에서 and, or, not 함수를 사용해서 복잡한 조건을 표현하세요: {{- if and .Values.ingress.enabled .Values.tls.enabled }}. 여러 조건을 조합할 수 있습니다.
💡 range 내에서 $ 변수로 루트 스코프에 접근하세요. 중첩 루프에서 외부 값을 참조할 때 필수입니다: {{ $.Values.service.port }} 또는 {{ include "myapp.fullname" $ }}
💡 empty 함수로 빈 값을 안전하게 체크하세요: {{- if not (empty .Values.annotations) }}. nil, 빈 문자열, 빈 리스트, 빈 맵을 모두 처리합니다.
💡 range가 빈 리스트를 순회할 때를 대비해서 기본 동작을 제공하세요. else를 range와 함께 사용하면 리스트가 비었을 때 실행됩니다: {{- range .Values.items }} ... {{- else }} # 기본 항목 {{- end }}
💡 복잡한 조건은 헬퍼 함수로 추출하세요. 템플릿에 로직이 너무 많으면 가독성이 떨어지므로 _helpers.tpl에 조건 판단 헬퍼를 만들고 include로 호출하는 것이 좋습니다.
7. 의존성 관리
시작하며
여러분이 웹 애플리케이션을 배포할 때 PostgreSQL, Redis, RabbitMQ 같은 인프라 컴포넌트들을 매번 따로 설치하고 연결 설정을 수동으로 맞추고 계신가요? 각 컴포넌트의 버전 호환성을 확인하고, 네트워크 설정을 조정하는 작업이 번거롭고 오류가 발생하기 쉽습니다.
이런 문제는 의존성을 수동으로 관리할 때 발생합니다. 애플리케이션이 여러 외부 서비스에 의존하는 경우, 각각을 별도로 설치하고 설정을 동기화하는 것은 매우 복잡하고 실수하기 쉽습니다.
특히 팀원들마다 다른 버전을 설치하거나 설정을 놓치는 경우가 많습니다. 바로 이럴 때 필요한 것이 Helm의 의존성 관리입니다.
Chart.yaml에 필요한 서브차트들을 선언하면 Helm이 자동으로 다운로드하고 함께 설치해줍니다. 마치 npm이나 pip처럼 패키지 의존성을 선언적으로 관리할 수 있습니다.
개요
간단히 말해서, Helm 의존성 관리는 차트가 다른 차트를 포함하고 함께 배포할 수 있게 하는 시스템입니다. Chart.yaml의 dependencies 섹션에 필요한 차트들을 나열하면 됩니다.
왜 의존성 관리가 중요할까요? 실무에서는 애플리케이션이 단독으로 동작하는 경우가 거의 없습니다.
예를 들어, 전형적인 웹 애플리케이션은 데이터베이스, 캐시, 메시지 큐, 메트릭 수집기 등 여러 컴포넌트에 의존합니다. 이들을 하나의 패키지로 묶으면 "원클릭 설치"가 가능해집니다.
기존에는 각 컴포넌트를 개별적으로 helm install 했다면, 의존성으로 선언하면 helm install my-app 한 번으로 모든 것이 함께 설치됩니다. 더 중요한 것은 서브차트의 설정을 부모 차트의 values.yaml에서 통합 관리할 수 있다는 점입니다.
Helm 의존성의 핵심 특징은 세 가지입니다. 첫째, 버전 범위 지정으로 자동 업데이트를 제어할 수 있습니다.
둘째, condition과 tags로 선택적 의존성을 구현합니다. 셋째, 부모 차트의 values를 서브차트로 전달해서 통합 설정이 가능합니다.
이런 특징들이 복잡한 애플리케이션 스택을 단일 차트로 패키징할 수 있게 해줍니다.
코드 예제
# Chart.yaml에서 의존성 선언
dependencies:
# 공개 저장소의 차트 사용
- name: postgresql
version: "~12.0.0" # 12.0.x 버전 허용
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled # values로 활성화 제어
- name: redis
version: "^17.0.0" # 17.x.x 버전 허용
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
# 로컬 차트 사용 (charts/ 디렉토리에 있는 경우)
- name: monitoring
version: "1.0.0"
repository: "file://../monitoring"
tags:
- observability
# values.yaml에서 서브차트 설정
postgresql:
enabled: true # condition과 매칭
auth:
username: myapp
password: secretpass
database: myappdb
primary:
persistence:
enabled: true
size: 10Gi
storageClass: fast-ssd
redis:
enabled: true
architecture: standalone
auth:
enabled: false
master:
persistence:
enabled: false
# 의존성 업데이트 및 설치 명령
# helm dependency update ./my-chart
# helm install my-release ./my-chart
설명
이것이 하는 일: Helm 의존성 시스템은 여러 차트를 하나의 배포 단위로 묶어줍니다. 부모 차트를 설치하면 선언된 모든 서브차트가 자동으로 다운로드되고 함께 배포됩니다.
첫 번째로, dependencies 선언이 필요한 차트들을 정의합니다. name은 서브차트의 이름, version은 호환되는 버전 범위, repository는 차트 저장소 URL입니다.
version에 ~를 사용하면 마이너 버전까지, ^를 사용하면 메이저 버전까지 자동 업데이트를 허용합니다. 정확한 버전을 지정할 수도 있습니다.
버전 범위를 어떻게 설정하느냐에 따라 안정성과 유연성의 균형을 조절할 수 있습니다. 그 다음으로, helm dependency update 명령이 실행되면서 서브차트들을 다운로드합니다.
이 명령은 Chart.yaml을 읽어서 각 의존성을 repository에서 가져와 charts/ 디렉토리에 .tgz 파일로 저장합니다. 동시에 Chart.lock 파일을 생성해서 정확히 어떤 버전이 다운로드되었는지 기록합니다.
이는 재현 가능한 빌드를 보장합니다. condition 필드가 강력한 유연성을 제공합니다.
condition: postgresql.enabled로 설정하면 values.yaml의 postgresql.enabled 값에 따라 PostgreSQL 서브차트 설치 여부가 결정됩니다. 이를 활용하면 개발 환경에서는 외부 데이터베이스를 사용하고(enabled: false), 프로덕션에서는 번들된 데이터베이스를 사용하는(enabled: true) 등의 시나리오를 지원할 수 있습니다.
values 전달 메커니즘이 핵심입니다. 부모 차트의 values.yaml에서 서브차트 이름을 키로 사용하면 해당 섹션이 서브차트의 values로 전달됩니다.
postgresql: 아래의 모든 설정이 PostgreSQL 차트로 전달되어 그 차트의 기본 values를 오버라이드합니다. 이렇게 하면 여러 차트의 설정을 하나의 파일에서 통합 관리할 수 있습니다.
tags 필드는 여러 의존성을 그룹화합니다. tags: [observability]로 표시된 차트들은 values.yaml에서 tags.observability: true/false로 일괄 제어할 수 있습니다.
예를 들어, 모니터링 관련 차트들을 observability 태그로 묶으면 한 번에 활성화/비활성화할 수 있습니다. 여러분이 의존성 관리를 활용하면 복잡한 애플리케이션 스택을 쉽게 배포할 수 있습니다.
개발자는 helm install 한 번으로 전체 환경을 구축할 수 있고, DevOps 팀은 버전 호환성을 Chart.yaml에서 중앙 관리할 수 있습니다. 또한 프로덕션에서는 관리형 서비스를 사용하고, 개발/테스트에서는 번들된 서비스를 사용하는 하이브리드 전략도 쉽게 구현할 수 있습니다.
실전 팁
💡 프로덕션에서는 정확한 버전을 지정하세요. version: "12.1.5"처럼 구체적으로 지정하면 예상치 못한 업데이트로 인한 호환성 문제를 방지할 수 있습니다. 개발 환경에서는 ~나 ^로 유연성을 허용할 수 있습니다.
💡 Chart.lock 파일을 버전 관리에 포함하세요. 이 파일은 정확히 어떤 버전의 서브차트가 사용되었는지 기록하므로, 팀원들이 동일한 버전을 사용하도록 보장합니다. 재현 가능한 빌드의 핵심입니다.
💡 로컬 차트 의존성은 상대 경로로 참조할 수 있습니다: repository: "file://../common-library". 모노레포에서 여러 차트가 공통 라이브러리를 공유할 때 유용합니다.
💡 import-values로 서브차트의 값을 부모 차트로 가져올 수 있습니다. 서브차트가 생성한 Secret 이름이나 Service 엔드포인트를 부모 차트에서 참조해야 할 때 사용합니다.
💡 helm dependency list 명령으로 현재 의존성 상태를 확인하세요. 어떤 버전이 다운로드되었는지, 업데이트가 필요한지 한눈에 볼 수 있습니다.
8. 차트 패키징과 배포
시작하며
여러분이 열심히 만든 Helm 차트를 팀원들이나 다른 환경에서 사용하려고 할 때 어떻게 공유하시나요? 파일을 압축해서 이메일로 보내거나, Git 저장소를 직접 클론하게 하는 방법은 버전 관리와 배포가 번거롭습니다.
이런 문제는 차트 배포 전략이 없을 때 발생합니다. 소프트웨어를 빌드하고 패키징해서 저장소에 게시하는 것처럼, Helm 차트도 체계적으로 패키징하고 저장소를 통해 배포해야 합니다.
그래야 버전 관리, 검색, 자동 업데이트가 가능합니다. 바로 이럴 때 필요한 것이 차트 패키징과 배포 프로세스입니다.
helm package로 차트를 .tgz 파일로 만들고, 차트 저장소(Chart Repository)에 게시하면 누구나 helm install로 쉽게 설치할 수 있습니다. 마치 npm publish나 docker push와 같은 개념입니다.
개요
간단히 말해서, 차트 패키징은 차트 디렉토리를 하나의 .tgz 아카이브 파일로 압축하는 것이고, 배포는 이를 차트 저장소에 게시해서 다른 사람들이 사용할 수 있게 하는 것입니다. 왜 패키징과 배포가 중요할까요?
실무에서는 여러 팀이 차트를 만들고 공유합니다. 예를 들어, 플랫폼 팀이 공통 인프라 차트를 만들면 여러 애플리케이션 팀이 이를 사용합니다.
중앙화된 차트 저장소가 없으면 누가 어떤 버전의 차트를 사용하는지 추적하기 어렵고, 업데이트를 배포하기도 힘듭니다. 기존에는 차트 파일을 직접 복사하거나 Git 저장소를 공유했다면, 차트 저장소를 사용하면 helm search로 검색하고, helm repo update로 최신 버전을 가져오고, helm upgrade로 자동 업데이트할 수 있습니다.
Docker Registry나 npm Registry와 동일한 경험을 제공합니다. 차트 패키징과 배포의 핵심 특징은 세 가지입니다.
첫째, helm package는 차트를 재현 가능한 아티팩트로 만듭니다. 둘째, 차트 저장소는 HTTP 서버만 있으면 되는 간단한 구조입니다.
셋째, GitHub Pages, S3, Artifact Hub, OCI Registry 등 다양한 저장소 옵션이 있습니다. 이런 특징들이 차트 배포를 유연하고 접근성 높게 만듭니다.
코드 예제
# 1. 차트 린트 (문법 검사 및 베스트 프랙티스 확인)
helm lint ./my-chart
# 2. 차트 패키징 (.tgz 파일 생성)
helm package ./my-chart
# 출력: my-chart-1.2.3.tgz
# 3. 패키지에 서명 (선택사항, 보안 강화)
helm package --sign --key 'my-key' --keyring ~/.gnupg/secring.gpg ./my-chart
# 4. 로컬에서 차트 저장소 생성
mkdir my-chart-repo
mv my-chart-1.2.3.tgz my-chart-repo/
helm repo index my-chart-repo/ --url https://charts.example.com
# index.yaml 파일 생성됨
# 5. GitHub Pages로 배포 (무료 차트 저장소)
# my-chart-repo/ 내용을 gh-pages 브랜치에 푸시
git checkout gh-pages
cp -r my-chart-repo/* .
git add .
git commit -m "Release my-chart 1.2.3"
git push origin gh-pages
# 6. 사용자가 차트 저장소 추가 및 설치
helm repo add myrepo https://charts.example.com
helm repo update
helm search repo myrepo/my-chart
helm install my-release myrepo/my-chart --version 1.2.3
# 7. OCI Registry 사용 (Helm 3.8+)
helm package ./my-chart
helm push my-chart-1.2.3.tgz oci://registry.example.com/charts
helm install my-release oci://registry.example.com/charts/my-chart --version 1.2.3
설명
이것이 하는 일: 차트 패키징과 배포는 차트를 배포 가능한 아티팩트로 만들고, 중앙 저장소를 통해 버전 관리되는 방식으로 배포합니다. 소프트웨어 패키지 관리 시스템과 동일한 워크플로우를 제공합니다.
첫 번째로, helm lint가 차트의 품질을 검증합니다. 문법 오류, 누락된 필수 필드, 베스트 프랙티스 위반 사항을 찾아서 보고합니다.
Chart.yaml의 필수 필드가 있는지, 템플릿이 유효한 YAML을 생성하는지, 들여쓰기가 올바른지 등을 체크합니다. 패키징 전에 항상 lint를 실행해서 문제를 조기에 발견하는 것이 중요합니다.
그 다음으로, helm package가 실행되면서 차트 디렉토리를 단일 .tgz 파일로 압축합니다. 파일 이름은 <차트이름>-<버전>.tgz 형식입니다.
이 파일은 차트의 모든 파일(Chart.yaml, values.yaml, templates/, charts/ 등)을 포함하지만, .helmignore에 나열된 파일은 제외됩니다. 패키지는 재현 가능한 빌드를 보장하며, 동일한 소스에서 항상 동일한 패키지가 생성됩니다.
helm repo index가 차트 저장소의 인덱스 파일을 생성합니다. index.yaml 파일은 저장소의 모든 차트와 버전을 나열하는 메타데이터 파일입니다.
클라이언트는 이 파일을 먼저 다운로드해서 사용 가능한 차트를 검색합니다. --url 플래그는 차트 파일들이 실제로 호스팅되는 기본 URL을 지정합니다.
새 버전을 추가할 때마다 helm repo index를 다시 실행해서 index.yaml을 업데이트해야 합니다. 차트 저장소의 구조는 매우 단순합니다.
HTTP로 접근 가능한 디렉토리에 index.yaml과 .tgz 파일들만 있으면 됩니다. GitHub Pages, AWS S3, Azure Blob Storage, Google Cloud Storage 등 정적 파일 호스팅 서비스를 모두 차트 저장소로 사용할 수 있습니다.
이는 별도의 서버 소프트웨어 없이도 차트 배포가 가능하다는 뜻입니다. OCI Registry 지원이 최신 트렌드입니다.
Helm 3.8부터 OCI(Open Container Initiative) 표준을 지원해서 Docker Registry, Harbor, Artifact Registry 등에 차트를 저장할 수 있습니다. helm push로 차트를 레지스트리에 푸시하고, oci:// 스킴으로 설치할 수 있습니다.
이미 컨테이너 이미지를 레지스트리로 관리하고 있다면 차트도 같은 곳에서 관리할 수 있어 운영이 단순해집니다. 여러분이 차트를 제대로 패키징하고 배포하면 조직 전체의 차트 관리가 체계화됩니다.
플랫폼 팀은 공통 차트를 중앙 저장소에 게시하고, 개발 팀은 helm repo add 한 번으로 접근할 수 있습니다. 버전 업데이트도 helm repo update && helm upgrade로 자동화할 수 있습니다.
CI/CD 파이프라인에 통합하면 코드 변경 시 자동으로 차트가 패키징되고 배포되는 완전 자동화된 워크플로우를 구축할 수 있습니다.
실전 팁
💡 CI/CD 파이프라인에 helm lint와 helm package를 통합하세요. 코드 리뷰 전에 자동으로 차트 품질을 검증하고, 머지 시 자동으로 패키징해서 저장소에 게시하면 인간의 실수를 방지할 수 있습니다.
💡 GitHub Releases와 GitHub Pages를 조합하면 무료 차트 저장소를 만들 수 있습니다. chart-releaser-action을 사용하면 Git 태그를 푸시할 때 자동으로 차트를 패키징하고 저장소를 업데이트합니다.
💡 차트에 서명(--sign)을 추가하면 변조 방지와 출처 검증이 가능합니다. 프로덕션 환경에서는 신뢰할 수 있는 소스의 차트만 설치하도록 정책을 강제할 수 있습니다.
💡 Artifact Hub(https://artifacthub.io/)에 차트를 게시하면 전 세계 개발자들이 검색하고 사용할 수 있습니다. 오픈소스 차트를 배포할 때 훌륭한 플랫폼입니다.
💡 멀티 환경 배포를 위해 차트 버전과 앱 버전을 분리 관리하세요. 동일한 앱 버전(appVersion)을 다른 차트 버전(version)으로 여러 번 패키징할 수 있습니다. 예를 들어, 앱 v2.0.0에 대한 차트 v1.0.0(초기 릴리스)과 v1.1.0(템플릿 개선)이 있을 수 있습니다.
댓글 (0)
함께 보면 좋은 카드 뉴스
Istio 보안 완벽 가이드
마이크로서비스 환경에서 필수적인 Istio 보안 기능을 실무 중심으로 설명합니다. mTLS부터 인증, 인가까지 단계별로 학습하여 안전한 서비스 메시를 구축할 수 있습니다.
Istio 트래픽 관리 완벽 가이드
Istio의 트래픽 관리 기능을 마스터하는 완벽 가이드입니다. VirtualService와 DestinationRule을 활용한 라우팅부터 트래픽 분할, 헤더 기반 라우팅까지 실무에 필요한 모든 내용을 다룹니다.
Istio 설치와 구성 완벽 가이드
Kubernetes 환경에서 Istio 서비스 메시를 설치하고 구성하는 방법을 초급 개발자도 쉽게 이해할 수 있도록 실무 스토리와 비유로 풀어낸 가이드입니다. istioctl 설치부터 사이드카 주입까지 단계별로 학습합니다.
서비스 메시 완벽 가이드
마이크로서비스 간 통신을 안전하고 효율적으로 관리하는 서비스 메시의 핵심 개념부터 실전 도입까지, 초급 개발자를 위한 완벽한 입문서입니다. Istio와 Linkerd 비교, 사이드카 패턴, 실무 적용 노하우를 담았습니다.
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.