Published on

ArgoCD Ollama: GitOps für lokale Modelle

Authors

TL;DR

ArgoCD Ollama ist die GitOps-Schicht über einem lokalen Ollama-Server: Manifeste liegen in Git, Argo CD gleicht den Cluster ab. Die Application-CR (argoproj.io/v1alpha1) zeigt auf repoURL, path und destination.namespace. Automated Sync startet ohne prune — die Doku setzt Prune standardmäßig aus. Modelle kommen nach einem gesunden Sync per PostSync-Job über POST /api/pull. Image ollama/ollama, Port 11434, Daten unter /root/.ollama. Gelesen in der Argo-CD- und Ollama-Doku am 2026-10-18. Kein Anbieter-Ranking.


Was die Query meint — und was nicht

IT-Leiter suchen „argocd ollama“, wenn Modellstand und Container-Image aus Git kommen sollen, nicht wenn sie Ollama einmal per Helm auf den Cluster legen. Der Cluster-Schnitt ohne GitOps bleibt Ollama auf Kubernetes. GitOps als Prinzip, ohne Ollama-Application: GitOps für KI-Plattformen. Dasselbe Image ohne Kubernetes: Ollama Docker. Bind-Adresse und Port: Ollama Port 11434.

Argo CD im selben Cluster erreicht das Ziel über https://kubernetes.default.svc. Ein externes Ziel registriert die Getting-Started-Seite extra. Die Application-Objekte liegen üblicherweise im Namespace argocd.


Application-CR: Quelle und Ziel

Die Spezifikation verlangt mindestens Projekt, Quelle und Ziel. CreateNamespace=true legt den Ziel-Namespace an, falls er fehlt.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: ollama
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://git.example.invalid/ki/ollama-gitops.git
    targetRevision: HEAD
    path: apps/ollama
  destination:
    server: https://kubernetes.default.svc
    namespace: ollama
  syncPolicy:
    syncOptions:
      - CreateNamespace=true

repoURL darf ein Git-Repo oder ein Helm-Chart-Repo sein. Bei einem Chart-Repo setzen Sie chart und lassen path weg. Für Git-hosted Charts bleibt path stehen, chart bleibt leer. valuesObject überschreibt Chart-Werte im Application-Objekt; das Feld hat Vorrang vor values.

Die Beispiel-URL oben ist ein Platzhalter. Tragen Sie Ihr internes Repo ein.


Was in Git liegt

Die Ollama-Docker-Seite startet den Server so: Image ollama/ollama, Volume /root/.ollama, Port 11434. Mit NVIDIA kommt --gpus=all dazu, nachdem das NVIDIA Container Toolkit steht. AMD nutzt den Tag rocm und die Geräte /dev/kfd sowie /dev/dri. Im Cluster entspricht das einem Workload plus Service; die GPU-Requests bleiben im Kubernetes-Artikel.

Ollama bindet laut FAQ standardmäßig 127.0.0.1:11434. Im Pod muss der Server im Cluster-Netz lauschen. Setzen Sie OLLAMA_HOST analog zur FAQ, zum Beispiel 0.0.0.0:11434. Ohne das bleibt der Service leer, obwohl der Container läuft.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ollama
  namespace: ollama
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ollama
  template:
    metadata:
      labels:
        app: ollama
    spec:
      containers:
        - name: ollama
          image: ollama/ollama
          ports:
            - containerPort: 11434
          env:
            - name: OLLAMA_HOST
              value: "0.0.0.0:11434"
          volumeMounts:
            - name: models
              mountPath: /root/.ollama
      volumes:
        - name: models
          persistentVolumeClaim:
            claimName: ollama-models
---
apiVersion: v1
kind: Service
metadata:
  name: ollama
  namespace: ollama
spec:
  selector:
    app: ollama
  ports:
    - port: 11434
      targetPort: 11434

Ein PersistentVolumeClaim für die Modellblobs gehört ins gleiche Verzeichnis. Ohne Volume zieht jeder neue Pod die Gewichte erneut. HTTPS_PROXY steuert laut FAQ den Modell-Pull; HTTP_PROXY soll nicht gesetzt werden, weil Ollama Modelle nur über HTTPS holt.


Automated Sync: zuerst ohne Prune

Automated Sync vergleicht Git und Live-Stand. Die Pipeline braucht dann keinen Zugriff auf die Argo-CD-API; ein Commit reicht. Die Auto-Sync-Seite nennt die Semantik:

  • Sync nur, wenn die Application OutOfSync ist.
  • Pro Kombination aus Commit-SHA und Parametern ein Versuch. Derselbe Stand wird nicht erneut synchronisiert, außer selfHeal ist an.
  • Ein fehlgeschlagener Sync gegen denselben Stand wird nicht wiederholt.
  • Rollback ist bei aktivem Automated Sync nicht möglich.
  • Das Reconcile-Intervall steht in argocd-cm unter timeout.reconciliation, Default 120s plus Jitter 60s, also höchstens drei Minuten.
spec:
  syncPolicy:
    automated:
      enabled: true
      prune: false
      selfHeal: false
      allowEmpty: false
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m
    syncOptions:
      - CreateNamespace=true

prune ist in der Doku absichtlich aus. Erst einschalten, wenn gelöschte Manifeste den Cluster wirklich räumen sollen. allowEmpty schützt vor einem leeren Repo, das sonst alle Ressourcen entfernen würde. selfHeal gleicht manuelle Cluster-Änderungen nach dem Self-Heal-Timeout wieder an Git an; der Controller-Default ist fünf Sekunden (--self-heal-timeout-seconds).

Die Retry-Zahlen oben stehen so in der Auto-Sync-Seite: fünf Versuche, Basis fünf Sekunden, Faktor zwei, Deckel drei Minuten. limit: -1 bedeutet unbegrenzt.


Modelle per PostSync ziehen

Ein Image-Rollout lädt keine GGUF-Datei. Die Pull-API erwartet POST /api/pull mit Pflichtfeld model. Beispiel der Doku: {"model": "gemma4"}. stream: false unterdrückt die Fortschrittszeilen.

PostSync läuft erst, wenn Sync-Hooks durch sind, das Apply geklappt hat und die Ressourcen Healthy sind. Die Hook-Annotation heißt argocd.argoproj.io/hook. Benannte Hooks entstehen nur einmal; generateName oder die Policy BeforeHookCreation erzeugt sie erneut. Fehlt die Delete-Policy, nimmt Argo CD BeforeHookCreation an. HookSucceeded löscht den Job nach Erfolg und hält den Sync-Status sauber. ttlSecondsAfterFinished am Job führt laut Hook-Doku zu OutOfSync, weil Git den Job noch kennt.

Hooks laufen nicht bei Selective Sync.

apiVersion: batch/v1
kind: Job
metadata:
  generateName: ollama-pull-
  namespace: ollama
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: pull
          image: curlimages/curl
          command:
            - curl
            - -sS
            - http://ollama.ollama.svc.cluster.local:11434/api/pull
            - -d
            - '{"model": "gemma4", "stream": false}'
  backoffLimit: 2

gemma4 ist das Beispiel aus der Pull-Doku, kein Empfehlungsranking. Tragen Sie das Modell ein, das Ihr Volume halten soll. Wellen steuert argocd.argoproj.io/sync-wave; Default ist Welle null, negative Wellen laufen früher. Beim Prunen kehrt sich die Reihenfolge um.

Ein leerer Generate-Request lädt ein bereits gezogenen Modell in den Speicher. Die FAQ zeigt POST /api/generate mit {"model": "mistral"}. Default bleibt das Modell fünf Minuten geladen; keep_alive: -1 hält es, 0 entlädt sofort. OLLAMA_KEEP_ALIVE setzt denselben Default für den Server.


Betrieb im Cluster

Die FAQ nennt die Server-Grenzen, die Sie als Env im Manifest setzen — nicht als freie Schätzung:

  • OLLAMA_CONTEXT_LENGTH überschreibt das Default-Fenster von 4096 Tokens.
  • OLLAMA_MAX_LOADED_MODELS begrenzt parallele Modelle. Default: dreimal die GPU-Zahl, bei CPU drei.
  • OLLAMA_NUM_PARALLEL ist default 1. RAM skaliert mit OLLAMA_NUM_PARALLEL mal OLLAMA_CONTEXT_LENGTH.
  • OLLAMA_MAX_QUEUE ist default 512. Darüber antwortet der Server mit 503.

0.0.0.0 öffnet alle Interfaces. Die FAQ verlangt dafür eine Netzbegrenzung. Im Cluster heißt das ClusterIP plus NetworkPolicy, kein NodePort ins Firmennetz ohne Proxy.

Installation von Argo CD selbst: Namespace argocd, Manifest stable/manifests/install.yaml, laut Getting-Started mit --server-side --force-conflicts. Grund: einige CRDs überschreiten das 262-KB-Limit der Client-Apply-Annotation. Für Produktion empfiehlt dieselbe Seite eine gepinnte Version; das dort genannte Beispiel ist v3.2.0. Das Admin-Startpasswort steht in argocd-initial-admin-secret und soll nach dem Wechsel gelöscht werden.


Häufige Fehler

Pod läuft, Service bleibt stumm. OLLAMA_HOST fehlt. Default ist 127.0.0.1:11434.

PostSync schlägt fehl. Der Hook wartet auf Healthy. Ohne laufenden Server oder ohne Service-DNS kommt der Curl nicht an. Selective Sync überspringt Hooks.

Application bleibt OutOfSync nach dem Pull-Job. Oft ttlSecondsAfterFinished statt hook-delete-policy.

Automated Sync wiederholt einen Fehler nicht. Derselbe Commit wird nach einem Fehlschlag nicht erneut versucht. Neuer Commit oder manuelles Sync.

Manuelle kubectl-Änderungen verschwinden. Dann ist selfHeal an. Ohne Self-Heal bleibt der Live-Stand, bis Git sich ändert.

Rollback in der UI ist gesperrt. Automated Sync und Rollback schließen sich laut Auto-Sync-Seite aus.


Fazit

Argo CD ersetzt nicht den Ollama-Server. Es hält Image, Env und Modell-Pull im selben Git-Stand. Zuerst Application ohne Prune, dann Volume, dann PostSync auf /api/pull. Der Kubernetes-Artikel bleibt für GPU-Scheduling zuständig; dieser Text nur für die GitOps-Schleife.

📖 Verwandte Artikel

Weitere interessante Beiträge zu ähnlichen Themen