0322fc6989
## Summary - Bumps the jellyfin-ha image to v0.2.0 on fafflix and cheeztv ## Why v0.2.0 bakes the LDAP and SSO auth plugins into the image; with the plugin config PR this enables browser SSO and app-password logins. Reviewed-on: #426 Co-authored-by: unkin-agent <unkin-agent@unkin.net> Co-committed-by: unkin-agent <unkin-agent@unkin.net>
254 lines
10 KiB
YAML
254 lines
10 KiB
YAML
---
|
|
apiVersion: apps/v1
|
|
kind: StatefulSet
|
|
metadata:
|
|
name: cheeztv
|
|
namespace: cheeztv
|
|
spec:
|
|
# HA: two replicas coordinate transcode session ownership through Valkey and
|
|
# resume each other's HLS segments off the shared RWX transcode PVC. Stable
|
|
# pod names (cheeztv-0/1) are the lease owner identity, hence StatefulSet.
|
|
replicas: 2
|
|
serviceName: cheeztv
|
|
podManagementPolicy: Parallel
|
|
updateStrategy:
|
|
type: RollingUpdate
|
|
selector:
|
|
matchLabels:
|
|
app: cheeztv
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: cheeztv
|
|
spec:
|
|
securityContext:
|
|
# Group-write the shared RWX volumes and grant the render/video groups so
|
|
# the runAsUser 1000 process can open the Intel DRI render node injected
|
|
# by the device plugin.
|
|
fsGroup: 1000
|
|
supplementalGroups:
|
|
- 44
|
|
- 105
|
|
- 109
|
|
seccompProfile:
|
|
type: RuntimeDefault
|
|
affinity:
|
|
# Spread the two replicas across nodes for node-level HA. Soft so a
|
|
# single-GPU-node cluster still schedules both (i915 has 4 shared slots).
|
|
podAntiAffinity:
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
|
- weight: 100
|
|
podAffinityTerm:
|
|
labelSelector:
|
|
matchLabels:
|
|
app: cheeztv
|
|
topologyKey: kubernetes.io/hostname
|
|
initContainers:
|
|
# Seed the fork's PostgreSQL provider (database.xml) and Intel iGPU
|
|
# hardware transcode settings (encoding.xml) before Jellyfin starts.
|
|
# Runs as root to chown into the shared config volume; mirrors the fork
|
|
# Helm chart's inject-db-config. Each file is written only when absent so
|
|
# admin changes persisted to the shared RWX /config survive pod restarts.
|
|
- name: inject-config
|
|
image: busybox:1.37.0
|
|
command:
|
|
- sh
|
|
- -c
|
|
- |
|
|
mkdir -p /config/config
|
|
chown 1000:1000 /config/config
|
|
chmod 775 /config/config
|
|
if [ ! -e /config/config/database.xml ]; then
|
|
cat > /config/config/database.xml << 'DBEOF'
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
<DatabaseConfigurationOptions>
|
|
<DatabaseType>Jellyfin-PostgreSQL</DatabaseType>
|
|
<LockingBehavior>NoLock</LockingBehavior>
|
|
</DatabaseConfigurationOptions>
|
|
DBEOF
|
|
chown 1000:1000 /config/config/database.xml
|
|
chmod 664 /config/config/database.xml
|
|
fi
|
|
# VAAPI on the Intel render node the device plugin injects
|
|
# (/dev/dri/renderD128 — ffmpeg's default DRM node, reachable via
|
|
# the render/video supplementalGroups). Without this the attached
|
|
# iGPU is idle and every transcode runs in software. Omitted
|
|
# elements fall back to the fork's EncodingOptions defaults.
|
|
if [ ! -e /config/config/encoding.xml ]; then
|
|
cat > /config/config/encoding.xml << 'ENCEOF'
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
<EncodingOptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
|
<EncodingThreadCount>-1</EncodingThreadCount>
|
|
<HardwareAccelerationType>vaapi</HardwareAccelerationType>
|
|
<VaapiDevice>/dev/dri/renderD128</VaapiDevice>
|
|
<EnableHardwareEncoding>true</EnableHardwareEncoding>
|
|
<AllowHevcEncoding>true</AllowHevcEncoding>
|
|
<AllowAv1Encoding>false</AllowAv1Encoding>
|
|
<EnableIntelLowPowerH264HwEncoder>false</EnableIntelLowPowerH264HwEncoder>
|
|
<EnableIntelLowPowerHevcHwEncoder>false</EnableIntelLowPowerHevcHwEncoder>
|
|
<EnableTonemapping>false</EnableTonemapping>
|
|
<EnableVppTonemapping>false</EnableVppTonemapping>
|
|
<HardwareDecodingCodecs>
|
|
<string>h264</string>
|
|
<string>hevc</string>
|
|
<string>vc1</string>
|
|
<string>vp9</string>
|
|
</HardwareDecodingCodecs>
|
|
</EncodingOptions>
|
|
ENCEOF
|
|
chown 1000:1000 /config/config/encoding.xml
|
|
chmod 664 /config/config/encoding.xml
|
|
fi
|
|
resources:
|
|
requests:
|
|
cpu: 10m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 64Mi
|
|
volumeMounts:
|
|
- name: config
|
|
mountPath: /config
|
|
containers:
|
|
- name: cheeztv
|
|
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/jellyfin-ha:v0.2.0
|
|
imagePullPolicy: IfNotPresent
|
|
ports:
|
|
- name: http
|
|
containerPort: 8096
|
|
protocol: TCP
|
|
env:
|
|
# Pod identity for the Valkey transcode lease owner. The fork reads
|
|
# JELLYFIN_INSTANCE_ID (falling back to MachineName); the stable
|
|
# StatefulSet pod name gives each replica a unique lease identity so
|
|
# takeover can target a dead replica. JELLYFIN_HA_POD_NAME is set for
|
|
# parity with the fork Helm chart (nothing currently reads it).
|
|
- name: JELLYFIN_INSTANCE_ID
|
|
valueFrom:
|
|
fieldRef:
|
|
fieldPath: metadata.name
|
|
- name: JELLYFIN_HA_POD_NAME
|
|
valueFrom:
|
|
fieldRef:
|
|
fieldPath: metadata.name
|
|
# Multiple replicas must not each answer UDP auto-discovery.
|
|
- name: JELLYFIN_Network__AutoDiscovery
|
|
value: "false"
|
|
# Config dir must differ from the data root (Jellyfin sanity check).
|
|
- name: JELLYFIN_CONFIG_DIR
|
|
value: /config/config
|
|
# Distributed transcode session store (cheeztv-ha additions).
|
|
- name: Jellyfin__TranscodeStore__RedisConnectionString
|
|
value: "valkey-cheeztv-valkey:6379,abortConnect=false"
|
|
- name: Jellyfin__TranscodeStore__LeaseDurationSeconds
|
|
value: "30"
|
|
# PostgreSQL main DB via the CNPG-generated app secret, routed through
|
|
# the PgBouncer pooler. Composed with $(VAR) expansion so the password
|
|
# is never rendered into the manifest; CNPG passwords are URL-safe.
|
|
- name: PGUSER
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: cheeztv-postgres-app
|
|
key: username
|
|
- name: PGPASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: cheeztv-postgres-app
|
|
key: password
|
|
- name: PGDB
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: cheeztv-postgres-app
|
|
key: dbname
|
|
- name: POSTGRES_CONNECTION_STRING
|
|
value: "postgresql://$(PGUSER):$(PGPASSWORD)@cheeztv-postgres-pooler:5432/$(PGDB)"
|
|
- name: DATABASE_URL
|
|
value: "postgresql://$(PGUSER):$(PGPASSWORD)@cheeztv-postgres-pooler:5432/$(PGDB)"
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /health
|
|
port: http
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 30
|
|
timeoutSeconds: 5
|
|
failureThreshold: 3
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /health
|
|
port: http
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 10
|
|
timeoutSeconds: 5
|
|
failureThreshold: 3
|
|
resources:
|
|
requests:
|
|
cpu: "1"
|
|
memory: 1Gi
|
|
gpu.intel.com/i915: "1"
|
|
limits:
|
|
cpu: "4"
|
|
memory: 6Gi
|
|
# Intel iGPU (QSV/VA-API) slot. Requesting it pins the pod to a
|
|
# GPU-labelled node and injects /dev/dri/renderD* automatically, so
|
|
# no /dev/dri hostPath or privileged container is needed. VA-API is
|
|
# pre-enabled via the seeded encoding.xml (see inject-config), so
|
|
# transcodes use the iGPU on first boot with no manual UI step.
|
|
gpu.intel.com/i915: "1"
|
|
securityContext:
|
|
runAsUser: 1000
|
|
runAsGroup: 1000
|
|
volumeMounts:
|
|
- name: config
|
|
mountPath: /config
|
|
- name: transcode
|
|
# Fork's real transcode temp path. RWX so a surviving pod reads the
|
|
# in-flight .ts/.m3u8 segments of the pod it takes over. A per-pod
|
|
# volume here silently breaks HA takeover.
|
|
mountPath: /config/transcodes
|
|
- name: cache
|
|
mountPath: /cache
|
|
- name: media-tv
|
|
# Kids-only instance: mount just the tvshows/kids subtree of the
|
|
# shared TV subvolume (subPath kids) so cheeztv's library never
|
|
# exposes the adult tree. Path matches fafflix's /media/tv-kids
|
|
# mount so the same episode resolves identically across instances.
|
|
mountPath: /media/tv
|
|
subPath: kids
|
|
readOnly: true
|
|
- name: media-movies
|
|
# Kids-only instance: mount just the movies/kids subtree of the
|
|
# shared movies subvolume (subPath kids).
|
|
mountPath: /media/movies
|
|
subPath: kids
|
|
readOnly: true
|
|
volumes:
|
|
- name: config
|
|
persistentVolumeClaim:
|
|
claimName: cheeztv-config
|
|
- name: transcode
|
|
persistentVolumeClaim:
|
|
claimName: cheeztv-transcode
|
|
- name: media-tv
|
|
persistentVolumeClaim:
|
|
claimName: cheeztv-media-tv
|
|
- name: media-movies
|
|
persistentVolumeClaim:
|
|
claimName: cheeztv-media-movies
|
|
volumeClaimTemplates:
|
|
# Per-pod scratch cache — RWO, disposable, one PVC per replica.
|
|
- metadata:
|
|
name: cache
|
|
annotations:
|
|
# Exclude the per-pod cache PVCs from the cheeztv-config k8up Schedule
|
|
# (skipWithoutAnnotation is false cluster-wide). Cache is disposable and
|
|
# RWO — it would also fail to mount into the backup pod while in use.
|
|
k8up.io/backup: "false"
|
|
spec:
|
|
accessModes:
|
|
- ReadWriteOnce
|
|
resources:
|
|
requests:
|
|
storage: 30Gi
|
|
storageClassName: cephrbd-fast-delete
|
|
volumeMode: Filesystem
|