add fornax coordinator and worker k8s manifests

This commit is contained in:
Will Anderson
2026-04-13 23:57:17 +00:00
parent 0aa1f4333c
commit 180fb91c95
16 changed files with 581 additions and 0 deletions
+65
View File
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- externalsecret.yaml
- deployment.yaml
- service.yaml
- ingress.yaml
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: fornax
labels:
managed-by: argocd
@@ -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
@@ -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
+34
View File
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- externalsecret.yaml
- deployment.yaml
- service.yaml
- 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
@@ -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
@@ -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
@@ -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