Helm 차트: Kubernetes 패키지 관리

Helm 패키지 관리와 함께하는 Kubernetes 배포

Helm은 전통적인 운영 체제에서 익숙한 패키지 관리 개념을 도입하여 Kubernetes 애플리케이션 배포를 혁신적으로 바꾸었습니다.

Kubernetes 채택이 증가하면서 수십 개의 YAML 파일로 구성된 복잡한 애플리케이션을 관리하는 것이 어려워졌습니다. Helm Charts는 이러한 문제를 해결하기 위해 모든 리소스를 버전 관리되고 구성 가능한 패키지로 묶습니다.

software-developer in the nature 이 멋진 이미지는 AI 모델 Flux 1 dev에 의해 생성되었습니다.

Helm: Kubernetes 패키지 관리자 이해

Helm은 Kubernetes에 대해 apt가 Debian에, yum이 RedHat에, Homebrew가 macOS에 하는 역할을 합니다. Helm은 Kubernetes 애플리케이션을 Charts로 패키징합니다. 이는 관련된 Kubernetes 리소스를 설명하는 파일의 모음입니다. 하나의 Chart는 완전한 애플리케이션 스택을 배포할 수 있습니다: 웹 서버, 데이터베이스, 캐싱 레이어, 인그레스 규칙, 모니터링 구성 요소 등입니다. Kubernetes에 익숙하지 않은 경우, Kubernetes Cheatsheet는 시작에 필요한 필수 명령어와 개념을 제공합니다.

현대 DevOps에서 Helm의 중요성

복잡성 감소: 20개 이상의 YAML 파일을 관리하는 대신, 하나의 Chart로 구성 가능한 값만 관리합니다.

재현 가능성: 개발, 스테이징, 프로덕션 환경에서 환경에 따라 값이 오버라이드되는 동일한 구성으로 배포할 수 있습니다. 이는 일관성이 중요한 복잡한 마이크로서비스 아키텍처에서 특히 유용합니다.

버전 관리: Charts는 버전이 지정되어 있어 쉽게 롤백 및 업그레이드 추적을 할 수 있습니다.

커뮤니티 생태계: PostgreSQL, Redis, NGINX, Prometheus 등 인기 있는 애플리케이션을 위한 수천 개의 사전 구성된 Charts가 Artifact Hub(이전 Helm Hub)를 통해 제공됩니다.

템플릿 기능: Go 템플릿은 입력 값에 따라 동적으로 리소스를 생성하여 중복을 줄입니다.

Helm 아키텍처 및 핵심 개념

Helm 3 아키텍처

Helm 2에서 문제가 되었던 서버 측 구성 요소인 Tiller를 제거함으로써 Helm 3은 아키텍처를 단순화했습니다:

  • Helm 클라이언트: Kubernetes API와 직접 상호 작용하는 CLI 도구
  • Chart: 템플릿과 메타데이터를 포함하는 패키지 형식
  • 릴리스: Kubernetes 클러스터에서 실행 중인 Chart의 인스턴스
  • 리포지토리: Charts의 저장 위치 (HTTP 서버 또는 OCI 레지스트리)

Helm Chart의 주요 구성 요소

my-app-chart/
├── Chart.yaml          # Chart 메타데이터 및 버전
├── values.yaml         # 기본 구성 값
├── charts/             # 종속 Chart
├── templates/          # Kubernetes 리소스 템플릿
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── _helpers.tpl    # 템플릿 도우미
│   └── NOTES.txt       # 설치 후 메모
├── .helmignore         # 패키징 시 무시할 파일
├── README.md
└── LICENSE

첫 번째 Helm Chart 생성

새 Chart 초기화

helm create my-application
cd my-application

이 명령은 배포, 서비스, 인그레스에 대한 예제 템플릿을 포함한 시작 Chart를 생성합니다.

Chart.yaml: 메타데이터 정의

apiVersion: v2
name: my-application
description: A production-ready application chart
type: application
version: 1.0.0        # Chart 버전
appVersion: "2.4.1"   # 애플리케이션 버전

maintainers:
  - name: Your Team
    email: team@company.com

dependencies:
  - name: postgresql
    version: "12.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled

values.yaml: 구성 관리

values.yaml 파일은 사용자가 오버라이드할 수 있는 기본 구성 값을 정의합니다. 이 접근 방식은 구성과 템플릿을 분리하여, 템플릿 파일을 수정하지 않고도 다양한 환경(개발, 스테이징, 프로덕션)을 관리하고 배포를 맞춤화하는 것이 쉽습니다.

replicaCount: 3

image:
  repository: myapp/backend
  tag: "1.0.0"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: true
  className: "nginx"
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
  hosts:
    - host: myapp.example.com
      paths:
        - path: /
          pathType: Prefix

resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 250m
    memory: 256Mi

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80

템플릿: 동적 Kubernetes 매니페스트

템플릿은 Go 템플릿 구문을 사용하여 Kubernetes 리소스를 동적으로 생성합니다. 이러한 템플릿은 단순한 Deployments부터 상태가 필요한 애플리케이션에 사용되는 복잡한 StatefulSets까지 모든 Kubernetes 리소스 유형을 생성할 수 있습니다. 안정적인 식별자와 지속 가능한 볼륨이 필요한 애플리케이션의 경우, Deployments 대신 StatefulSets을 사용해야 합니다. 이에 대한 자세한 내용은 Kubernetes에서 StatefulSets 및 지속 가능한 저장소 가이드를 참조하십시오.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-application.fullname" . }}
  labels:
    {{- include "my-application.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "my-application.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "my-application.selectorLabels" . | nindent 8 }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - containerPort: 8080
          protocol: TCP
        resources:
          {{- toYaml .Values.resources | nindent 10 }}

템플릿 도우미 (_helpers.tpl)

반복을 피하기 위해 재사용 가능한 템플릿 함수를 생성합니다:

{{/*
차트 이름을 확장합니다.
*/}}
{{- define "my-application.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
기본적으로 완전한 애플리케이션 이름을 생성합니다.
*/}}
{{- define "my-application.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}

{{/*
공통 라벨
*/}}
{{- define "my-application.labels" -}}
helm.sh/chart: {{ include "my-application.chart" . }}
{{ include "my-application.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

Helm Chart 관리: 설치 및 운영

Chart 설치

# 리포지토리에서 설치
helm install my-release bitnami/postgresql

# 로컬 디렉토리에서 설치
helm install my-app ./my-application

# 사용자 정의 값으로 설치
helm install my-app ./my-application -f values-production.yaml

# 인라인 값 오버라이드로 설치
helm install my-app ./my-application \
  --set replicaCount=5 \
  --set image.tag=2.0.0

릴리스 업그레이드

# 새 값으로 업그레이드
helm upgrade my-app ./my-application -f values-production.yaml

# 실패 시 자동 롤백
helm upgrade my-app ./my-application --atomic --timeout 5m

# 리소스 강제 업데이트
helm upgrade my-app ./my-application --force

롤백 및 이력

# 릴리스 이력 보기
helm history my-app

# 이전 버전으로 롤백
helm rollback my-app

# 특정 버전으로 롤백
helm rollback my-app 3

테스트 및 디버깅

# 생성된 매니페스트를 보기 위한 드라이런
helm install my-app ./my-application --dry-run --debug

# 설치 없이 템플릿 렌더링
helm template my-app ./my-application

# 차트 문제점 검사
helm lint ./my-application

# 테스트 훅으로 릴리스 테스트
helm test my-app

고급 Helm 기능

차트 종속성

Helm Charts는 다른 Charts에 종속될 수 있어, 재사용 가능한 구성 요소로 복잡한 애플리케이션을 구성할 수 있습니다. 이는 데이터베이스, 메시지 큐, 기타 지원 서비스가 필요한 마이크로서비스를 배포할 때 특히 유용합니다. Chart.yaml에서 종속성을 정의합니다:

dependencies:
  - name: redis
    version: "17.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled
  - name: postgresql
    version: "12.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled

종속성 업데이트:

helm dependency update ./my-application

Helm 훅으로 라이프사이클 관리

릴리스 라이프사이클의 특정 시점에서 실행되는 훅:

apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "my-application.fullname" . }}-db-migration
  annotations:
    "helm.sh/hook": pre-upgrade,pre-install
    "helm.sh/hook-weight": "5"
    "helm.sh/hook-delete-policy": before-hook-creation
spec:
  template:
    spec:
      containers:
      - name: db-migrate
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        command: ["python", "manage.py", "migrate"]
      restartPolicy: Never

훅 유형은 다음과 같습니다:

  • pre-install: 리소스 설치 전
  • post-install: 모든 리소스 설치 후
  • pre-upgrade: 업그레이드 전
  • post-upgrade: 업그레이드 후
  • pre-delete: 삭제 전
  • post-delete: 삭제 후
  • pre-rollback: 롤백 전
  • post-rollback: 롤백 후

조건문 및 흐름 제어

{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "my-application.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 "my-application.fullname" $ }}
            port:
              number: {{ $.Values.service.port }}
      {{- end }}
  {{- end }}
{{- end }}

OCI 레지스트리 지원: 현대 Chart 배포

Helm 3.8 이후 OCI(Open Container Initiative) 레지스트리 지원이 안정화되어, Chart를 컨테이너 이미지와 함께 저장할 수 있습니다.

OCI 레지스트리에 게시

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

# 차트 패키징
helm package ./my-application

# OCI 레지스트리에 푸시
helm push my-application-1.0.0.tgz oci://registry.example.com/charts

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

OCI 레지스트리의 이점

  • 통합 저장소: 차트와 이미지를 한 곳에 저장
  • 표준 도구: 기존 레지스트리 인프라 사용
  • 더 나은 보안: 레지스트리 인증 및 스캔 활용
  • 더 간단한 관리: 별도의 Chart 레포지토리 서버 필요 없음

프로덕션용 Helm Chart 최고 실천 방법

1. 값 구조 및 문서화

모든 값을 주석으로 문서화합니다:

# -- 애플리케이션 복제 수
replicaCount: 3

# -- 이미지 구성
image:
  # -- 이미지 저장소
  repository: myapp/backend
  # -- 이미지 끌어오기 정책
  pullPolicy: IfNotPresent
  # -- 이미지 태그 (기본값은 chart appVersion)
  tag: ""

2. 리소스 관리

항상 리소스 요청 및 제한을 설정합니다:

resources:
  limits:
    cpu: 1000m
    memory: 1Gi
  requests:
    cpu: 500m
    memory: 512Mi

3. 보안 컨텍스트

컨테이너에 보안 컨텍스트를 정의합니다:

securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  fsGroup: 1000
  capabilities:
    drop:
    - ALL
  readOnlyRootFilesystem: true

4. 건강 점검

라이브니스 및 리드니스 프로브를 포함합니다:

livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5

5. ConfigMap 및 Secret 관리

생산 배포에서 적절한 Secret 관리가 중요합니다. 값 파일에 Secret을 저장하는 대신 외부 Secret 관리자(Sealed Secrets, External Secrets Operator, 또는 Vault)를 사용하세요. 이는 데이터베이스 비밀번호, API 키, 인증서와 같은 민감한 데이터를 안전하게 처리합니다:

envFrom:
- secretRef:
    name: {{ include "my-application.fullname" . }}-secrets
- configMapRef:
    name: {{ include "my-application.fullname" . }}-config

6. 스키마 검증

사용자 입력을 검증하기 위해 values.schema.json을 생성합니다:

{
  "$schema": "https://json-schema.org/draft-07/schema#",
  "properties": {
    "replicaCount": {
      "type": "integer",
      "minimum": 1
    },
    "image": {
      "type": "object",
      "properties": {
        "repository": {
          "type": "string"
        },
        "tag": {
          "type": "string"
        }
      },
      "required": ["repository"]
    }
  },
  "required": ["image"]
}

7. NOTES.txt 사용자 지침

설치 후 지침을 제공합니다:

1. 애플리케이션 URL을 얻으려면 다음을 실행하세요:
{{- if .Values.ingress.enabled }}
  https://{{ (index .Values.ingress.hosts 0).host }}
{{- else if contains "NodePort" .Values.service.type }}
  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "my-application.fullname" . }})
  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT
{{- end }}

2. 배포를 모니터링하세요:
  kubectl --namespace {{ .Release.Namespace }} get pods -l "app.kubernetes.io/name={{ include "my-application.name" . }}"

Helm Chart 테스트 및 CI/CD 통합

chart-testing (ct)으로 Chart 테스트

# chart-testing 설치
brew install chart-testing

# 차트 린트
ct lint --config ct.yaml

# 차트 설치 및 테스트
ct install --config ct.yaml

GitHub Actions 예제

name: Helm Chart CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Helm 설정
        uses: azure/setup-helm@v3
        with:
          version: 3.12.0

      - name: chart-testing 설정
        uses: helm/chart-testing-action@v2

      - name: 차트 린트
        run: ct lint --config ct.yaml

      - name: kind 클러스터 생성
        uses: helm/kind-action@v1

      - name: 차트 설치
        run: ct install --config ct.yaml

Helm과 함께하는 GitOps: ArgoCD 및 Flux

ArgoCD 및 Flux와 같은 GitOps 도구는 Helm Charts와 원활하게 통합되어 선언적이고 자동화된 배포를 가능하게 합니다. 이러한 도구는 Git 저장소의 변경을 감시하고 Helm 릴리스를 자동으로 동기화하여 지속적인 배포를 간단하게 만듭니다. 복잡한 마이크로서비스 아키텍처의 경우, Helm을 통해 배포된 서비스 간 일관성을 유지하기 위해 분산 트랜잭션 패턴인 Saga 패턴을 고려하세요.

ArgoCD 애플리케이션

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-application
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/my-app-chart
    targetRevision: main
    path: charts/my-application
    helm:
      valueFiles:
        - values-production.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Flux HelmRelease

apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: my-application
  namespace: flux-system
spec:
  interval: 5m
  chart:
    spec:
      chart: my-application
      version: '1.x.x'
      sourceRef:
        kind: HelmRepository
        name: my-charts
  values:
    replicaCount: 5
    image:
      tag: "2.0.0"

일반적인 Helm 문제 해결

문제: 실패한 업그레이드가 대기 상태

# 릴리스 상태 확인
helm list --all-namespaces

# 릴리스 세부 정보 확인
helm status my-app -n namespace

# 필요 시 강제 삭제 (주의 필요)
kubectl delete secret -n namespace -l owner=helm,name=my-app

문제: 템플릿 렌더링 오류

# 템플릿 렌더링 디버깅
helm template my-app ./my-application --debug

# Kubernetes에 대해 검증
helm template my-app ./my-application | kubectl apply --dry-run=client -f -

문제: 값이 적용되지 않음

# 병합된 값 확인
helm get values my-app

# 모든 계산된 값 표시
helm get values my-app --all

Helm 생태계 및 도구

Helm 생태계에는 Kubernetes 기술과 통합된 기능을 확장하는 다양한 도구가 포함되어 있습니다. 서비스 간 통신, 트래픽 관리, 보안 정책과 같은 고급 네트워킹 기능이 필요한 애플리케이션을 배포할 때, Istio 및 Linkerd와 함께하는 Service Mesh를 고려하세요. 이는 Helm Charts를 통해 배포 및 관리할 수 있습니다.

필수 도구

  • Helmfile: Helm Charts를 배포하기 위한 선언형 명세
  • Helm Diff: 업그레이드 전 변경 사항 미리 보기
  • Helm Secrets: SOPS를 사용한 Secret 관리
  • Nova: 오래된 Helm Charts 찾기
  • Pluto: 사용되지 않은 Kubernetes API 감지

예제 Helmfile

repositories:
  - name: bitnami
    url: https://charts.bitnami.com/bitnami

releases:
  - name: postgresql
    namespace: database
    chart: bitnami/postgresql
    version: 12.x.x
    values:
      - postgresql:
          auth:
            database: myapp
            username: appuser

  - name: my-app
    namespace: production
    chart: ./charts/my-application
    values:
      - values-production.yaml
    needs:
      - database/postgresql

Helm의 미래

Helm 생태계는 계속 진화하고 있습니다:

  • OCI-Native: OCI 레지스트리가 표준으로 전환
  • 개선된 보안: 더 나은 Secret 관리 및 서명 기능
  • 성능: 대규모 Charts의 빠른 렌더링 및 설치
  • WASM 지원: Chart 플러그인 및 확장에 대한 WebAssembly
  • 더 나은 검증: 강화된 스키마 검증 및 정책 강제

결론

Helm은 Kubernetes 패키지 관리의 de facto 표준이 되었으며, 현대 DevOps 실천을 위해 이를 마스터하는 것이 필수적입니다. Chart 구조, 템플릿, 값 관리 및 최고 실천 방법을 이해함으로써 유지보수가 가능하고 보안이 강화되며 확장성이 있는 Kubernetes 배포를 생성할 수 있습니다.

간단한 Charts부터 시작하여, 훅 및 종속성과 같은 고급 기능을 점차 도입하고, 프로덕션 등급 인프라를 위해 GitOps 도구와 통합하세요. Helm 커뮤니티는 수천 개의 사전 구성된 Charts를 제공하지만, 조직의 요구에 맞춘 맞춤형 Charts를 생성하는 것이 진정한 힘입니다. 상태 없는 애플리케이션 또는 지속 가능한 저장소가 필요한 상태 있는 워크로드를 배포하든, Helm은 Kubernetes 리소스 관리의 복잡성을 간소화합니다. 새 Kubernetes 클러스터를 설정하는 팀은 Kubespray로 Kubernetes 설치 또는 Kubernetes 배포판인 k3s 또는 MicroK8s를 개발 환경에 사용하는 것을 고려하세요. 홈랩 또는 소규모 클러스터에 적합한 배포판을 평가하는 경우, Kubernetes 배포판의 종합 비교를 참조하세요.

유용한 링크