İçeriğe geç
9 dk okuma DevOps

Helm Chart Geliştirme: En İyi Uygulamalar

Helm, Kubernetes'in standart paket yöneticisidir ve uygulama deployment'larını tekrarlanabilir ve yönetilebilir kılar. Ancak chart kalitesi, operasyonel maliyetleri doğrudan etkiler. Aşırı soyutlanmış şablonlar, yetersiz test ve gözden kaçan güvenlik açıkları, production'da ciddi sorunlara neden olabilir. Bu makalede, production'a hazır Helm chart'ları geliştirmek için en iyi uygulamalar, yapısal tasarım prensipleri, test stratejileri ve güvenlik konuları detaylı olarak ele alınacaktır.

Chart Yapısı ve Organizasyonu

Standart Dizin Yapısı

Production'a hazır bir Helm chart, belirli bir dizin yapısına uymalıdır. Bu yapı, herhangi bir ekip üyesinin chart'ı kolayca anlamasını ve üzerinde çalışmasını sağlar. İyi organize edilmiş bir chart şu yapıyı takip eder:

``

my-chart/

├── Chart.yaml # Chart metadata

├── values.yaml # Default değerler

├── values.schema.json # Değer doğrulama şeması

├── requirements.yaml # Dependencies (Helm 2 için)

├── charts/ # Subchart'lar

├── crds/ # Custom Resource Definitions

├── templates/ # Kubernetes manifest şablonları

│ ├── deployment.yaml

│ ├── service.yaml

│ ├── ingress.yaml

│ ├── _helpers.tpl # Paylaşılan template fonksiyonları

│ └── tests/ # Test pod'ları

├── ci/ # CI/CD için values dosyaları

└── README.md # Chart dokümantasyonu

`

Her Chart.yaml, metadata'yı açıkça declare etmelidir. Önemli kurallar: Her zaman Helm 3+ için apiVersion: v2 kullanın, chart'ı uygulamadan bağımsız olarak versiyonlayın ve uyumsuz cluster'larda yüklemeyi önlemek için kubeVersion belirtin.

Values Schema Validation

Values schema'sı, yanlış yapılandırmaların cluster'a ulaşmadan yakalanmasını sağlar. Helm, herhangi bir şablonu render etmeden önce bu şemayı doğrular:

`json

{

"$schema": "https://json-schema.org/draft/2020-12/schema",

"type": "object",

"properties": {

"image": {

"type": "object",

"properties": {

"repository": { "type": "string" },

"tag": { "type": "string" },

"pullPolicy": { "type": "string", "enum": ["IfNotPresent", "Always", "Never"] }

},

"required": ["repository"]

},

"replicaCount": {

"type": "integer",

"minimum": 1,

"maximum": 10

}

}

}

`

eksik bir required field hemen açık bir hata mesajıyla başarısız olur. Bu, şablon paniğinden çok daha iyidir.

Template Tasarım Prensipleri

DRY ve YAGNI Dengesi

İki kritik prensip, Helm template kalitesini belirler. DRY (Don't Repeat Yourself) prensibi, manifest tekrarını önler. Tekrarlayan kalıpları _helpers.tpl'e çıkarın ve template fonksiyonları olarak yeniden kullanın:

`yaml

{{- define "myapp.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 -}}

{{- define "myapp.labels" -}}

app.kubernetes.io/name: {{ include "myapp.fullname" . }}

app.kubernetes.io/instance: {{ .Release.Name }}

app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}

app.kubernetes.io/component: "backend"

app.kubernetes.io/part-of: {{ .Chart.Name }}

app.kubernetes.io/managed-by: {{ .Release.Service }}

helm.sh/chart: {{ include "myapp.chart" . }}

{{- end }}

`

63 karakter kısaltması opsiyonel değildir. Kubernetes, 63 karakterden uzun isimleri reddeder. Release adınız staging-my-long-application-name olduğunda, bu sınır hızla gelir. Uzun release isimleriyle deployment'ların CI'da başarısız olduğunu gördüm.

YAGNI (You Aren't Gonna Need It) prensibi, aşırı soyutlamayı önler. Her ek fonksiyon, koşullu ifade veya parametre cognitive overhead ekler. Mevcut gereksinimleri karşılayan basit şablonlar yazın. Gelecekteki olası senaryolar için karmaşıklık eklemeyin.

Doğru Deployment Template'i

Production için zorunlu pattern'leri içeren bir Deployment template'i:

`yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: {{ include "myapp.fullname" . }}

labels:

{{- include "myapp.labels" . | nindent 4 }}

spec:

{{- if not .Values.autoscaling.enabled }}

replicas: {{ .Values.replicaCount }}

{{- end }}

selector:

matchLabels:

{{- include "myapp.selectorLabels" . | nindent 6 }}

template:

metadata:

annotations:

checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}

labels:

{{- include "myapp.labels" . | nindent 8 }}

spec:

{{- with .Values.imagePullSecrets }}

imagePullSecrets:

{{- toYaml . | nindent 8 }}

{{- end }}

serviceAccountName: {{ include "myapp.serviceAccountName" . }}

securityContext:

runAsNonRoot: true

fsGroup: 65534

containers:

- name: {{ .Chart.Name }}

image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"

imagePullPolicy: {{ .Values.image.pullPolicy }}

securityContext:

allowPrivilegeEscalation: false

readOnlyRootFilesystem: true

capabilities:

drop:

- ALL

ports:

- name: http

containerPort: {{ .Values.service.targetPort }}

protocol: TCP

{{- with .Values.readinessProbe }}

readinessProbe:

{{- toYaml . | nindent 12 }}

{{- end }}

{{- with .Values.livenessProbe }}

livenessProbe:

{{- toYaml . | nindent 12 }}

{{- end }}

resources:

{{- toYaml .Values.resources | nindent 12 }}

`

Bu checksum/config annotation'ı, ConfigMap değiştiğinde rolling restart tetikler. Bunu olmadan, bir config değeri güncellediğinizde Helm başarılı der, ancak pod'lar eski config ile çalışmaya devam eder çünkü Deployment spec'i değişmemiştir. Bu saatlerce confusion'a neden olabilir.

Test Stratejileri

Çok Katmanlı Kalite Güvencesi

Helm chart testleri, CI/CD pipeline güvenilirliğini doğrudan etkiler. Çok katmanlı bir test yaklaşımı için Chart Testing (ct) aracını ve helm-unittest'i birleştirin:

Katman 1: Syntax ve Lint kontrolü

`bash

helm lint my-chart/

helm template my-chart/ --debug

`

Katman 2: Unit Test (helm-unittest)

`bash

helm unittest my-chart/

`

Katman 3: Template Rendering Validation

`bash

ct lint --config ct.yaml

`

Katman 4: Integration Tests

`bash

helm test my-chart/

`

CI'da bu pipeline, çoğu sorunu cluster'a herhangi bir şey dokunmadan yakalar.

Test Pod'ları

Helm'in dahili test framework'ünü kullanarak test pod'ları ekleyin:

`yaml

apiVersion: v1

kind: Pod

metadata:

name: "{{ include "myapp.fullname" . }}-test-ready"

annotations:

"helm.sh/hook": test

spec:

containers:

- name: wget

image: busybox

command: ['wget', '-q', '--spider', 'http://{{ include "myapp.fullname" . }}:{{ .Values.service.port }}']

restartPolicy: Never

`

Değer Yönetimi Pattern'leri

Environment-Specific Values

Tek bir values.yaml'da tüm environment'ları yönetmeye çalışmak, şişmiş ve yönetilemez dosyalara yol açar. Her environment için ayrı values dosyaları kullanın:

`yaml

replicaCount: 1

image:

repository: myapp

tag: 'v1.0.0'

resources:

limits:

cpu: 500m

memory: 512Mi

requests:

cpu: 100m

memory: 128Mi

replicaCount: 3

autoscaling:

enabled: true

minReplicas: 3

maxReplicas: 10

replicaCount: 2

resources:

limits:

cpu: 1000m

memory: 1Gi

`

Helm bunları sırayla birleştirir — sonraki dosyalar öncekileri override eder.

Nested Yapı Önerisi

`

image:

repository: nginx

tag: '1.25'

pullPolicy: IfNotPresent

autoscaling:

enabled: false

minReplicas: 1

maxReplicas: 10

`

Bu yapı, değerlerin gruplanmasını sağlar ve değer dosyasını okunabilir tutar.

Subchart ve Library Chart'lar

Subchart Yönetimi

Gerçek uygulamalar, database'ler, message queue'lar ve monitoring agent'ları gibi başka chart'lara bağımlıdır. Bunları Chart.yaml'da declare edin:

`yaml

dependencies:

- name: redis

version: "17.x.x"

repository: "https://charts.bitnami.com/bitnami"

condition: redis.enabled

tags:

- database

`

Bu, chart'ları charts/ klasörüne indirir ve bir Chart.lock dosyası oluşturur. Bu lock dosyasını commit edin — tekrarlanabilir build'ler için versiyonları sabitler.

Library Chart'lar

Birden fazla servis arasında paylaşılan şablonlar için library chart oluşturun:

`yaml

apiVersion: v2

name: common-library

type: library

version: 1.0.0

`

Library chart'ların kendi template'leri yoktur — sadece diğer chart'ların include edebileceği _helpers.tpl sağlar. Bu, 50+ mikroservis genelinde standardizasyon için mükemmeldir.

CI/CD Entegrasyonu

GitHub Actions Örneği

Kötü chart'ların production'a ulaşmasını önleyin:

`yaml

name: Helm Chart CI

on: [push, pull_request]

jobs:

test:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- name: Setup Helm

uses: azure/setup-helm@v3

- name: Run chart-testing

uses: helm/chart-testing-action@v2.4.0

with:

command: lint-and-validate

config: ct.yaml

- name: Helm unittest

run: |

helm unittest my-chart/

- name: Render templates

run: |

helm template my-chart/ --debug >/dev/null

`

Güvenlik En İyi Uygulamaları

Immutable Tags

`yaml

image:

tag: 'v2.1.0' # Asla "latest" kullanmayın

`

Pinned versiyonlar, tekrarlanabilir deployment'lar için vazgeçilmezdir.

External Secrets Management

Secrets'ı values.yaml'da plaintext olarak tutmayın. Bunun yerine External Secrets Operator, Sealed Secrets veya Vault kullanın:

`yaml

envFrom:

- secretRef:

name: {{ include "myapp.fullname" . }}-credentials

`

Security ContextDefaults

`yaml

podSecurityContext:

runAsNonRoot: true

runAsUser: 1000

seccompProfile:

type: RuntimeDefault

containerSecurityContext:

allowPrivilegeEscalation: false

readOnlyRootFilesystem: true

capabilities:

drop:

- ALL

`

Resource Limits

Environment başına resource değerlerini ayarlayın. Development için minimal tutun, production trafik için uygun değerler belirleyin:

`yaml

resources:

limits:

cpu: 1000m

memory: 1Gi

requests:

cpu: 500m

memory: 512Mi

`

Helmfile ve Multi-Environment

Düzine cluster genelinde onlarca release yönetirken, raw helm komutları yönetilemez hale gelir. Helmfile, tüm Helm state'inizi açıklayan bir declarative katman sağlar:

`yaml

repositories:

- name: bitnami

url: https://charts.bitnami.com/bitnami

releases:

- name: myapp

namespace: production

chart: ./charts/myapp

values:

- values-production.yaml

secrets:

- secrets-production.yaml.enc

hooks:

- events: ["pre apply"]

showlogs: true

command: "./scripts/pre-deploy.sh"

`

Helmfile, release order'ı takip eder, dependencies'leri halleder ve apply etmeden önce helm diff çalıştırır. Helm için eksik olan orkestrasyon katmanıdır.

ArgoCD Entegrasyonu

ArgoCD, Helm chart'ları native olarak destekler. Bir Git repository'deki chart'a işaret edin:

`yaml

apiVersion: argoproj.io/v1alpha1

kind: Application

metadata:

name: myapp

namespace: argocd

spec:

source:

repoURL: https://github.com/myorg/helm-charts

targetRevision: main

path: charts/myapp

helm:

valueFiles:

- values.yaml

- values-production.yaml

destination:

server: https://kubernetes.default.svc

namespace: production

syncPolicy:

automated:

prune: true

selfHeal: true

`

ArgoCD, sync sırasında Helm template'ini render eder, canlı state'e karşı karşılaştırır ve sadece farkı uygular.

Kaçınılması Gereken Anti-Pattern'ler

Birçok ekipte chart bakımından sonra, bunlar karşı çıktığım anti-pattern'lerdir:

Birincisi, image tag'ı latest olarak default etmek. Bunun yerine .Chart.AppVersion'ı default olarak kullanın.

İkincisi, secrets'ı values.yaml'da tutmak. Secrets, Vault, AWS Secrets Manager gibi external secret manager'larda olmalı ve envFrom veya external-secrets-operator üzerinden referans verilmeli. Credentials'ları asla bir chart'a check etmeyin.

Üçüncüsü, devasa monolithic şablonlar. Bir template dosyası 150 satırı aşarsa, bölün. Tekrarlayan bloklar için _helpers.tpl'deki named template'leri kullanın.

Dördüncüsü, resource request veya limit olmaması. Resource tanımı olmayan bir chart, onu kaldıramayacak node'larda schedule edilebilir veya daha kötüsü, sınırsız kaynak tüketerek diğer workload'ları starve edebilir.

Beşincisi, PodDisruptionBudget atlamak. Node drain ve cluster upgrade'ler sırasında availability'ı önemsiyorsanız, PDB zorunludur. Her multi-replica workload için minAvailable: 1 default edin.

Sonuç

Bir Helm chart, uygulamanız ve cluster arasındaki arayüzdür. Operational knowledge'inizi kodlar: uygulama nasıl deploy edilmeli, hangi kaynaklara ihtiyaç duyar, nasıl scale olur ve upgrade'ler sırasında ne olur. Chart'larınızı uygulama kodu kadar ciddiye alın. PR'larda gözden geçirin, CI'da test edin, düzgün versiyonlayın. Laptop'ta çalışan chart ile 3 AM'de production node failure'ından sağ çıkan chart çok farklı şeylerdir. 3 AM senaryosu için build edin, laptop senaryosu kendiliğinden hallolur.