From 39f5fe3683db87b296c8eb62ec5fef5fced50ed1 Mon Sep 17 00:00:00 2001 From: rock Date: Tue, 8 Sep 2026 18:26:07 -0700 Subject: [PATCH] feat: add ComfyUI, rebalance GPU allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GPU rebalance (4× V100 32GB on worker-1): - reasoning: 2 GPU (unchanged, PP=2 for Qwen3-32B) - ornith: 2→1 GPU (scale to 1 replica, ornith:35b only) - comfyui: 0→1 GPU (new) - embeddings/reranker: 0 GPU (CPU, unchanged) qwen2.5:3b-instruct moved to CPU on talos-cp-2 (144GB RAM). Separate Ollama deployment + 5Gi PVC, pulls model on first start. Gateway config updated in homelab-frontend (separate commit). Co-authored-by: poimen --- k8s/apps/comfyui/deployment.yaml | 80 +++++++++++++++++ k8s/apps/comfyui/ingress.yaml | 25 ++++++ k8s/apps/comfyui/kustomization.yaml | 7 ++ k8s/apps/comfyui/service.yaml | 14 +++ k8s/apps/llm-serving/kustomization.yaml | 1 + k8s/apps/llm-serving/ornith.yaml | 23 ++--- k8s/apps/llm-serving/qwen-cpu.yaml | 115 ++++++++++++++++++++++++ k8s/argocd/apps/58-comfyui.yaml | 32 +++++++ 8 files changed, 281 insertions(+), 16 deletions(-) create mode 100644 k8s/apps/comfyui/deployment.yaml create mode 100644 k8s/apps/comfyui/ingress.yaml create mode 100644 k8s/apps/comfyui/kustomization.yaml create mode 100644 k8s/apps/comfyui/service.yaml create mode 100644 k8s/apps/llm-serving/qwen-cpu.yaml create mode 100644 k8s/argocd/apps/58-comfyui.yaml diff --git a/k8s/apps/comfyui/deployment.yaml b/k8s/apps/comfyui/deployment.yaml new file mode 100644 index 0000000..164cfba --- /dev/null +++ b/k8s/apps/comfyui/deployment.yaml @@ -0,0 +1,80 @@ +# ComfyUI — GPU-accelerated image generation on worker-1. +# Uses 1x V100 32GB (sm70). Freed by scaling ornith 2→1. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: comfyui + namespace: comfyui + labels: + app: comfyui +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: comfyui + template: + metadata: + labels: + app: comfyui + spec: + nodeSelector: + kubernetes.io/hostname: worker-1 + runtimeClassName: nvidia + containers: + - name: comfyui + image: ghcr.io/ai-dock/comfyui:v2-cuda-12.1.1-base-22.04 + ports: + - containerPort: 8188 + protocol: TCP + env: + - name: NVIDIA_VISIBLE_DEVICES + value: "all" + - name: COMFYUI_FLAGS + value: "--listen 0.0.0.0 --port 8188" + resources: + requests: + cpu: "4" + memory: 8Gi + nvidia.com/gpu: "1" + limits: + cpu: "8" + memory: 16Gi + nvidia.com/gpu: "1" + volumeMounts: + - mountPath: /workspace/ComfyUI/models + name: models + - mountPath: /workspace/ComfyUI/output + name: output + readinessProbe: + httpGet: + path: / + port: 8188 + periodSeconds: 10 + initialDelaySeconds: 30 + startupProbe: + httpGet: + path: / + port: 8188 + failureThreshold: 60 + periodSeconds: 10 + volumes: + - name: models + persistentVolumeClaim: + claimName: comfyui-models + - name: output + emptyDir: {} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: comfyui-models + namespace: comfyui +spec: + accessModes: + - ReadWriteOnce + storageClassName: longhorn + resources: + requests: + storage: 50Gi diff --git a/k8s/apps/comfyui/ingress.yaml b/k8s/apps/comfyui/ingress.yaml new file mode 100644 index 0000000..69e5cc5 --- /dev/null +++ b/k8s/apps/comfyui/ingress.yaml @@ -0,0 +1,25 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: comfyui + namespace: comfyui + annotations: + nginx.ingress.kubernetes.io/proxy-read-timeout: "600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "600" + nginx.ingress.kubernetes.io/proxy-body-size: "0" + # WebSocket support for ComfyUI's live preview + nginx.ingress.kubernetes.io/proxy-http-version: "1.1" + nginx.ingress.kubernetes.io/proxy-set-headers: "Upgrade" +spec: + ingressClassName: nginx + rules: + - host: comfy.riotpiao.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: comfyui + port: + number: 80 diff --git a/k8s/apps/comfyui/kustomization.yaml b/k8s/apps/comfyui/kustomization.yaml new file mode 100644 index 0000000..1d4f62e --- /dev/null +++ b/k8s/apps/comfyui/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - deployment.yaml + - service.yaml + - ingress.yaml diff --git a/k8s/apps/comfyui/service.yaml b/k8s/apps/comfyui/service.yaml new file mode 100644 index 0000000..7afee74 --- /dev/null +++ b/k8s/apps/comfyui/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: comfyui + namespace: comfyui + labels: + app: comfyui +spec: + selector: + app: comfyui + ports: + - port: 80 + targetPort: 8188 + protocol: TCP diff --git a/k8s/apps/llm-serving/kustomization.yaml b/k8s/apps/llm-serving/kustomization.yaml index af1bb5a..e7da0a8 100644 --- a/k8s/apps/llm-serving/kustomization.yaml +++ b/k8s/apps/llm-serving/kustomization.yaml @@ -14,6 +14,7 @@ resources: - ornith.yaml - reasoning.yaml - reranker.yaml + - qwen-cpu.yaml - networkpolicy.yaml # No namespace transformer: every file sets its own, and the transformer would # rewrite metadata.namespace on anything cross-namespace added later. diff --git a/k8s/apps/llm-serving/ornith.yaml b/k8s/apps/llm-serving/ornith.yaml index 97189f3..5abfa20 100644 --- a/k8s/apps/llm-serving/ornith.yaml +++ b/k8s/apps/llm-serving/ornith.yaml @@ -33,12 +33,8 @@ spec: ollama pull ornith:35b - ollama pull qwen2.5:3b-instruct - ollama run ornith:35b "ok" >/dev/null 2>&1 || true - ollama run qwen2.5:3b-instruct "ok" >/dev/null 2>&1 || true - wait $SERVE_PID ' @@ -54,7 +50,7 @@ spec: - name: OLLAMA_NUM_PARALLEL value: '1' - name: OLLAMA_MAX_LOADED_MODELS - value: '2' + value: '1' image: ollama/ollama:0.32.9@sha256:1685741456770df6e3cceb2a945a5f75e020f658d1701509668d6f4688f1dd3f name: kserve-container ports: @@ -65,8 +61,7 @@ spec: command: - /bin/sh - -c - - ollama ps 2>/dev/null | grep -q ornith && ollama ps 2>/dev/null | - grep -q qwen2.5 + - ollama ps 2>/dev/null | grep -q ornith periodSeconds: 10 resources: limits: @@ -82,8 +77,7 @@ spec: command: - /bin/sh - -c - - ollama ps 2>/dev/null | grep -q ornith && ollama ps 2>/dev/null | - grep -q qwen2.5 + - ollama ps 2>/dev/null | grep -q ornith failureThreshold: 120 periodSeconds: 15 volumeMounts: @@ -91,13 +85,10 @@ spec: name: models deploymentStrategy: type: Recreate - # 2 replicas -- each its own GPU, each loading both ornith:35b and - # qwen2.5:3b-instruct -- so 2 concurrent implementer-style calls each - # get an independent instance instead of contending on one, at the - # cost of judge/qwen traffic still sharing whichever replica an - # implementer call also lands on. - maxReplicas: 2 - minReplicas: 2 + # 1 replica -- ornith:35b only. qwen2.5:3b moved to CPU on cp-2. + # Frees 1 GPU for ComfyUI. + maxReplicas: 1 + minReplicas: 1 nodeSelector: kubernetes.io/hostname: worker-1 runtimeClassName: nvidia diff --git a/k8s/apps/llm-serving/qwen-cpu.yaml b/k8s/apps/llm-serving/qwen-cpu.yaml new file mode 100644 index 0000000..3fca3d5 --- /dev/null +++ b/k8s/apps/llm-serving/qwen-cpu.yaml @@ -0,0 +1,115 @@ +# qwen2.5:3b-instruct on CPU (talos-cp-2, 144GB RAM, 24 cores). +# Moved off GPU to free a V100 for ComfyUI. Latency ~10x slower +# than GPU but sufficient for lightweight tasks (summarization, +# classification, quick answers). +apiVersion: apps/v1 +kind: Deployment +metadata: + name: qwen-cpu + namespace: llm-serving + labels: + app: qwen-cpu + app.kubernetes.io/name: qwen-cpu + app.kubernetes.io/part-of: llm-serving +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: qwen-cpu + template: + metadata: + labels: + app: qwen-cpu + app.kubernetes.io/name: qwen-cpu + app.kubernetes.io/part-of: llm-serving + spec: + nodeSelector: + kubernetes.io/hostname: talos-cp-2 + tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule + containers: + - name: ollama + image: ollama/ollama:0.32.9@sha256:1685741456770df6e3cceb2a945a5f75e020f658d1701509668d6f4688f1dd3f + command: ["/bin/sh", "-c"] + args: + - | + ollama serve & + SERVE_PID=$! + until ollama list >/dev/null 2>&1; do sleep 2; done + ollama pull qwen2.5:3b-instruct + ollama run qwen2.5:3b-instruct "ok" >/dev/null 2>&1 || true + wait $SERVE_PID + env: + - name: OLLAMA_HOST + value: "0.0.0.0:8080" + - name: OLLAMA_MODELS + value: /root/.ollama/models + - name: OLLAMA_CONTEXT_LENGTH + value: "32768" + - name: OLLAMA_KEEP_ALIVE + value: "-1" + - name: OLLAMA_MAX_LOADED_MODELS + value: "1" + - name: OLLAMA_NUM_PARALLEL + value: "2" + ports: + - containerPort: 8080 + protocol: TCP + readinessProbe: + exec: + command: ["/bin/sh", "-c", "ollama ps 2>/dev/null | grep -q qwen2.5"] + periodSeconds: 10 + startupProbe: + exec: + command: ["/bin/sh", "-c", "ollama ps 2>/dev/null | grep -q qwen2.5"] + failureThreshold: 60 + periodSeconds: 10 + resources: + requests: + cpu: "4" + memory: 4Gi + limits: + cpu: "8" + memory: 8Gi + volumeMounts: + - mountPath: /root/.ollama + name: ollama-data + volumes: + - name: ollama-data + persistentVolumeClaim: + claimName: qwen-cpu-data +--- +apiVersion: v1 +kind: Service +metadata: + name: qwen-cpu + namespace: llm-serving + labels: + app: qwen-cpu + app.kubernetes.io/part-of: llm-serving +spec: + selector: + app: qwen-cpu + ports: + - port: 80 + targetPort: 8080 + protocol: TCP +--- +# Small PVC for qwen2.5:3b model weights (~1.9GB). +# Separate from llm-models PVC which is pinned to worker-1. +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: qwen-cpu-data + namespace: llm-serving +spec: + accessModes: + - ReadWriteOnce + storageClassName: longhorn + resources: + requests: + storage: 5Gi diff --git a/k8s/argocd/apps/58-comfyui.yaml b/k8s/argocd/apps/58-comfyui.yaml new file mode 100644 index 0000000..cc311a8 --- /dev/null +++ b/k8s/argocd/apps/58-comfyui.yaml @@ -0,0 +1,32 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: comfyui + namespace: argocd + labels: + app.kubernetes.io/name: comfyui + app.kubernetes.io/component: image-generation + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: homelab + revisionHistoryLimit: 3 + source: + repoURL: https://forgejo.riotpiao.com/rock/homelab.git + targetRevision: main + path: k8s/apps/comfyui + destination: + server: https://kubernetes.default.svc + namespace: comfyui + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + retry: + limit: 5 + backoff: + duration: 5s + factor: 2 + maxDuration: 3m