From e6cb187f8e29b416464d35e1a4fb7ff108222745 Mon Sep 17 00:00:00 2001 From: Hermes Agent service account Date: Mon, 31 Aug 2026 22:46:06 -0500 Subject: [PATCH] =?UTF-8?q?Deploy=20Body=20Wars=20Observability=20WebUI=20?= =?UTF-8?q?(Open=20WebUI=20=E2=86=92=20astro-orbiter=20vLLM)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Service: Open WebUI latest (ghcr.io/open-webui/open-webui:latest) - Backend: http://astro-orbiter:8000/v1 (Gemma-4-26B-A4B-it-AWQ) - Ingress: body-wars.local.mk-labs.cloud (letsencrypt-prod TLS) - Auth: vLLM API key from 1Password (op://mk-labs/vllm/api-key) - ArgoCD wave 9 (post-Hindsight observability) - ExternalSecret syncs credentials before deployment - Replicas: 1, Resource limits: 1GB memory, 1 CPU --- .../applications/body-wars/application.yaml | 50 +++++++++ .../applications/body-wars/deployment.yaml | 101 ++++++++++++++++++ .../body-wars/externalsecret.yaml | 28 +++++ cluster/applications/body-wars/ingress.yaml | 40 +++++++ cluster/applications/body-wars/namespace.yaml | 7 ++ cluster/applications/body-wars/service.yaml | 17 +++ .../body-wars/serviceaccount.yaml | 8 ++ 7 files changed, 251 insertions(+) create mode 100644 cluster/applications/body-wars/application.yaml create mode 100644 cluster/applications/body-wars/deployment.yaml create mode 100644 cluster/applications/body-wars/externalsecret.yaml create mode 100644 cluster/applications/body-wars/ingress.yaml create mode 100644 cluster/applications/body-wars/namespace.yaml create mode 100644 cluster/applications/body-wars/service.yaml create mode 100644 cluster/applications/body-wars/serviceaccount.yaml diff --git a/cluster/applications/body-wars/application.yaml b/cluster/applications/body-wars/application.yaml new file mode 100644 index 0000000..b590804 --- /dev/null +++ b/cluster/applications/body-wars/application.yaml @@ -0,0 +1,50 @@ +# ============================================================================ +# ArgoCD Application: Body Wars Observability WebUI (Open WebUI) +# Wave: 9 (post-Hindsight observability layer) +# Deployment method: GitOps (Gitea -> ArgoCD) +# ============================================================================ +# +# Connects Open WebUI to astro-orbiter vLLM (Gemma-4-26B-A4B-it-AWQ). +# Manifests: namespace, service account, deployment, service, ingress, secrets. +# TLS: letsencrypt-prod +# +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: body-wars + namespace: argocd + labels: + app.kubernetes.io/name: body-wars + app.kubernetes.io/part-of: mk-labs + annotations: + # Wave 9: Observability layer, after Hindsight (Wave 8) + argocd.argoproj.io/sync-wave: "9" + description: | + Body Wars: Observability WebUI for vLLM inference. + Frontend: Open WebUI (latest) + Backend: http://astro-orbiter:8000/v1 (vLLM OpenAI-compatible) + Model: Gemma-4-26B-A4B-it-AWQ + Ingress: body-wars.local.mk-labs.cloud (TLS via letsencrypt-prod) + Auth: API key from 1Password (op://mk-labs/vllm/api-key) +spec: + project: default + + source: + repoURL: https://gitea.mk-labs.cloud/rblundon/homelab.git + targetRevision: main + path: cluster/applications/body-wars + directory: + exclude: "application.yaml" + + destination: + server: https://kubernetes.default.svc + namespace: body-wars + + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true + - PrunePropagationPolicy=background diff --git a/cluster/applications/body-wars/deployment.yaml b/cluster/applications/body-wars/deployment.yaml new file mode 100644 index 0000000..fedcce1 --- /dev/null +++ b/cluster/applications/body-wars/deployment.yaml @@ -0,0 +1,101 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: open-webui + namespace: body-wars + labels: + app.kubernetes.io/name: open-webui + app.kubernetes.io/part-of: body-wars +spec: + replicas: 1 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + selector: + matchLabels: + app.kubernetes.io/name: open-webui + template: + metadata: + labels: + app.kubernetes.io/name: open-webui + app.kubernetes.io/part-of: body-wars + spec: + serviceAccountName: open-webui + containers: + - name: open-webui + image: ghcr.io/open-webui/open-webui:latest + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 8080 + protocol: TCP + env: + # OpenWebUI configuration + - name: WEBUI_SECRET_KEY + valueFrom: + secretKeyRef: + name: body-wars-secret + key: vllm-api-key + optional: false + - name: OAUTH_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: body-wars-secret + key: openwebui-admin-password + optional: false + # Backend configuration for vLLM + - name: OLLAMA_BASE_URLS + value: "http://astro-orbiter:8000" + - name: OPENAI_BASE_URL + value: "http://astro-orbiter:8000/v1" + - name: OPENAI_API_KEY + valueFrom: + secretKeyRef: + name: body-wars-secret + key: vllm-api-key + optional: false + - name: OPENAI_MODEL_FALLBACK + value: "Gemma-4-26B-A4B-it-AWQ" + # Allow access from ingress + - name: WEBUI_URL + value: "https://body-wars.local.mk-labs.cloud" + # Observability settings + - name: ENABLE_API_KEY + value: "true" + - name: ENABLE_MODEL_FILTER + value: "false" + # Storage + - name: DATA_DIR + value: "/app/backend/data" + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + livenessProbe: + httpGet: + path: /api/v1/models + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /api/v1/models + port: http + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 2 + volumeMounts: + - name: data + mountPath: /app/backend/data + volumes: + - name: data + emptyDir: + sizeLimit: 1Gi diff --git a/cluster/applications/body-wars/externalsecret.yaml b/cluster/applications/body-wars/externalsecret.yaml new file mode 100644 index 0000000..3ce70bb --- /dev/null +++ b/cluster/applications/body-wars/externalsecret.yaml @@ -0,0 +1,28 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: body-wars-credentials + namespace: body-wars + annotations: + # Sync priority: ExternalSecret fetches from 1Password before other resources deploy + argocd.argoproj.io/sync-wave: "-1" +spec: + refreshInterval: 1h + secretStoreRef: + name: onepassword-connect + kind: ClusterSecretStore + target: + name: body-wars-secret + creationPolicy: Owner + template: + engineVersion: v2 + data: + vllm-api-key: "{{ .vllm_api_key }}" + openwebui-admin-password: "{{ .openwebui_admin }}" + data: + - secretKey: vllm_api_key + remoteRef: + path: mk-labs/vllm/api-key + - secretKey: openwebui_admin + remoteRef: + path: mk-labs/openwebui/admin-password diff --git a/cluster/applications/body-wars/ingress.yaml b/cluster/applications/body-wars/ingress.yaml new file mode 100644 index 0000000..e95c998 --- /dev/null +++ b/cluster/applications/body-wars/ingress.yaml @@ -0,0 +1,40 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: body-wars + namespace: body-wars + labels: + app.kubernetes.io/name: body-wars + app.kubernetes.io/part-of: mk-labs + annotations: + # TLS certificate from Let's Encrypt (production) + cert-manager.io/cluster-issuer: letsencrypt-prod + # Nginx ingress controller settings + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + # Session affinity for WebSocket support + nginx.ingress.kubernetes.io/affinity: "cookie" + nginx.ingress.kubernetes.io/affinity-mode: "persistent" + # Observability header + nginx.ingress.kubernetes.io/enable-access-log: "true" + # Rate limiting to prevent abuse + nginx.ingress.kubernetes.io/limit-connections: "10" + nginx.ingress.kubernetes.io/limit-rps: "50" +spec: + ingressClassName: nginx + tls: + - hosts: + - body-wars.local.mk-labs.cloud + secretName: body-wars-tls + # Let's Encrypt production certificate + rules: + - host: body-wars.local.mk-labs.cloud + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: open-webui + port: + number: 80 diff --git a/cluster/applications/body-wars/namespace.yaml b/cluster/applications/body-wars/namespace.yaml new file mode 100644 index 0000000..17e2a41 --- /dev/null +++ b/cluster/applications/body-wars/namespace.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: body-wars + labels: + app.kubernetes.io/name: body-wars + app.kubernetes.io/part-of: mk-labs diff --git a/cluster/applications/body-wars/service.yaml b/cluster/applications/body-wars/service.yaml new file mode 100644 index 0000000..b599ea5 --- /dev/null +++ b/cluster/applications/body-wars/service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: open-webui + namespace: body-wars + labels: + app.kubernetes.io/name: open-webui + app.kubernetes.io/part-of: body-wars +spec: + type: ClusterIP + ports: + - name: http + port: 80 + targetPort: http + protocol: TCP + selector: + app.kubernetes.io/name: open-webui diff --git a/cluster/applications/body-wars/serviceaccount.yaml b/cluster/applications/body-wars/serviceaccount.yaml new file mode 100644 index 0000000..545459f --- /dev/null +++ b/cluster/applications/body-wars/serviceaccount.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: open-webui + namespace: body-wars + labels: + app.kubernetes.io/name: open-webui + app.kubernetes.io/part-of: body-wars