1. října 2024

Helm vs Kustomize vs YAML

Nasazování aplikací do Kubernetes clusteru má několik způsobů, na tři velké se podíváme v tomhle článku.

YAML

YAML is a human-friendly data serialization language for all programming languages.

YAML je lidmi čitelný formát pro serializaci dat pro všechny programovací jazyky.

V Kubernetes je YAML snad úplně všude, od konfigurace clusteru až po nasazení aplikací. Ale to není jediné místo, kde je YAML populární, rozšířil se i jinde. Například v definicích CI/CD pipeline

Zde je příklad deploymentu v YAML:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.14.2
          ports:
            - containerPort: 80

YAML jako takový je poměrně čistou a čitelnou variantou, jak do Kubernetes dostat nějaké aplikace, ale protože jde jen o textový soubor, postrádá tedy jakoukoliv flexibilitu. Pokud tedy chceme nasazovat pomocí YAMLu, potřebujeme buď YAML generovat, přepisovat na základě hiearachie nebo YAML soubory šablonovat. A tím se dostáváme právě ke Kustomize a Helmu.

Co se vám občas může hodit vědět, je že JSON je podmnožinou YAMLu. Tedy jakýkoliv JSON text, vložený do YAML souboru, je validní YAML soubor.

V YAML si ale dávejte pozor na odsazení. YAML je až přecitlivělý na mezery a odsazení. Při špatné struktuře tedy dokáže velice potrápit. Pokud nemáte rádi Python kvůli odsazení, tak YAML pravděpodobně také nebude vaším oblíbencem.

Při práci s YAMLem, se také může hodit nástroj yq, který umožňuje pracovat s YAML objekty v příkazové řádce.

Například získání hodnoty z YAML souboru:

yq eval '.spec.replicas' deployment.yaml

Pokud jako deployment.yaml použijeme předchozí příklad, dostaneme hodnotu 3.

yq je alternativou k jq, který pracuje s JSONem, ten se vám také může někdy hodit 😉.

K nasazení obyčejných YAML manifestů pak použijeme kubectl:

kubectl apply -f deployment.yaml

Kustomize

Kustomize je nástroj, který umožňuje deklarativně upravovat YAML soubory. Stále tedy máme statické YAML soubory, ale už dokážeme vytvářet různé varianty aplikací pomocí jednoho zdroje.

Kustomize je součástí kubectl od verze 1.14, ale je možné ho používat i samostatně.

Kustomize pracuje s adresářovou strukturou, kde v kořenovém adresáři máme kustomization.yaml soubor, který obsahuje informace o tom, jaké soubory a jak upravit/přepisovat.

Kustomize jako takový nedělá nic jiného, než že přepisuje YAML, tedy mapu dle klíče a hodnot díky postupnému (hierarchickému) načítání souborů.

Příklad kustomization.yaml souboru v base adresáři:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - configmap.yaml
  - deployment.yaml
  - ingress.yaml
  - service.yaml

Příklad kustomization.yaml souboru v overlays/production adresáři:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
  - ../../base
patchesStrategicMerge:
  - ingress.yaml

Při používání Kustomize se typicky dodržuje následující struktura:

.
├── base
|   ├── configmap.yaml
│   ├── deployment.yaml
|   ├── ingress.yaml
│   ├── service.yaml
│   └── kustomization.yaml
└── overlays
    ├── production
    |   ├── ingress.yaml
    │   └── kustomization.yaml
    └── staging
        ├── ingress.yaml
        └── kustomization.yaml

Pokud si chcete zkontrolovat výsledný YAML manifest, ale zatím jej nechcete aplikovat do clusteru, můžete použít následující příkaz:

kustomize build overlays/production

Výstup můžete ručně zkontrolovat v příkazovém řádku nebo uložit do souboru a zkontrolovat v editoru nebo automaticky například v CI/CD pipeline pomocí nástroje jako je kubeconform nebo Kyverno.

Následně se pomocí kubectl apply a přepínače -k můžeme Kustomize soubory aplikovat:

kubectl apply -k overlays/production

Helm

Helm se staví k YAML souborům jiným způsobem. Protože Helm je napsaný v Go, používá tedy Go šablony, aby do YAMLu vložil hodnoty, které mu poskytneme a následně vyrenderuje výsledný YAML manifest, který pošle do Kubernetes clusteru, na základě kontextu v našem kubeconfigu.

Protože YAML je citlivý na odsazení a netisknutelné znaky obecně (whitespaces), řada lidí Helm nemá ráda nebo jej přímo nesnáší (stačí se podívat na Twitter/X). Podle mě je to o zvyku, ale při složitějších Helm chartech (tedy balíčcích) a hlavně YAML templatingu, to není tak přehledné jako čistý YAML.

Na druhou stranu, Helm je za mě skvělý nástroj, jak dodávat aplikace zákazníkům třetích stran. Helm je v tomto ohledu mnohem snažší na distribuci a použití, než Kustomize nebo čistý YAML. A díky integraci Helmu do snad všech GitOps nástrojů (například ArgoCD nebo FluxCD), není problém použít ani ty.

Co je trochu problematické u Helmu, je práce s Custom Resource Definitions (CRD), které Helm sice umí spravovat, ale to řešení není ideální. Helm nabízí podporu pro CRD skrze složku crds, ale tam nastává problém při aktualizaci nebo odinstalaci Helm chartu, kdy se CRD neaktualizují a neodinstalují. Hack/workaround je vložit je do složky templates, ale to není úplně ideální řešení vzhledem k povaze CRD.

Helm je nástroj do příkazové řádky (pokud nepoužíváte GitOps), takže běžné použití vypadá následovně:

# pokud instalujete Helm chart z adresáře, lokálně:
# helm install <název instalace chartu> <cesta>
helm install mychart ./mychart

# pokud instalujete Helm chart z Helm registry:
# 1. přidání Helm registry
# helm repo add <název repozitáře> <URL repozitáře>
helm repo add myrepo https://myrepo.com/charts
# 2. aktualizace repozitáře
helm repo update
# 3. instalace Helm chartu
# helm install <název instalace chartu> <název chartu>
helm install mychart myrepo/mychart

# případně je možné (a ze zkušenosti lepší) používat
# helm upgrade --install <název instalace chartu> <název chartu>
helm upgrade --install mychart myrepo/mychart

# odinstalace Helm chartu
helm uninstall mychart

Pokud byste si googlili informace o Helmu, tak hledejte pouze Helm 3.x, který je aktuální a podporovanou verzí.

Helm chart

Helm chart je balíček, který v sobě drží všechny manifesty a zároveň obsahuje tzv. values.yaml soubor, který popisuje, jaké hodnoty je možné do Helm chartu vložit.

Protože YAML nemá nijak definované schéma, jak má soubor vypadat (jaké mají být hodnoty a klíče atd.), začala komunita vytvářet JSON schémata pro YAML soubory, aby bylo možné validovat, jaké hodnoty můžeme do souboru vložit. Zároveň je možné tyto JSON schémata využít pro IDE, které podporují schémata, jako je například Visual Studio Code.

Helm chart se skládá z několika částí:

JSON schéma pro values.yaml je pouze komunitní zvyklostí a nemá jasně definovaný název, ale obvykle se jmenuje values.schema.json.

A Helm chart adresářová struktura může vypadat následovně:

.
├── charts
├── templates
│   ├── _helpers.tpl
│   ├── configmap.yaml
│   ├── deployment.yaml
│   ├── ingress.yaml
│   └── service.yaml
├── values.yaml
├── Chart.yaml
└── Chart.lock

Helm registry

Aby bylo možné Helm charty sdílet a instalovat, existuje Helm registry nebo můžete provozovat vlastní, popř. dnes je možné využít i OCI registries.

Helm registry je tedy adresář s Helm charty, který drží jednak index a také všechny verze Helm chartu.

Pokud Helm chart nahráváte do registry, Helm chart se zabalí do tarballu a následně se nahraje do registru. V závislosti na registry serveru, se pak tvoří index (index.yaml), který obsahuje informace o všech Helm charty automaticky, nebo je třeba jej generovat ručně.

Pokud byste chtěli vlastní self-hosted registry, je možné využít například Harbor nebo GitHub Pages.

Helm templating

Jak jsem psal výše, Helm používá Go šablony, které umožňují vkládat hodnoty do YAML manifestů.

Příklad deployment.yaml souboru s Helm templatingem:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-deployment
spec:
  replicas: {{ .Values.replicas }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}
    spec:
      containers:
      - name: {{ .Release.Name }}
        image: {{ .Values.image }}
        ports:
        - containerPort: {{ .Values.port }}

Helm má několik "globálních" proměnných, Chart, Release a Values, které umožňují přístup k různým hodnotám. V šabolnách je dále možné využít řadu funkcní jako je trim, toYaml a další (viz dokumentace). Nebo podmiňovat některé části manifestu pomocí if a else bloků.

Další důležitou součástí Helmu, jsou define bloky, které umožňují vytvářet vlastní funkce a tedy zjednodušit šablony. Tyto šablony jsou typicky v souboru templates/_helpers.tpl.

Příklad _helpers.tpl souboru:

{{- define "mychart.labels" -}}
labels:
  app: {{ .Release.Name }}
{{- end -}}

A následně v deployment.yaml souboru:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-deployment
spec:
  replicas: {{ .Values.replicas }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}
  template:
    metadata:
      {{ include "mychart.labels" . }}
    spec:
      containers:
      - name: {{ .Release.Name }}
        image: {{ .Values.image }}
        ports:
        - containerPort: {{ .Values.port }}

Další cesty

YAML, Helm a Kustomize ale nejsou jediné způsoby, jak nasazovat aplikace do Kubernetes.

jsonnet

Jsonnet je další templating jazyk, který umožňuje generovat JSON a YAML soubory. Osobně s ním nemám mnoho zkušeností, ale jedná se o další velmi oblíbený nástroj pro práci s Kubernetes manifesty.

Terraform

Terraform o kterém jsem psal v tomto článku je také možností pro nasazení aplikací do Kubernetes clusteru.

Konkrétně s pomocí Kubernetes provideru můžeme vytvářet a upravovat zdroje v Kubernetes clusteru. Jednotlivé resources jsou pak nativní Terraform resources v HCL (HashiCorp Configuration Language).

Příklad deploymentu NGINXu v Terraformu:

resource "kubernetes_deployment" "nginx" {
  metadata {
    name = "nginx-deployment"
  }

  spec {
    replicas = 3

    selector {
      match_labels = {
        app = "nginx"
      }
    }

    template {
      metadata {
        labels = {
          app = "nginx"
        }
      }

      spec {
        container {
          image = "nginx:1.14.2"
          name  = "nginx"

          port {
            container_port = 80
          }
        }
      }
    }
  }
}

Nebo využít komunitní kubectl provider, ten ale nespravuje resources v HCL, ale aplikuje přímo YAML soubory. Dalším užitečným providerem je Helm provider, který umožňuje spravovat Helm charty, pomocí Terraformu.

resource "helm_release" "nginx" {
  name       = "nginx"
  repository = "https://charts.bitnami.com/bitnami"
  chart      = "nginx"
  version    = "8.9.0"

  set {
    name  = "replicaCount"
    value = "3"
  }
}

Operátory

Kubernetes Operator je technicky aplikace, která ve svém kódu staví na existujících Kubernetes API a resources a vytváří abstrakce nad nimi. Obecně tedy rozšiřují možnosti Kubernetes a umožňují automatizovat operace, které by jinak byly manuální.

Operátor, aby vytvořil svoje resources, tzv. Custom Resources (CR), používá Custom Resource Definitions (CRD), tj. popisují Kubernetes, jak resourcy které operátor spravuje vypadají. V operátoru následně běží controller, který spravuje tyto resources.

Ale všechny definice CRD a CR, jsou opět typicky v YAMLu nebo je můžeme spravovat přes Kustomize popř. Helm. Takže si zas tolik nepomůžeme.

Kubernetes školení

Zajímá Vás Kubernetes a chcete se dozvědět víc? Jak pracovat s Kubernetes, jak spravovat cluster, jak nasadit aplikaci do Kubernetes atd.? Pak přijďte na moje školení o Kubernetes. Školení probíhá formou workshopu, kde si všechno vyzkoušíte na vlastní kůži. Přihlaste se buď na veřejný termín nebo mě kontaktujte ohledně firemního školení.