From 180fb91c95c40988f2016e8fc26c240417424a8d Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Mon, 13 Apr 2026 23:57:17 +0000 Subject: [PATCH] add fornax coordinator and worker k8s manifests --- servers/legion/apps/fornax.yaml | 65 +++++++ .../k8s/fornax/coordinator/deployment.yaml | 51 ++++++ .../fornax/coordinator/externalsecret.yaml | 23 +++ .../k8s/fornax/coordinator/ingress.yaml | 25 +++ .../k8s/fornax/coordinator/kustomization.yaml | 7 + .../k8s/fornax/coordinator/namespace.yaml | 6 + .../k8s/fornax/coordinator/service.yaml | 15 ++ servers/legion/k8s/fornax/kustomization.yaml | 8 + servers/legion/k8s/fornax/media-pv.yaml | 34 ++++ .../k8s/fornax/worker/base/deployment.yaml | 164 ++++++++++++++++++ .../fornax/worker/base/externalsecret.yaml | 18 ++ .../k8s/fornax/worker/base/kustomization.yaml | 7 + .../legion/k8s/fornax/worker/base/pvc.yaml | 12 ++ .../k8s/fornax/worker/base/service.yaml | 15 ++ .../overlays/worker-tx253/kustomization.yaml | 67 +++++++ .../overlays/worker-tx34/kustomization.yaml | 64 +++++++ 16 files changed, 581 insertions(+) create mode 100644 servers/legion/apps/fornax.yaml create mode 100644 servers/legion/k8s/fornax/coordinator/deployment.yaml create mode 100644 servers/legion/k8s/fornax/coordinator/externalsecret.yaml create mode 100644 servers/legion/k8s/fornax/coordinator/ingress.yaml create mode 100644 servers/legion/k8s/fornax/coordinator/kustomization.yaml create mode 100644 servers/legion/k8s/fornax/coordinator/namespace.yaml create mode 100644 servers/legion/k8s/fornax/coordinator/service.yaml create mode 100644 servers/legion/k8s/fornax/kustomization.yaml create mode 100644 servers/legion/k8s/fornax/media-pv.yaml create mode 100644 servers/legion/k8s/fornax/worker/base/deployment.yaml create mode 100644 servers/legion/k8s/fornax/worker/base/externalsecret.yaml create mode 100644 servers/legion/k8s/fornax/worker/base/kustomization.yaml create mode 100644 servers/legion/k8s/fornax/worker/base/pvc.yaml create mode 100644 servers/legion/k8s/fornax/worker/base/service.yaml create mode 100644 servers/legion/k8s/fornax/worker/overlays/worker-tx253/kustomization.yaml create mode 100644 servers/legion/k8s/fornax/worker/overlays/worker-tx34/kustomization.yaml diff --git a/servers/legion/apps/fornax.yaml b/servers/legion/apps/fornax.yaml new file mode 100644 index 0000000..9f8cfd8 --- /dev/null +++ b/servers/legion/apps/fornax.yaml @@ -0,0 +1,65 @@ +# Fornax coordinator — qBittorrent-compatible torrent distribution coordinator +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: fornax-coordinator + namespace: argocd +spec: + project: default + source: + repoURL: http://10.43.1.53:3000/will/infrastructure.git + targetRevision: main + path: servers/legion/k8s/fornax/coordinator + destination: + server: https://kubernetes.default.svc + namespace: media + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true +--- +# Fornax worker tx253 — gluetun (ProtonVPN) + qBittorrent worker +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: fornax-worker-tx253 + namespace: argocd +spec: + project: default + source: + repoURL: http://10.43.1.53:3000/will/infrastructure.git + targetRevision: main + path: servers/legion/k8s/fornax/worker/overlays/worker-tx253 + destination: + server: https://kubernetes.default.svc + namespace: media + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true +--- +# Fornax worker tx34 — gluetun (ProtonVPN) + qBittorrent worker +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: fornax-worker-tx34 + namespace: argocd +spec: + project: default + source: + repoURL: http://10.43.1.53:3000/will/infrastructure.git + targetRevision: main + path: servers/legion/k8s/fornax/worker/overlays/worker-tx34 + destination: + server: https://kubernetes.default.svc + namespace: media + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/servers/legion/k8s/fornax/coordinator/deployment.yaml b/servers/legion/k8s/fornax/coordinator/deployment.yaml new file mode 100644 index 0000000..3d6ac72 --- /dev/null +++ b/servers/legion/k8s/fornax/coordinator/deployment.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: fornax-coordinator + namespace: media + labels: + app: fornax-coordinator +spec: + replicas: 1 + selector: + matchLabels: + app: fornax-coordinator + template: + metadata: + labels: + app: fornax-coordinator + spec: + containers: + - name: coordinator + image: registry.neuralplatform.ai/fornax/coordinator:latest + imagePullPolicy: Always + ports: + - containerPort: 3000 + name: http + env: + - name: PORT + value: "3000" + - name: NODE_ENV + value: production + envFrom: + - secretRef: + name: fornax-coordinator-secrets + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + livenessProbe: + httpGet: + path: /api/v2/app/version + port: http + initialDelaySeconds: 15 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /api/v2/app/version + port: http + initialDelaySeconds: 5 + periodSeconds: 10 diff --git a/servers/legion/k8s/fornax/coordinator/externalsecret.yaml b/servers/legion/k8s/fornax/coordinator/externalsecret.yaml new file mode 100644 index 0000000..2365cda --- /dev/null +++ b/servers/legion/k8s/fornax/coordinator/externalsecret.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: fornax-coordinator-secrets + namespace: media +spec: + refreshInterval: 1h + secretStoreRef: + name: vault + kind: ClusterSecretStore + target: + name: fornax-coordinator-secrets + creationPolicy: Owner + data: + - secretKey: DATABASE_URL + remoteRef: + key: secret/data/fornax/coordinator + property: DATABASE_URL + - secretKey: REDPANDA_BROKERS + remoteRef: + key: secret/data/fornax/coordinator + property: REDPANDA_BROKERS diff --git a/servers/legion/k8s/fornax/coordinator/ingress.yaml b/servers/legion/k8s/fornax/coordinator/ingress.yaml new file mode 100644 index 0000000..a027b19 --- /dev/null +++ b/servers/legion/k8s/fornax/coordinator/ingress.yaml @@ -0,0 +1,25 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: fornax-coordinator + namespace: media + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + cert-manager.io/cluster-issuer: letsencrypt-prod +spec: + ingressClassName: traefik + tls: + - secretName: fornax-coordinator-tls + hosts: + - fornax.neuralplatform.ai + rules: + - host: fornax.neuralplatform.ai + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: fornax-coordinator + port: + number: 3000 diff --git a/servers/legion/k8s/fornax/coordinator/kustomization.yaml b/servers/legion/k8s/fornax/coordinator/kustomization.yaml new file mode 100644 index 0000000..ec12119 --- /dev/null +++ b/servers/legion/k8s/fornax/coordinator/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - externalsecret.yaml + - deployment.yaml + - service.yaml + - ingress.yaml diff --git a/servers/legion/k8s/fornax/coordinator/namespace.yaml b/servers/legion/k8s/fornax/coordinator/namespace.yaml new file mode 100644 index 0000000..e1cf873 --- /dev/null +++ b/servers/legion/k8s/fornax/coordinator/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: fornax + labels: + managed-by: argocd diff --git a/servers/legion/k8s/fornax/coordinator/service.yaml b/servers/legion/k8s/fornax/coordinator/service.yaml new file mode 100644 index 0000000..482b3e7 --- /dev/null +++ b/servers/legion/k8s/fornax/coordinator/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: fornax-coordinator + namespace: media + labels: + app: fornax-coordinator +spec: + selector: + app: fornax-coordinator + ports: + - name: http + port: 3000 + targetPort: 3000 + type: ClusterIP diff --git a/servers/legion/k8s/fornax/kustomization.yaml b/servers/legion/k8s/fornax/kustomization.yaml new file mode 100644 index 0000000..b5ddea8 --- /dev/null +++ b/servers/legion/k8s/fornax/kustomization.yaml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - media-pv.yaml + - coordinator/externalsecret.yaml + - coordinator/deployment.yaml + - coordinator/service.yaml + - coordinator/ingress.yaml diff --git a/servers/legion/k8s/fornax/media-pv.yaml b/servers/legion/k8s/fornax/media-pv.yaml new file mode 100644 index 0000000..64e251f --- /dev/null +++ b/servers/legion/k8s/fornax/media-pv.yaml @@ -0,0 +1,34 @@ +# Static PV for /media on Legion host — shared between workers and media apps +apiVersion: v1 +kind: PersistentVolume +metadata: + name: media-data + labels: + type: local +spec: + capacity: + storage: 500Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: "" + claimRef: + name: media-data + namespace: media + hostPath: + path: /media + type: Directory +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: media-data + namespace: media +spec: + accessModes: + - ReadWriteOnce + storageClassName: "" + resources: + requests: + storage: 500Gi + volumeName: media-data diff --git a/servers/legion/k8s/fornax/worker/base/deployment.yaml b/servers/legion/k8s/fornax/worker/base/deployment.yaml new file mode 100644 index 0000000..7f1276d --- /dev/null +++ b/servers/legion/k8s/fornax/worker/base/deployment.yaml @@ -0,0 +1,164 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: fornax-worker-WORKER_ID + namespace: media + labels: + app: fornax-worker-WORKER_ID + app.kubernetes.io/part-of: fornax + app.kubernetes.io/component: worker +spec: + replicas: 1 + selector: + matchLabels: + app: fornax-worker-WORKER_ID + template: + metadata: + labels: + app: fornax-worker-WORKER_ID + spec: + initContainers: + - name: tun-setup + image: busybox:latest + securityContext: + privileged: true + command: + - sh + - -c + - | + mkdir -p /dev/net && [ -c /dev/net/tun ] || mknod /dev/net/tun c 10 200 && \ + chmod 666 /dev/net/tun && \ + sysctl -w net.ipv6.conf.all.disable_ipv6=1 || true && \ + ip route flush table 51820 2>/dev/null || true && \ + ip rule del priority 101 2>/dev/null || true && \ + ip rule del table 51820 2>/dev/null || true + - name: qbt-config-patch + image: busybox:latest + command: + - sh + - -c + - | + CONF=/config/qBittorrent/qBittorrent.conf + HASH='@ByteArray(HqYj1eGsdXlQ4CSy597Y9A==:J9hsJIlU5FYfHb5rY5qQoIpVpTijryS/H+CE07oMtplL/ytneBVFd2tfVJtqGjhdht8tEi4wmqSSlqTgEu444w==)' + mkdir -p /config/qBittorrent + if [ -f "$CONF" ]; then + if grep -q "Password_PBKDF2" "$CONF"; then + sed -i "s|WebUI\\\\Password_PBKDF2=.*|WebUI\\\\Password_PBKDF2=\"$HASH\"|" "$CONF" + else + sed -i "/^\\[Preferences\\]/a WebUI\\\\Password_PBKDF2=\"$HASH\"" "$CONF" + fi + fi + echo "Password hash set." + volumeMounts: + - mountPath: /config + name: config + containers: + - name: gluetun + image: ghcr.io/qdm12/gluetun:latest + imagePullPolicy: Always + securityContext: + capabilities: + add: + - NET_ADMIN + env: + - name: VPN_SERVICE_PROVIDER + value: protonvpn + - name: VPN_TYPE + value: wireguard + - name: WIREGUARD_PRIVATE_KEY + valueFrom: + secretKeyRef: + name: fornax-worker-WORKER_ID-secrets + key: PROTONVPN_PRIVATE_KEY + - name: SERVER_NAMES + value: PROTONVPN_SERVER + - name: VPN_PORT_FORWARDING + value: "on" + - name: FIREWALL_OUTBOUND_SUBNETS + value: 10.42.0.0/16,10.43.0.0/16 + ports: + - containerPort: 8888 + name: proxy + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + cpu: 200m + memory: 512Mi + volumeMounts: + - mountPath: /tmp/gluetun + name: gluetun-data + - name: portforward-helper + image: alpine:latest + imagePullPolicy: Always + command: + - sh + - -c + - | + until apk add -q curl 2>/dev/null; do sleep 5; done + echo "Watching /tmp/gluetun/forwarded_port for assigned port..." + while true; do + if [ -f /tmp/gluetun/forwarded_port ]; then + PORT=$(cat /tmp/gluetun/forwarded_port) + if [ -n "$PORT" ] && [ "$PORT" != "0" ]; then + echo "$(date): Forwarded port: $PORT — updating qBittorrent" + curl -s -c /tmp/qbt.txt -X POST http://localhost:8080/api/v2/auth/login \ + -d "username=admin&password=adminadmin" >/dev/null 2>&1 + curl -s -b /tmp/qbt.txt -X POST http://localhost:8080/api/v2/app/setPreferences \ + -d "json={\"listen_port\":$PORT,\"random_port\":false}" >/dev/null 2>&1 + echo "$(date): qBittorrent listen port set to $PORT" + else + echo "$(date): Port file empty, waiting..." + fi + else + echo "$(date): Waiting for gluetun to write port file..." + fi + sleep 45 + done + resources: + requests: + cpu: 10m + memory: 32Mi + limits: + cpu: 50m + memory: 96Mi + volumeMounts: + - mountPath: /tmp/gluetun + name: gluetun-data + - name: qbittorrent + image: lscr.io/linuxserver/qbittorrent:latest + imagePullPolicy: Always + env: + - name: PUID + value: "1000" + - name: PGID + value: "1000" + - name: TZ + value: America/Chicago + - name: WEBUI_PORT + value: "8080" + ports: + - containerPort: 8080 + name: webui + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 1Gi + volumeMounts: + - mountPath: /config + name: config + - mountPath: /media + name: media + volumes: + - name: gluetun-data + emptyDir: {} + - name: config + persistentVolumeClaim: + claimName: fornax-worker-WORKER_ID-config + - name: media + persistentVolumeClaim: + claimName: media-data diff --git a/servers/legion/k8s/fornax/worker/base/externalsecret.yaml b/servers/legion/k8s/fornax/worker/base/externalsecret.yaml new file mode 100644 index 0000000..c50f289 --- /dev/null +++ b/servers/legion/k8s/fornax/worker/base/externalsecret.yaml @@ -0,0 +1,18 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: fornax-worker-WORKER_ID-secrets + namespace: media +spec: + refreshInterval: 1h + secretStoreRef: + name: vault + kind: ClusterSecretStore + target: + name: fornax-worker-WORKER_ID-secrets + creationPolicy: Owner + data: + - secretKey: PROTONVPN_PRIVATE_KEY + remoteRef: + key: secret/data/fornax/worker-WORKER_ID + property: private_key diff --git a/servers/legion/k8s/fornax/worker/base/kustomization.yaml b/servers/legion/k8s/fornax/worker/base/kustomization.yaml new file mode 100644 index 0000000..089d69f --- /dev/null +++ b/servers/legion/k8s/fornax/worker/base/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - externalsecret.yaml + - deployment.yaml + - service.yaml + - pvc.yaml diff --git a/servers/legion/k8s/fornax/worker/base/pvc.yaml b/servers/legion/k8s/fornax/worker/base/pvc.yaml new file mode 100644 index 0000000..9588f83 --- /dev/null +++ b/servers/legion/k8s/fornax/worker/base/pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: fornax-worker-WORKER_ID-config + namespace: media +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path + resources: + requests: + storage: 1Gi diff --git a/servers/legion/k8s/fornax/worker/base/service.yaml b/servers/legion/k8s/fornax/worker/base/service.yaml new file mode 100644 index 0000000..8176a89 --- /dev/null +++ b/servers/legion/k8s/fornax/worker/base/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: fornax-worker-WORKER_ID + namespace: media + labels: + app: fornax-worker-WORKER_ID +spec: + selector: + app: fornax-worker-WORKER_ID + ports: + - name: webui + port: 8080 + targetPort: 8080 + type: ClusterIP diff --git a/servers/legion/k8s/fornax/worker/overlays/worker-tx253/kustomization.yaml b/servers/legion/k8s/fornax/worker/overlays/worker-tx253/kustomization.yaml new file mode 100644 index 0000000..3a92399 --- /dev/null +++ b/servers/legion/k8s/fornax/worker/overlays/worker-tx253/kustomization.yaml @@ -0,0 +1,67 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ../../base + +namePrefix: "" +nameSuffix: "" + +patches: + - target: + kind: Deployment + name: fornax-worker-WORKER_ID + patch: |- + - op: replace + path: /metadata/name + value: fornax-worker-tx253 + - op: replace + path: /metadata/labels/app + value: fornax-worker-tx253 + - op: replace + path: /spec/selector/matchLabels/app + value: fornax-worker-tx253 + - op: replace + path: /spec/template/metadata/labels/app + value: fornax-worker-tx253 + - op: replace + path: /spec/template/spec/containers/0/env/3/value + value: US-TX#253 + - op: replace + path: /spec/template/spec/containers/0/env/2/valueFrom/secretKeyRef/name + value: fornax-worker-tx253-secrets + - op: replace + path: /spec/template/spec/volumes/1/persistentVolumeClaim/claimName + value: fornax-worker-tx253-config + - target: + kind: Service + name: fornax-worker-WORKER_ID + patch: |- + - op: replace + path: /metadata/name + value: fornax-worker-tx253 + - op: replace + path: /metadata/labels/app + value: fornax-worker-tx253 + - op: replace + path: /spec/selector/app + value: fornax-worker-tx253 + - target: + kind: PersistentVolumeClaim + name: fornax-worker-WORKER_ID-config + patch: |- + - op: replace + path: /metadata/name + value: fornax-worker-tx253-config + - target: + kind: ExternalSecret + name: fornax-worker-WORKER_ID-secrets + patch: |- + - op: replace + path: /metadata/name + value: fornax-worker-tx253-secrets + - op: replace + path: /spec/target/name + value: fornax-worker-tx253-secrets + - op: replace + path: /spec/data/0/remoteRef/key + value: secret/data/fornax/worker-tx253 diff --git a/servers/legion/k8s/fornax/worker/overlays/worker-tx34/kustomization.yaml b/servers/legion/k8s/fornax/worker/overlays/worker-tx34/kustomization.yaml new file mode 100644 index 0000000..e987044 --- /dev/null +++ b/servers/legion/k8s/fornax/worker/overlays/worker-tx34/kustomization.yaml @@ -0,0 +1,64 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ../../base + +patches: + - target: + kind: Deployment + name: fornax-worker-WORKER_ID + patch: |- + - op: replace + path: /metadata/name + value: fornax-worker-tx34 + - op: replace + path: /metadata/labels/app + value: fornax-worker-tx34 + - op: replace + path: /spec/selector/matchLabels/app + value: fornax-worker-tx34 + - op: replace + path: /spec/template/metadata/labels/app + value: fornax-worker-tx34 + - op: replace + path: /spec/template/spec/containers/0/env/3/value + value: US-TX#34 + - op: replace + path: /spec/template/spec/containers/0/env/2/valueFrom/secretKeyRef/name + value: fornax-worker-tx34-secrets + - op: replace + path: /spec/template/spec/volumes/1/persistentVolumeClaim/claimName + value: fornax-worker-tx34-config + - target: + kind: Service + name: fornax-worker-WORKER_ID + patch: |- + - op: replace + path: /metadata/name + value: fornax-worker-tx34 + - op: replace + path: /metadata/labels/app + value: fornax-worker-tx34 + - op: replace + path: /spec/selector/app + value: fornax-worker-tx34 + - target: + kind: PersistentVolumeClaim + name: fornax-worker-WORKER_ID-config + patch: |- + - op: replace + path: /metadata/name + value: fornax-worker-tx34-config + - target: + kind: ExternalSecret + name: fornax-worker-WORKER_ID-secrets + patch: |- + - op: replace + path: /metadata/name + value: fornax-worker-tx34-secrets + - op: replace + path: /spec/target/name + value: fornax-worker-tx34-secrets + - op: replace + path: /spec/data/0/remoteRef/key + value: secret/data/fornax/worker-tx34