· · 12 min leestijd Advanced

K8sGPT met een lokaal 70B model op Apple Silicon

Hands-on test van K8sGPT met Llama 3.3 70B op een Mac Studio M3 Ultra. Werkt autonomous cluster management met lokale LLMs? Inclusief security analyse.

Een brein in een pot aan een klein cluster mompelt diagnoses, zij houdt een hand op de schakelaar, een wolk-blob wacht buiten

TL;DR Ik heb K8sGPT op een Llama 3.3 70B-model gezet dat op een Mac Studio in m’n eigen kantoor draait, zodat er geen clustermetadata het netwerk verlaat, en er drie kapotte workloads doorheen gehaald. Alle drie gevonden, en de fixes die het schreef had ik van een collega geaccepteerd, op de resource-getallen na, want die waren verzonnen. Assisted diagnose werkt, met 25 seconden per query. Autonomous cluster management is nog steeds marketing, en de security-sectie is waarom ik een model niks op eigen houtje zou laten aanpassen.

Elk vendor-pitchdeck heeft op dit moment dezelfde slide. “Autonomous cluster management.” Een AI kijkt naar je Kubernetes-cluster, ziet het probleem, stelt de diagnose en fixt het terwijl jij slaapt. Platform engineers stoppen met brandjes blussen en het cluster geneest zichzelf.

Ik draai een homelab omdat ik wil weten wat erin gebeurt. Dus zo’n slide wil ik eerst zelf draaien voordat ik ’m geloof.

Het addertje bij de meeste van die tools is wat “AI” blijkt te betekenen als je de kleine lettertjes leest. Het betekent dat je clustermetadata naar OpenAI gaat. Podnamen, labels, foutmeldingen, alles. Ik self-host juist zodat die data op m’n eigen netwerk blijft, en een AIOps-verhaal dat pas werkt als ik dat aan een derde partij geef, heeft mij niks te bieden.

Dus dit is de versie die ik wilde proberen. K8sGPT gericht op een Llama 3.3 70B-model dat lokaal draait op Apple Silicon. Geen cloud-API, geen data die het pand verlaat. Is een lokaal model goed genoeg om nuttig te zijn voor clusterdiagnose? Of kost soevereiniteit hier meer dan het waard is? Die vraag beantwoordt deze post.

Eén ding voor de commando’s beginnen. Dit is een homelab-experiment. Ik beschrijf wat ik getest heb en wat ik vond, en de security-sectie verderop is de reden dat je niks hiervan in productie moet draaien. Lees het zo.

De hardware en de software

De hardware

Een Mac Studio M3 Ultra met 512GB unified memory. De M3 Ultra heeft 80 GPU-cores die je voor inference kunt gebruiken, en unified memory betekent dat er niks gekopieerd wordt tussen CPU-RAM en GPU-RAM.

Met zo’n €10.000 is het een dure bak, en ik doe niet alsof dat anders is. Het is ook zo’n beetje de enige consumentenhardware die een 70B-model op Q8-quantization draait op een snelheid waar je op blijft wachten. Unified memory is de hele truc. Op een gewone PC met een losse kaart betaal je bij elke pass een kopieerbelasting tussen CPU- en GPU-RAM, en die belasting is wat je de das omdoet.

De softwarestack

ComponentVersieRol
vLLM0.6.xInference server met Metal backend
Llama 3.3 70BQ8_0Het taalmodel (~75GB)
K8sGPT Operator0.1.xKubernetes operator voor diagnose
k3s1.29.xLokaal Kubernetes cluster

vLLM installeren met de Metal-backend

vLLM heeft experimentele Metal-support voor Apple Silicon. De installatie is een conda-environment en één pip:

# Maak een dedicated conda environment
conda create -n vllm python=3.11
conda activate vllm

# Installeer vLLM met Metal support
pip install vllm

# Verifieer Metal backend
python -c "import vllm; print(vllm.__version__)"

Experimenteel is het juiste woord. Op het moment van schrijven is de Metal-support in vLLM ruw, en dat merk je. llama.cpp met z’n Metal-backend is de stabiele optie, maar K8sGPT wil een OpenAI-compatible API en die geeft vLLM me out of the box. Dus ik nam de ruigere route, liever dan een shim schrijven.

Het model downloaden

# Download het model (Q8 quantization, ~75GB)
huggingface-cli download meta-llama/Llama-3.3-70B-Instruct \
  --local-dir ./models/llama-3.3-70b-instruct

Je hebt een Hugging Face-account nodig en je moet de Llama-licentie accepteren.

De vLLM-server starten

# Start de inference server
vllm serve ./models/llama-3.3-70b-instruct \
  --host 0.0.0.0 \
  --port 8000 \
  --dtype float16 \
  --max-model-len 8192 \
  --device mps

De --device mps-flag forceert Metal Performance Shaders. Laat je ’m weg, dan valt vLLM terug op de CPU.

Check dat ie draait:

curl http://localhost:8000/v1/models

De K8sGPT-operator deployen

De operator gaat met Helm het cluster in:

# Voeg de Helm repo toe
helm repo add k8sgpt https://charts.k8sgpt.ai/
helm repo update

# Installeer de operator
helm install k8sgpt-operator k8sgpt/k8sgpt-operator \
  -n k8sgpt-operator-system \
  --create-namespace

Dan een K8sGPT-resource die de backend naar de vLLM-server op de Mac laat wijzen:

# k8sgpt-config.yaml
apiVersion: core.k8sgpt.ai/v1alpha1
kind: K8sGPT
metadata:
  name: k8sgpt-local
  namespace: k8sgpt-operator-system
spec:
  ai:
    enabled: true
    model: llama-3.3-70b-instruct
    backend: localai
    baseUrl: http://192.168.1.100:8000/v1  # IP van je Mac Studio
  noCache: false
  version: v0.3.40
  analyzers:
    - Pod
    - Deployment
    - Service
    - ReplicaSet
    - PersistentVolumeClaim
    - Ingress
    - StatefulSet
    - CronJob
kubectl apply -f k8sgpt-config.yaml

Drie kapotte dingen

De setup draait. Nu het deel dat telt. Is het nuttig, of is het een heel dure manier om met overtuiging een fout antwoord te krijgen? Ik gooide er drie bewust kapotte dingen in en gaf de output een cijfer zoals ik dat bij de eerste poging van een junior engineer zou doen.

Scenario A: een CrashLoopBackOff

Een deployment die naar een ConfigMap verwijst die niet bestaat:

# broken-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: broken-app
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: broken-app
  template:
    metadata:
      labels:
        app: broken-app
    spec:
      containers:
      - name: app
        image: nginx:1.25
        envFrom:
        - configMapRef:
            name: app-config  # Deze ConfigMap bestaat niet
kubectl apply -f broken-deployment.yaml

Na een minuut staat de pod in CrashLoopBackOff. De operator schrijft z’n analyse naar een results-resource:

kubectl get results -n k8sgpt-operator-system -o yaml

Geparafraseerd was de output dit:

Analysis: Pod broken-app-xxxx is in CreateContainerConfigError state.
The pod is referencing a ConfigMap 'app-config' that does not exist
in the namespace.

Suggested remediation:
1. Create the missing ConfigMap:
   kubectl create configmap app-config --from-literal=KEY=value
2. Or remove the configMapRef from the deployment spec
3. Verify the ConfigMap name spelling matches exactly

Bruikbaar. De diagnose klopt en de suggesties zijn praktisch. Een junior engineer kan hiermee uit de voeten zonder op een dwaalspoor te raken. Wat het miste is de ene check die ik als eerste had gedaan, of de ConfigMap in een andere namespace staat. Die fout maak ik ongeveer eens per maand.

Scenario B: geen resource-limits

Een deployment met drie replica’s en helemaal geen resources-blok:

# no-limits.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: no-limits-app
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: no-limits
  template:
    metadata:
      labels:
        app: no-limits
    spec:
      containers:
      - name: app
        image: nginx:1.25
        # Geen resources gedefinieerd

De analyse van K8sGPT:

Analysis: Deployment no-limits-app has containers without resource
requests or limits defined.

Issues identified:
- Container 'app' has no CPU requests/limits
- Container 'app' has no memory requests/limits

Suggested remediation:
Add resource specifications to ensure predictable scheduling and
prevent resource starvation:

resources:
  requests:
    memory: "64Mi"
    cpu: "100m"
  limits:
    memory: "128Mi"
    cpu: "200m"

Deels bruikbaar. Het probleem ziet het. De getallen die het je geeft zijn verzonnen. Het model heeft geen enkele metric gezien, dus niks in die requests en limits is gebaseerd op wat deze workload doet. Een ervaren engineer haalt eerst echte data op, kubectl top pods of Prometheus, en sized van daaruit. De LLM gokt, en niks in de output zegt dat.

Het noemt ook de Vertical Pod Autoscaler niet, waarschuwt niet dat limits zonder requests kunnen bijten, en de verhouding 1:2 tussen requests en limits komt uit de lucht vallen.

Scenario C: een privileged pod

Een pod die zo’n beetje alles doet waar een security-policy voor bestaat om tegen te houden:

# insecure-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: insecure-pod
  namespace: default
spec:
  hostNetwork: true
  containers:
  - name: app
    image: nginx:1.25
    securityContext:
      privileged: true
      runAsRoot: true
    volumeMounts:
    - name: host-root
      mountPath: /host
  volumes:
  - name: host-root
    hostPath:
      path: /

De analyse, met de Trivy-integratie aan:

Analysis: Pod insecure-pod has multiple security issues:

Critical:
- Container running in privileged mode (privileged: true)
- Container has access to host network (hostNetwork: true)
- Container mounts host root filesystem

High:
- No securityContext.readOnlyRootFilesystem
- No securityContext.allowPrivilegeEscalation: false

Suggested remediation:
1. Remove privileged: true unless absolutely necessary
2. Use a dedicated network namespace (remove hostNetwork: true)
3. Avoid mounting host paths, especially root filesystem
4. Add security hardening:

   securityContext:
     privileged: false
     readOnlyRootFilesystem: true
     allowPrivilegeEscalation: false
     runAsNonRoot: true
     capabilities:
       drop:
         - ALL

Bruikbaar. Deze verraste me. De issues staan er allemaal en de remediatie leest als wat een security engineer je zou vertellen. Trivy verdient hier z’n plek door ook de image-vulnerabilities te flaggen, zodat je de misconfiguratie en het CVE -verhaal in één pass krijgt.

Wat mist is de policy-laag. Geen woord over Pod Security Standards en het restricted-profiel, en geen woord dat een Kyverno- of OPA -policy deze pod had moeten tegenhouden voordat hij ooit gescheduled werd.

Performance

Inference-snelheid

MetricWaarde
Tokens/seconde (prompt)~180 t/s
Tokens/seconde (generation)~25 t/s
Eerste token latency~2.5s
Typische analyse (500 tokens out)~22s

End-to-end latency

Van het moment dat een probleem bestaat tot het moment dat het rapport er ligt:

FaseTijd
Issue detectie (polling)30s (configurabel)
Context gathering~2s
LLM inference~20-30s
Result storage<1s
Totaal~55s

Resourcegebruik

Tijdens inference:

ResourceGebruik
GPU Memory (Metal)~78GB
CPU~15% (data preprocessing)
System Memory~12GB (naast model)
Power draw~180W

Tegenover de OpenAI-API

MetricLokaal (70B)OpenAI GPT-4
Latency~25s~5s
KwaliteitGoedZeer goed
Kosten€0 (na hardware)~€0.03/query
PrivacyVolledig lokaalData naar OpenAI

De OpenAI-API is sneller en de output is wat beter. De prijs is dat je clustermetadata je netwerk verlaat. Voor mij is die ruil de hele kern van de oefening. Ik wacht 25 seconden in plaats van 5 om de data thuis te houden, en dat vind ik geen moeilijke keuze. Of jij dezelfde maakt hangt af van wat er in je pod-labels staat.

Air-gapped

Kan het zonder enige internetverbinding? Ja, met wat voorbereiding. Hier verdient het lokale model zichzelf terug, want de air-gapped case is van tafel zodra je van een cloud-API afhangt.

Wat je vooraf downloadt

# 1. Model weights (~75GB)
huggingface-cli download meta-llama/Llama-3.3-70B-Instruct \
  --local-dir ./airgap-bundle/models/

# 2. vLLM Python packages
pip download vllm -d ./airgap-bundle/packages/

# 3. K8sGPT container images
docker pull ghcr.io/k8sgpt-ai/k8sgpt-operator:latest
docker save ghcr.io/k8sgpt-ai/k8sgpt-operator:latest > ./airgap-bundle/images/k8sgpt-operator.tar

# 4. Helm charts
helm pull k8sgpt/k8sgpt-operator --destination ./airgap-bundle/charts/

Transport en installatie

# Op de air-gapped machine:

# 1. Installeer Python packages offline
pip install --no-index --find-links=./airgap-bundle/packages/ vllm

# 2. Laad container images
docker load < ./airgap-bundle/images/k8sgpt-operator.tar
# Of push naar je lokale registry

# 3. Installeer Helm chart
helm install k8sgpt-operator ./airgap-bundle/charts/k8sgpt-operator-*.tgz \
  --set image.repository=your-local-registry/k8sgpt-operator

Wat offline werkt

ComponentAir-Gap ReadyNotes
vLLMJaGeen phone-home
Llama modelJaEenmalige download
K8sGPT OperatorJaGeen telemetry
Trivy DBNeeVereist periodieke updates

De Trivy-vulnerabilitydatabase is de uitzondering. Die heeft periodieke updates nodig, en die moet je apart transporteren. Zonder recente database mist K8sGPT nieuwe CVE’s.

Security en het threat-model

Hier moet ik eerlijk zijn. Een lokaal model geeft je datasoevereiniteit en verder niks aan de securitykant. De setup is zo veilig als de bak waar hij op draait, en een Mac Studio die als inference-server op je netwerk staat brengt z’n eigen stapel problemen mee. De LLM brengt een tweede stapel mee die specifiek voor het model geldt.

Platformproblemen

Een Mac Studio als inference-server heeft beperkingen die je niet wegconfigureert:

IssueImpact
Geen TPMGeen hardware attestation, geen measured boot
macOS is general-purposeNiet hardened zoals RHEL/Ubuntu met CIS benchmarks
Geen Secure Boot chainBoot process is niet cryptografisch geverifieerd
Updates vereisen internetOf handmatige interventie in air-gapped scenario
Single-user focusmacOS is niet ontworpen voor multi-tenant security

Dus voor alles met echte compliance-eisen, ISO27001 Annex A, NIS2 , SOC2, is een Mac Studio de verkeerde machine. Voor een homelab of een developmentomgeving is het prima.

Risico’s die bij het model horen

RisicoBeschrijving
Non-determinismeDezelfde input kan verschillende outputs geven
Prompt injectionMalicious pod names/labels kunnen de LLM manipuleren
HallucinationsModel kan schadelijke remediatie suggereren
Context leakageInfo uit eerdere queries kan in responses verschijnen
Supply chainModel weights kunnen backdoored zijn

Het threat-model

ThreatLikelihoodImpactMitigatie
Prompt injection via pod metadataMediumHighInput sanitization, output validation
Hallucinated destructive commandsMediumCriticalHuman-in-the-loop, geen auto-remediation
Model weights tamperingLowCriticalChecksum verificatie, trusted source
Context window data leakageMediumMediumKorte context, geen persistent memory
Unauthorized access to inference APIMediumHighNetwork segmentation, auth
Resource exhaustion (DoS)LowMediumRate limiting, resource quotas

Wat ik ermee zou doen

Bruikbaar, onder voorwaarden

Ja, een lokaal model is bruikbaar voor Kubernetes-diagnose, onder voorwaarden. Het herkent standaardissues correct, geeft remediatie waar je wat mee kunt, vangt securityproblemen, en doet dat allemaal zonder dat er een byte het netwerk verlaat.

Wat het niet kan telt net zo zwaar. Het kan geen probleem debuggen dat over meerdere componenten loopt. Het kan geen auto-remediation doen die je vertrouwt. Het kent jouw setup niet, en het kan niet garanderen dat het gelijk heeft.

Per use case

Homelab en leren

Ga ervoor. Je leert over LLM-inference-infrastructuur, over Kubernetes-troubleshootingpatronen en over waar AI-assisted operations ophouden nuttig te zijn, en de blast radius is je eigen cluster.

Development en staging

Bruikbaar met vangrails. Iemand beoordeelt de output voordat er iets wordt toegepast, elke LLM-interactie wordt gelogd, en er is geen auto-remediation. Alleen diagnose.

Productie, met internet

Gebruik een cloud-API. De modellen zijn beter, GPT-4 of Claude, de latency is lager, er hoeft geen hardware gekocht te worden en iemand anders tekent de SLA . Voor de meeste organisaties is de privacy-afweging acceptabel, zolang er geen PII in de clustermetadata zit.

Productie, air-gapped of soeverein

Alleen als laatste optie. Mag de data écht niet naar buiten, kijk dan eerst naar kleinere, dedicated modellen en zet defense-in-depth om de inference-server heen. Behandel elke regel die het model produceert als untrusted, log en audit alles, en hou het een assistent in plaats van een autoriteit.

Terug naar de slide

Autonomous cluster management met LLM’s is vandaag marketing. Wat ik getest heb is assisted diagnose, een model dat naar problemen wijst en suggesties doet, met mij die elke suggestie leest. De human-in-the-loop is een vereiste, en die gaat er niet af zodra je het model genoeg vertrouwt.

En toch ben ik onder de indruk. Een 70B-model op hardware in m’n eigen kantoor produceerde analyses die ik van een collega had geaccepteerd. Het gat tussen goed genoeg om te lezen en goed genoeg om zonder mij op een productiecluster te handelen is nog breed. Scenario B laat zien hoe breed. Het model verzon resource-limits en presenteerde ze als advies.

Ik heb het antwoord dat ik kwam halen. Een lokaal model is goed genoeg als diagnostische assistent, en het hier draaien betekent dat ik nooit heb hoeven kiezen tussen AIOps en de redenen uit Sovereign Infrastructure. Why Privacy Matters is de andere helft van waarom de data thuisblijft. Dus het blijft een assistent. Elke suggestie lees ik voordat ik ’m intyp, zoals ik dat zou doen bij een scherpe collega die m’n cluster nog nooit gezien heeft.