Skip to content

Helm Charts

Giriş

Helm, Kubernetes uygulamalarını paketlemek, dağıtmak ve yönetmek için kullanılan bir paket yöneticisidir. "Kubernetes için apt/yum" olarak tanımlanabilir. Helm Charts, Kubernetes kaynaklarını şablon olarak tanımlamanıza ve parametrize etmenize olanak tanır.

Helm Temelleri

Helm Kurulumu

bash
# Helm 3 kurulumu (macOS)
brew install helm

# Helm 3 kurulumu (Linux)
curl https://get.helm.sh/helm-v3.12.0-linux-amd64.tar.gz | tar xz
sudo mv linux-amd64/helm /usr/local/bin/

# Helm version kontrolü
helm version

# Helm repository ekleme
helm repo add stable https://charts.helm.sh/stable
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

Temel Helm Komutları

bash
# Chart oluşturma
helm create my-web-app

# Chart install etme
helm install my-release ./my-web-app
helm install my-release bitnami/postgresql

# Release listesi
helm list
helm list --all-namespaces

# Release status
helm status my-release

# Release upgrade
helm upgrade my-release ./my-web-app

# Release rollback
helm rollback my-release 1

# Release uninstall
helm uninstall my-release

# Chart template render
helm template my-release ./my-web-app

# Chart validate
helm lint ./my-web-app

Chart Yapısı

Temel Chart Dizin Yapısı

my-web-app/
├── Chart.yaml          # Chart metadata
├── values.yaml         # Default configuration values
├── charts/             # Chart dependencies
├── templates/          # Kubernetes manifest templates
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── configmap.yaml
│   ├── secret.yaml
│   ├── hpa.yaml
│   ├── serviceaccount.yaml
│   ├── _helpers.tpl    # Template helpers
│   ├── NOTES.txt       # Installation notes
│   └── tests/
│       └── test-connection.yaml
└── .helmignore         # Files to ignore

Chart.yaml Örneği

yaml
# Chart.yaml
apiVersion: v2
name: my-web-app
description: A comprehensive web application Helm chart
type: application
version: 1.0.0
appVersion: "1.0.0"
home: https://github.com/mycompany/my-web-app
sources:
  - https://github.com/mycompany/my-web-app
maintainers:
  - name: Development Team
    email: dev-team@mycompany.com
    url: https://mycompany.com
keywords:
  - web
  - application
  - microservice
  - spring-boot
annotations:
  category: Application Framework
dependencies:
  - name: postgresql
    version: 12.1.9
    repository: https://charts.bitnami.com/bitnami
    condition: postgresql.enabled
  - name: redis
    version: 17.3.7
    repository: https://charts.bitnami.com/bitnami
    condition: redis.enabled
  - name: nginx-ingress-controller
    version: 9.3.3
    repository: https://charts.bitnami.com/bitnami
    condition: ingress.enabled

Values.yaml Yapılandırması

Kapsamlı Values.yaml

yaml
# values.yaml
# Global configuration
global:
  imageRegistry: "myregistry.azurecr.io"
  imagePullSecrets:
    - name: registry-secret
  storageClass: "premium-ssd"

# Application configuration
app:
  name: my-web-app
  version: "1.0.0"
  
image:
  repository: my-web-app
  tag: "1.0.0"
  pullPolicy: IfNotPresent

# Replica configuration
replicaCount: 3

# Service configuration
service:
  type: ClusterIP
  port: 80
  targetPort: 8080
  annotations: {}

# Ingress configuration
ingress:
  enabled: true
  className: "nginx"
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/rate-limit: "100"
  hosts:
    - host: api.mycompany.com
      paths:
        - path: /api
          pathType: Prefix
        - path: /health
          pathType: Exact
  tls:
    - secretName: api-tls-secret
      hosts:
        - api.mycompany.com

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

# Auto scaling
autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 20
  targetCPUUtilizationPercentage: 70
  targetMemoryUtilizationPercentage: 80

# Node selector and affinity
nodeSelector: {}
tolerations: []
affinity:
  podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 100
      podAffinityTerm:
        labelSelector:
          matchExpressions:
          - key: app.kubernetes.io/name
            operator: In
            values:
            - my-web-app
        topologyKey: kubernetes.io/hostname

# Security context
securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  runAsGroup: 3000
  fsGroup: 2000

containerSecurityContext:
  allowPrivilegeEscalation: false
  runAsNonRoot: true
  runAsUser: 1000
  capabilities:
    drop:
    - ALL
    add:
    - NET_BIND_SERVICE
  readOnlyRootFilesystem: true

# Health checks
healthChecks:
  liveness:
    enabled: true
    path: /actuator/health
    initialDelaySeconds: 30
    periodSeconds: 10
    timeoutSeconds: 5
    failureThreshold: 3
  readiness:
    enabled: true
    path: /actuator/health/readiness
    initialDelaySeconds: 5
    periodSeconds: 5
    timeoutSeconds: 3
    failureThreshold: 3

# Environment configuration
environment:
  - name: SPRING_PROFILES_ACTIVE
    value: "production"
  - name: JAVA_OPTS
    value: "-Xmx384m -Xms256m"

# ConfigMap configuration
configMap:
  enabled: true
  data:
    application.properties: |
      server.port=8080
      management.endpoints.web.exposure.include=health,info,metrics,prometheus
      management.endpoint.health.show-details=always
      logging.level.com.mycompany=INFO

# Secret configuration
secrets:
  enabled: true
  data:
    database-url: "amRiYzpwb3N0Z3Jlc3FsOi8vcG9zdGdyZXMtc2VydmljZTo1NDMyL215ZGI="
    jwt-secret: "bXlfc3VwZXJfc2VjcmV0X2p3dF9rZXk="

# Service Account
serviceAccount:
  create: true
  annotations: {}
  name: ""

# Pod Disruption Budget
podDisruptionBudget:
  enabled: true
  minAvailable: 2

# Network Policy
networkPolicy:
  enabled: true
  ingress:
    enabled: true
    from:
      - namespaceSelector:
          matchLabels:
            name: nginx-ingress
  egress:
    enabled: true
    to:
      - namespaceSelector:
          matchLabels:
            name: database

# Monitoring
monitoring:
  enabled: true
  serviceMonitor:
    enabled: true
    path: /actuator/prometheus
    interval: 30s

# Volume mounts
volumeMounts:
  - name: tmp-volume
    mountPath: /tmp
  - name: cache-volume
    mountPath: /app/cache

volumes:
  - name: tmp-volume
    emptyDir: {}
  - name: cache-volume
    emptyDir:
      sizeLimit: 1Gi

# Database (PostgreSQL)
postgresql:
  enabled: true
  auth:
    postgresPassword: "super_secret_password"
    username: "app_user"
    password: "app_password"
    database: "my_app_db"
  primary:
    persistence:
      enabled: true
      size: 10Gi
      storageClass: "premium-ssd"
  metrics:
    enabled: true

# Cache (Redis)
redis:
  enabled: true
  auth:
    enabled: true
    password: "redis_password"
  master:
    persistence:
      enabled: true
      size: 2Gi
  metrics:
    enabled: true

Template Örnekleri

Deployment Template

yaml
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-web-app.fullname" . }}
  namespace: {{ .Release.Namespace }}
  labels:
    {{- include "my-web-app.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  selector:
    matchLabels:
      {{- include "my-web-app.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      annotations:
        checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
        checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
      labels:
        {{- include "my-web-app.selectorLabels" . | nindent 8 }}
    spec:
      {{- with .Values.global.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      serviceAccountName: {{ include "my-web-app.serviceAccountName" . }}
      securityContext:
        {{- toYaml .Values.securityContext | nindent 8 }}
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.global.imageRegistry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: {{ .Values.service.targetPort }}
              protocol: TCP
          {{- if .Values.healthChecks.liveness.enabled }}
          livenessProbe:
            httpGet:
              path: {{ .Values.healthChecks.liveness.path }}
              port: http
            initialDelaySeconds: {{ .Values.healthChecks.liveness.initialDelaySeconds }}
            periodSeconds: {{ .Values.healthChecks.liveness.periodSeconds }}
            timeoutSeconds: {{ .Values.healthChecks.liveness.timeoutSeconds }}
            failureThreshold: {{ .Values.healthChecks.liveness.failureThreshold }}
          {{- end }}
          {{- if .Values.healthChecks.readiness.enabled }}
          readinessProbe:
            httpGet:
              path: {{ .Values.healthChecks.readiness.path }}
              port: http
            initialDelaySeconds: {{ .Values.healthChecks.readiness.initialDelaySeconds }}
            periodSeconds: {{ .Values.healthChecks.readiness.periodSeconds }}
            timeoutSeconds: {{ .Values.healthChecks.readiness.timeoutSeconds }}
            failureThreshold: {{ .Values.healthChecks.readiness.failureThreshold }}
          {{- end }}
          env:
            {{- range .Values.environment }}
            - name: {{ .name }}
              value: {{ .value | quote }}
            {{- end }}
            {{- if .Values.secrets.enabled }}
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: {{ include "my-web-app.fullname" . }}-secret
                  key: database-url
            - name: JWT_SECRET
              valueFrom:
                secretKeyRef:
                  name: {{ include "my-web-app.fullname" . }}-secret
                  key: jwt-secret
            {{- end }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
          securityContext:
            {{- toYaml .Values.containerSecurityContext | nindent 12 }}
          volumeMounts:
            {{- range .Values.volumeMounts }}
            - name: {{ .name }}
              mountPath: {{ .mountPath }}
            {{- end }}
            {{- if .Values.configMap.enabled }}
            - name: config-volume
              mountPath: /app/config
            {{- end }}
      volumes:
        {{- range .Values.volumes }}
        - name: {{ .name }}
          {{- if .emptyDir }}
          emptyDir:
            {{- if .emptyDir.sizeLimit }}
            sizeLimit: {{ .emptyDir.sizeLimit }}
            {{- end }}
          {{- end }}
        {{- end }}
        {{- if .Values.configMap.enabled }}
        - name: config-volume
          configMap:
            name: {{ include "my-web-app.fullname" . }}-config
        {{- end }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
      {{- end }}

Service Template

yaml
# templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ include "my-web-app.fullname" . }}
  namespace: {{ .Release.Namespace }}
  labels:
    {{- include "my-web-app.labels" . | nindent 4 }}
  {{- with .Values.service.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.targetPort }}
      protocol: TCP
      name: http
  selector:
    {{- include "my-web-app.selectorLabels" . | nindent 4 }}

Ingress Template

yaml
# templates/ingress.yaml
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "my-web-app.fullname" . }}
  namespace: {{ .Release.Namespace }}
  labels:
    {{- include "my-web-app.labels" . | nindent 4 }}
  {{- with .Values.ingress.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
spec:
  {{- if .Values.ingress.className }}
  ingressClassName: {{ .Values.ingress.className }}
  {{- end }}
  {{- if .Values.ingress.tls }}
  tls:
    {{- range .Values.ingress.tls }}
    - hosts:
        {{- range .hosts }}
        - {{ . | quote }}
        {{- end }}
      secretName: {{ .secretName }}
    {{- end }}
  {{- end }}
  rules:
    {{- range .Values.ingress.hosts }}
    - host: {{ .host | quote }}
      http:
        paths:
          {{- range .paths }}
          - path: {{ .path }}
            pathType: {{ .pathType }}
            backend:
              service:
                name: {{ include "my-web-app.fullname" $ }}
                port:
                  number: {{ $.Values.service.port }}
          {{- end }}
    {{- end }}
{{- end }}

ConfigMap Template

yaml
# templates/configmap.yaml
{{- if .Values.configMap.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "my-web-app.fullname" . }}-config
  namespace: {{ .Release.Namespace }}
  labels:
    {{- include "my-web-app.labels" . | nindent 4 }}
data:
  {{- range $key, $value := .Values.configMap.data }}
  {{ $key }}: |
{{ $value | indent 4 }}
  {{- end }}
{{- end }}

Secret Template

yaml
# templates/secret.yaml
{{- if .Values.secrets.enabled }}
apiVersion: v1
kind: Secret
metadata:
  name: {{ include "my-web-app.fullname" . }}-secret
  namespace: {{ .Release.Namespace }}
  labels:
    {{- include "my-web-app.labels" . | nindent 4 }}
type: Opaque
data:
  {{- range $key, $value := .Values.secrets.data }}
  {{ $key }}: {{ $value }}
  {{- end }}
{{- end }}

HPA Template

yaml
# templates/hpa.yaml
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: {{ include "my-web-app.fullname" . }}
  namespace: {{ .Release.Namespace }}
  labels:
    {{- include "my-web-app.labels" . | nindent 4 }}
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {{ include "my-web-app.fullname" . }}
  minReplicas: {{ .Values.autoscaling.minReplicas }}
  maxReplicas: {{ .Values.autoscaling.maxReplicas }}
  metrics:
    {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
    {{- end }}
    {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
    {{- end }}
{{- end }}

ServiceMonitor Template

yaml
# templates/servicemonitor.yaml
{{- if and .Values.monitoring.enabled .Values.monitoring.serviceMonitor.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: {{ include "my-web-app.fullname" . }}
  namespace: {{ .Release.Namespace }}
  labels:
    {{- include "my-web-app.labels" . | nindent 4 }}
spec:
  selector:
    matchLabels:
      {{- include "my-web-app.selectorLabels" . | nindent 6 }}
  endpoints:
  - port: http
    path: {{ .Values.monitoring.serviceMonitor.path }}
    interval: {{ .Values.monitoring.serviceMonitor.interval }}
{{- end }}

Helper Functions

_helpers.tpl

yaml
{{/*
# templates/_helpers.tpl
Expand the name of the chart.
*/}}
{{- define "my-web-app.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
*/}}
{{- define "my-web-app.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "my-web-app.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "my-web-app.labels" -}}
helm.sh/chart: {{ include "my-web-app.chart" . }}
{{ include "my-web-app.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/part-of: {{ .Values.app.name }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "my-web-app.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-web-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "my-web-app.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "my-web-app.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

{{/*
Generate backend URL
*/}}
{{- define "my-web-app.backendUrl" -}}
{{- if .Values.ingress.enabled }}
{{- $host := index .Values.ingress.hosts 0 }}
{{- if .Values.ingress.tls }}
https://{{ $host.host }}
{{- else }}
http://{{ $host.host }}
{{- end }}
{{- else }}
http://{{ include "my-web-app.fullname" . }}:{{ .Values.service.port }}
{{- end }}
{{- end }}

{{/*
Database connection string
*/}}
{{- define "my-web-app.databaseUrl" -}}
{{- if .Values.postgresql.enabled }}
{{- printf "jdbc:postgresql://%s-postgresql:5432/%s" .Release.Name .Values.postgresql.auth.database }}
{{- else }}
{{- .Values.externalDatabase.url }}
{{- end }}
{{- end }}

{{/*
Redis connection string
*/}}
{{- define "my-web-app.redisUrl" -}}
{{- if .Values.redis.enabled }}
{{- printf "redis://%s-redis-master:6379" .Release.Name }}
{{- else }}
{{- .Values.externalRedis.url }}
{{- end }}
{{- end }}

Multi-Environment Configuration

Development Values

yaml
# values-dev.yaml
replicaCount: 1

image:
  tag: "dev-latest"
  pullPolicy: Always

resources:
  limits:
    cpu: 200m
    memory: 256Mi
  requests:
    cpu: 100m
    memory: 128Mi

autoscaling:
  enabled: false

ingress:
  hosts:
    - host: api-dev.mycompany.com
      paths:
        - path: /
          pathType: Prefix

postgresql:
  primary:
    persistence:
      size: 1Gi

redis:
  master:
    persistence:
      size: 512Mi

environment:
  - name: SPRING_PROFILES_ACTIVE
    value: "development"
  - name: JAVA_OPTS
    value: "-Xmx192m -Xms128m"

Staging Values

yaml
# values-staging.yaml
replicaCount: 2

image:
  tag: "staging-latest"

resources:
  limits:
    cpu: 300m
    memory: 384Mi
  requests:
    cpu: 150m
    memory: 192Mi

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 5

ingress:
  hosts:
    - host: api-staging.mycompany.com
      paths:
        - path: /
          pathType: Prefix

postgresql:
  primary:
    persistence:
      size: 5Gi

environment:
  - name: SPRING_PROFILES_ACTIVE
    value: "staging"

Production Values

yaml
# values-prod.yaml
replicaCount: 5

image:
  tag: "1.0.0"
  pullPolicy: IfNotPresent

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

autoscaling:
  enabled: true
  minReplicas: 5
  maxReplicas: 50

ingress:
  annotations:
    nginx.ingress.kubernetes.io/rate-limit: "1000"
  hosts:
    - host: api.mycompany.com
      paths:
        - path: /
          pathType: Prefix

postgresql:
  primary:
    persistence:
      size: 100Gi
      storageClass: "premium-ssd"

redis:
  master:
    persistence:
      size: 10Gi

podDisruptionBudget:
  enabled: true
  minAvailable: 3

environment:
  - name: SPRING_PROFILES_ACTIVE
    value: "production"
  - name: JAVA_OPTS
    value: "-Xmx768m -Xms512m"

Chart Dependencies

requirements.yaml (Helm 2) / Chart.yaml dependencies (Helm 3)

yaml
# Chart.yaml dependencies section için
dependencies:
  - name: postgresql
    version: "~12.1.0"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled
    tags:
      - database
  - name: redis
    version: "~17.3.0"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled
    tags:
      - cache
  - name: prometheus
    version: "~15.0.0"
    repository: "https://prometheus-community.github.io/helm-charts"
    condition: monitoring.prometheus.enabled
    tags:
      - monitoring

Dependencies Update

bash
# Dependency güncelleme
helm dependency update

# Dependency build
helm dependency build

# Dependency list
helm dependency list

Testing

Test Templates

yaml
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "{{ include "my-web-app.fullname" . }}-test"
  labels:
    {{- include "my-web-app.labels" . | nindent 4 }}
  annotations:
    "helm.sh/hook": test
spec:
  restartPolicy: Never
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args: ['{{ include "my-web-app.fullname" . }}:{{ .Values.service.port }}/actuator/health']

Test Commands

bash
# Test çalıştırma
helm test my-release

# Test sonuçlarını görme
kubectl logs my-web-app-test

# Test pod'unu temizleme
helm test my-release --cleanup

Advanced Helm Techniques

Hooks

yaml
# templates/job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: "{{ include "my-web-app.fullname" . }}-migration"
  labels:
    {{- include "my-web-app.labels" . | nindent 4 }}
  annotations:
    "helm.sh/hook": pre-upgrade,pre-install
    "helm.sh/hook-weight": "-5"
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  template:
    metadata:
      name: "{{ include "my-web-app.fullname" . }}-migration"
      labels:
        {{- include "my-web-app.selectorLabels" . | nindent 8 }}
    spec:
      restartPolicy: Never
      containers:
      - name: migration
        image: "{{ .Values.global.imageRegistry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        command:
        - /bin/sh
        - -c
        - |
          echo "Running database migration..."
          java -jar app.jar --spring.profiles.active=migration

Notes Template

yaml
# templates/NOTES.txt
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
  {{- range .paths }}
  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
  {{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "my-web-app.fullname" . }})
  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "my-web-app.fullname" . }}'
  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "my-web-app.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
  echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "{{ include "my-web-app.selectorLabels" . }}" -o jsonpath="{.items[0].metadata.name}")
  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

2. Check the status of the deployment:
   kubectl get pods -l "{{ include "my-web-app.selectorLabels" . }}" -n {{ .Release.Namespace }}

3. View application logs:
   kubectl logs -f deployment/{{ include "my-web-app.fullname" . }} -n {{ .Release.Namespace }}

{{- if .Values.postgresql.enabled }}
4. Get PostgreSQL connection details:
   echo "PostgreSQL URL: {{ include "my-web-app.databaseUrl" . }}"
   echo "PostgreSQL Password: $(kubectl get secret --namespace {{ .Release.Namespace }} {{ .Release.Name }}-postgresql -o jsonpath="{.data.postgres-password}" | base64 --decode)"
{{- end }}

{{- if .Values.redis.enabled }}
5. Get Redis connection details:
   echo "Redis URL: {{ include "my-web-app.redisUrl" . }}"
   echo "Redis Password: $(kubectl get secret --namespace {{ .Release.Namespace }} {{ .Release.Name }}-redis -o jsonpath="{.data.redis-password}" | base64 --decode)"
{{- end }}

Packaging ve Repository

Chart Packaging

bash
# Chart package oluşturma
helm package ./my-web-app

# Chart package'ı belirli dizine kaydetme
helm package ./my-web-app -d ./packages

# Package'ı sign etme
helm package ./my-web-app --sign --key mykey --keyring ~/.gnupg/secring.gpg

Chart Repository

bash
# Local repository oluşturma
helm repo index ./packages --url https://my-charts.mycompany.com

# Repository'ye chart ekleme
helm repo add mycompany https://my-charts.mycompany.com
helm repo update

# Chart'ı repository'den install etme
helm install my-release mycompany/my-web-app

Best Practices

Values Organization

yaml
# values.yaml structure best practices
global:
  # Global values here

# Application specific values
app:
  name: my-web-app
  component: backend

# Image configuration
image:
  repository: my-web-app
  tag: ""  # Will use appVersion from Chart.yaml
  pullPolicy: IfNotPresent

# Don't use camelCase for keys that map to Kubernetes resources
service:
  type: ClusterIP
  port: 80

# Use clear, descriptive names
database:
  enabled: true
  host: postgres-service
  port: 5432

Template Best Practices

yaml
# Use consistent indentation
{{- with .Values.nodeSelector }}
nodeSelector:
  {{- toYaml . | nindent 2 }}
{{- end }}

# Use meaningful comments
{{/*
This template generates the deployment for the web application.
It includes configurable replicas, resources, and health checks.
*/}}

# Validate required values
{{- if not .Values.image.repository }}
{{- fail "image.repository is required" }}
{{- end }}

# Use proper conditionals
{{- if and .Values.ingress.enabled .Values.ingress.hosts }}
# Ingress configuration here
{{- end }}

Bu kapsamlı Helm rehberi, Kubernetes uygulamalarınızı etkili bir şekilde paketleyip yönetmeniz için gereken tüm bilgileri içermektedir. Helm Charts kullanarak uygulamalarınızı farklı ortamlarda tutarlı bir şekilde dağıtabilir ve yönetebilirsiniz.

Eren Demir tarafından oluşturulmuştur.