Helm Charts: Gestione dei pacchetti Kubernetes

Distribuzioni Kubernetes con la gestione dei pacchetti Helm

Helm ha rivoluzionato Kubernetes il deployment di applicazioni introducendo concetti di gestione dei pacchetti familiari provenienti da sistemi operativi tradizionali.

Con l’aumento dell’adozione di Kubernetes, la gestione di applicazioni complesse con decine di file YAML diventa impegnativa. I Helm Charts risolvono questo problema imballando tutte le risorse in pacchetti versionati e configurabili.

software-developer in the nature Questa bella immagine è generata da AI model Flux 1 dev.

Comprendere Helm: Il Gestore dei Pacchetti Kubernetes

Helm è per Kubernetes ciò che apt è per Debian, yum per RedHat, o Homebrew per macOS. Imballa le applicazioni Kubernetes in Charts – raccolte di file che descrivono risorse Kubernetes correlate. Un singolo Chart potrebbe distribuire un intero stack applicativo: server web, database, strati di caching, regole di ingresso e componenti di monitoraggio. Per coloro che sono nuovi a Kubernetes, una Kubernetes Cheatsheet fornisce comandi essenziali e concetti per iniziare.

Perché Helm è Importante nel Moderno DevOps

Riduzione della Complessità: Invece di gestire 20+ file YAML, si gestisce un singolo Chart con valori personalizzabili.

Riproducibilità: Distribuisci configurazioni identiche in ambienti di sviluppo, staging e produzione con sovrascritture di valori specifiche per l’ambiente. Questo è particolarmente utile quando si distribuiscono architetture microservizi complesse dove la coerenza è importante.

Controllo delle Versioni: I Charts sono versionati, abilitando il rollback facile e il tracciamento degli aggiornamenti.

Ecosistema della Comunità: Migliaia di Charts pre-costruiti disponibili tramite Artifact Hub (precedentemente Helm Hub) per applicazioni popolari come PostgreSQL, Redis, NGINX, Prometheus e altro.

Potere di Templating: I template Go abilitano la generazione dinamica di risorse basata sui valori di input, riducendo la duplicazione.

Architettura Helm e Concetti Principali

Architettura Helm 3

Helm 3 ha semplificato l’architettura rimuovendo Tiller, il componente problematico del lato server da Helm 2:

  • Helm Client: Strumento CLI che interagisce direttamente con l’API Kubernetes
  • Chart: Formato del pacchetto che contiene template e metadati
  • Release: Un’istanza di un Chart in esecuzione in un cluster Kubernetes
  • Repository: Posizione di archiviazione per i Charts (server HTTP o registro OCI)

Componenti Principali di un Helm Chart

my-app-chart/
├── Chart.yaml          # Metadati e versione del Chart
├── values.yaml         # Valori di configurazione predefiniti
├── charts/             # Charts dipendenti
├── templates/          # Template delle risorse Kubernetes
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── _helpers.tpl    # Aiuti per i template
│   └── NOTES.txt       # Note post-installazione
├── .helmignore         # File da ignorare durante l'imballaggio
├── README.md
└── LICENSE

Creare il Primo Helm Chart

Inizializzare un Nuovo Chart

helm create my-application
cd my-application

Questo genera un Chart iniziale con template di esempio per un deployment, un servizio e un ingresso.

Chart.yaml: Definire i Metadati

apiVersion: v2
name: my-application
description: Un Chart applicativo pronto per la produzione
type: application
version: 1.0.0        # Versione del Chart
appVersion: "2.4.1"   # Versione dell'applicazione

maintainers:
  - name: Il tuo Team
    email: team@company.com

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

values.yaml: Gestione della Configurazione

Il file values.yaml definisce le configurazioni predefinite che gli utenti possono sovrascrivere. Questo approccio separa la configurazione dai template, rendendo facile la gestione di diversi ambienti (sviluppo, staging, produzione) e la personalizzazione dei deployment senza modificare i file template.

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

Template: Manifesti Kubernetes Dinamici

I template utilizzano la sintassi di templating Go per generare dinamicamente le risorse Kubernetes. Questi template possono generare qualsiasi tipo di risorsa Kubernetes, da semplici Deployments a complessi StatefulSets per applicazioni che richiedono storage persistente. Per applicazioni che necessitano di identità stabili e volumi persistente, si desidera utilizzare StatefulSets invece di Deployments, come dettagliato nella nostra guida su StatefulSets e Storage Persistente in Kubernetes.

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 }}

Aiuti per Template (_helpers.tpl)

Crea funzioni riutilizzabili per evitare la ripetizione:

{{/*
Espandi il nome del Chart.
*/}}
{{- define "my-application.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Crea un nome completo di app predefinito.
*/}}
{{- 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 }}

{{/*
Etichette comuni
*/}}
{{- 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 }}

Gestione dei Charts Helm: Installazione e Operazioni

Installare un Chart

# Installare da un repository
helm install my-release bitnami/postgresql

# Installare da una directory locale
helm install my-app ./my-application

# Installare con valori personalizzati
helm install my-app ./my-application -f values-production.yaml

# Installare con sovrascritture di valori inline
helm install my-app ./my-application \
  --set replicaCount=5 \
  --set image.tag=2.0.0

Aggiornare le Releases

# Aggiornare con nuovi valori
helm upgrade my-app ./my-application -f values-production.yaml

# Aggiornare con rollback automatico in caso di fallimento
helm upgrade my-app ./my-application --atomic --timeout 5m

# Forzare l'aggiornamento delle risorse
helm upgrade my-app ./my-application --force

Rollback e Storia

# Visualizzare la storia delle releases
helm history my-app

# Rollback alla versione precedente
helm rollback my-app

# Rollback a una revisione specifica
helm rollback my-app 3

Test e Debugging

# Dry-run per visualizzare i manifesti generati
helm install my-app ./my-application --dry-run --debug

# Rendering dei template senza installazione
helm template my-app ./my-application

# Verifica del Chart per problemi
helm lint ./my-application

# Test della release con hook di test
helm test my-app

Funzionalità Avanzate di Helm

Dipendenze del Chart

I Charts Helm possono dipendere da altri Charts, permettendoti di comporre applicazioni complesse da componenti riutilizzabili. Questo è particolarmente utile quando si distribuiscono microservizi che necessitano di database, code di messaggi o altri servizi di supporto. Definisci le dipendenze in 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

Aggiorna le dipendenze:

helm dependency update ./my-application

Hook Helm per la Gestione del Ciclo di Vita

I hook vengono eseguiti in punti specifici del ciclo di vita della release:

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

I tipi di hook includono:

  • pre-install: Prima che le risorse siano installate
  • post-install: Dopo che tutte le risorse sono state installate
  • pre-upgrade: Prima dell’aggiornamento
  • post-upgrade: Dopo l’aggiornamento
  • pre-delete: Prima dell’eliminazione
  • post-delete: Dopo l’eliminazione
  • pre-rollback: Prima del rollback
  • post-rollback: Dopo il rollback

Logica Condizionale e Controllo del Flusso

{{- 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 }}

Supporto per Registri OCI: Distribuzione Moderna dei Charts

Da Helm 3.8, il supporto per i registri OCI (Open Container Initiative) è stabile, permettendo ai Charts di essere archiviati insieme alle immagini container.

Pubblicare su Registro OCI

# Accedi al registro
helm registry login registry.example.com

# Imballa il Chart
helm package ./my-application

# Pubblica sul registro OCI
helm push my-application-1.0.0.tgz oci://registry.example.com/charts

# Installa da registro OCI
helm install my-app oci://registry.example.com/charts/my-application --version 1.0.0

Vantaggi dei Registri OCI

  • Archiviazione Unificata: Charts e immagini nello stesso posto
  • Strumenti Standard: Utilizza l’infrastruttura esistente del registro
  • Migliore Sicurezza: Sfrutta l’autenticazione e la scansione del registro
  • Gestione Più Semplice: Non è necessario un server separato per i Chart

Migliori Pratiche per Charts Helm in Produzione

1. Struttura e Documentazione dei Valori

Documenta tutti i valori con commenti:

# -- Numero di repliche per l'applicazione
replicaCount: 3

# -- Configurazione dell'immagine
image:
  # -- Repository dell'immagine
  repository: myapp/backend
  # -- Politica di pull dell'immagine
  pullPolicy: IfNotPresent
  # -- Tag dell'immagine (predefinito al valore appVersion del Chart)
  tag: ""

2. Gestione delle Risorse

Imposta sempre le richieste e i limiti delle risorse:

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

3. Contesti di Sicurezza

Definisci contesti di sicurezza per i contenitori:

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

4. Controlli di Salute

Includi probe di vitalità e prontezza:

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

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

5. Gestione di ConfigMap e Secret

La gestione corretta dei secret è cruciale per le distribuzioni in produzione. Utilizza gestori esterni di secret (Sealed Secrets, External Secrets Operator o Vault) invece di archiviare i secret nei file values. Questo garantisce che dati sensibili come password del database, chiavi API e certificati siano gestiti in modo sicuro:

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

6. Validazione dello Schema

Crea values.schema.json per validare gli input degli utenti:

{
  "$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 per Guida all’Utente

Fornisci istruzioni post-installazione:

1. Ottenere l'URL dell'applicazione eseguendo:
{{- 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. Monitorare il deployment:
  kubectl --namespace {{ .Release.Namespace }} get pods -l "app.kubernetes.io/name={{ include "my-application.name" . }}"

Test dei Charts Helm e Integrazione con CI/CD

Test dei Charts con chart-testing (ct)

# Installa chart-testing
brew install chart-testing

# Lint dei Charts
ct lint --config ct.yaml

# Installa e testa i Charts
ct install --config ct.yaml

Esempio di 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: Set up Helm
        uses: azure/setup-helm@v3
        with:
          version: 3.12.0

      - name: Set up chart-testing
        uses: helm/chart-testing-action@v2

      - name: Lint charts
        run: ct lint --config ct.yaml

      - name: Create kind cluster
        uses: helm/kind-action@v1

      - name: Install charts
        run: ct install --config ct.yaml

GitOps con Helm: ArgoCD e Flux

Strumenti GitOps come ArgoCD e Flux si integrano in modo fluido con i Charts Helm, abilitando deployment dichiarativi e automatizzati. Questi strumenti monitorano il tuo repository Git per modifiche e sincronizzano automaticamente le releases Helm, rendendo il deployment continuo semplice. Per architetture microservizi complesse, considera come i pattern di transazioni distribuite come il pattern Saga possono aiutare a gestire la coerenza tra servizi distribuiti tramite Helm.

Applicazione 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

HelmRelease Flux

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"

Risoluzione dei Problemi Comuni con Helm

Problema: Aggiornamento Fallito Bloccato in Pendente

# Controlla lo stato della release
helm list --all-namespaces

# Ottieni i dettagli della release
helm status my-app -n namespace

# Forza l'eliminazione se necessario (usa con attenzione)
kubectl delete secret -n namespace -l owner=helm,name=my-app

Problema: Errori di Rendering dei Template

# Debug del rendering dei template
helm template my-app ./my-application --debug

# Valida contro Kubernetes
helm template my-app ./my-application | kubectl apply --dry-run=client -f -

Problema: Valori Non Applicati

# Controlla i valori fusi
helm get values my-app

# Mostra tutti i valori calcolati
helm get values my-app --all

Ecosistema e Strumenti Helm

L’ecosistema Helm include numerosi strumenti che estendono le sue capacità e si integrano con altre tecnologie Kubernetes. Quando si distribuiscono applicazioni che richiedono funzionalità avanzate di rete come la comunicazione servizio-servizio, la gestione del traffico e le politiche di sicurezza, considera l’integrazione con un Service Mesh con Istio e Linkerd, che possono essere distribuiti e gestiti tramite Charts Helm.

Strumenti Essenziali

  • Helmfile: Specifica dichiarativa per distribuire Charts Helm
  • Helm Diff: Anteprima dei cambiamenti prima dell’aggiornamento
  • Helm Secrets: Gestione dei secret con SOPS
  • Nova: Trova Charts Helm obsoleti
  • Pluto: Rileva API Kubernetes deprecate

Esempio di 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

Il Futuro di Helm

L’ecosistema Helm continua ad evolversi:

  • OCI-Nativo: Transizione completa ai registri OCI come standard
  • Migliore Sicurezza: Migliori funzionalità di gestione dei secret e firma
  • Prestazioni: Rendering e installazione più veloci per Charts grandi
  • Supporto WASM: WebAssembly per plugin e estensioni dei Charts
  • Migliore Validazione: Validazione dello schema e applicazione delle politiche migliorata

Conclusione

Helm è diventato lo standard de facto per la gestione dei pacchetti Kubernetes, e padroneggiarlo è essenziale per le pratiche moderne di DevOps. Comprendendo la struttura del Chart, il templating, la gestione dei valori e le migliori pratiche, puoi creare deployment Kubernetes mantenibili, sicuri e scalabili.

Inizia con Charts semplici, incorpora gradualmente funzionalità avanzate come hook e dipendenze, e integra gli strumenti GitOps per infrastrutture a livello di produzione. La comunità Helm fornisce migliaia di Charts pre-costruiti, ma il vero potere deriva dalla creazione di Charts personalizzati adatti alle esigenze dell’organizzazione. Che tu stia distribuendo applicazioni stateless o carichi di lavoro stateful che richiedono storage persistente, Helm semplifica la complessità della gestione delle risorse Kubernetes. Per i team che stanno configurando nuovi cluster Kubernetes, considera l’installazione di Kubernetes con Kubespray o esplora distribuzioni Kubernetes come k3s o MicroK8s per ambienti di sviluppo. Se stai valutando quale distribuzione si adatta alle tue esigenze di homelab o cluster piccoli, vedi la nostra confronto completo delle distribuzioni Kubernetes per un’analisi dettagliata.