arrstack,fafflix,cheeztv: provision shared mediastore CephFS volume (#428)
## Why
The media estate is currently split across two 1Ti CephFS subvolumes (`media-tv`
and `media-movies`), so a download that lands in one and imports to the other is
a cross-filesystem copy, not a hardlink move. A single 10Ti `mediastore`
subvolume (already created on `cephfs_csi_ssd_ec_4_1`) collapses that: one
filesystem holding `/{fafflix,cheeztv}/{tvseries,movies}` plus
`/nzbget/downloads/complete`, so every arr import is an atomic same-fs hardlink.
This PR only provisions the volume and seeds its tree. No existing PV, PVC or
workload is touched, and nothing mounts the new claims yet — the cutover of the
arrs and the two jellyfins is a separate change.
## How
- Add static PVs `arrstack-mediastore`, `fafflix-mediastore` and
`cheeztv-mediastore`, all pointing at the same rootPath
(`/volumes/csi_ssd_ec_4_1/mediastore/a0152dac-…`) with unique names and
volumeHandles pinned by `claimRef` — the established pattern for the shared
media subvolumes.
- Add the matching RWX 10Ti PVCs (`mediastore` in arrstack,
`fafflix-mediastore`, `cheeztv-mediastore`), annotated
`k8up.io/backup: "false"` and statically bound via `volumeName` +
`storageClassName: ""`.
- Add `mediastore-bootstrap`, a one-shot ArgoCD Sync-hook Job
(`hook-delete-policy: BeforeHookCreation`, no sync-wave needed) that mounts
the arrstack claim and `mkdir -p`s the directory tree as uid/gid 1000 —
the uid the arrstack media pods run as. Idempotent, so it self-heals on
every sync.
- Wire the new manifests into the arrstack, fafflix and cheeztv bases.
## Validation
- `kustomize build` clean on `apps/overlays/au-syd1/{arrstack,fafflix,cheeztv}`
- `kubeconform` clean on all three overlays (91 / 29 / 32 resources valid)
- pre-commit (yamllint, check-yaml, no-plain-secrets) passed
Reviewed-on: #428
Co-authored-by: unkin-agent <unkin-agent@unkin.net>
Co-committed-by: unkin-agent <unkin-agent@unkin.net>
This commit was merged in pull request #428.
This commit is contained in:
@@ -8,9 +8,12 @@ resources:
|
|||||||
- pv-media-tv.yaml
|
- pv-media-tv.yaml
|
||||||
- pv-media-movies.yaml
|
- pv-media-movies.yaml
|
||||||
- pv-mediafs.yaml
|
- pv-mediafs.yaml
|
||||||
|
- pv-mediastore.yaml
|
||||||
- pvc-media-tv.yaml
|
- pvc-media-tv.yaml
|
||||||
- pvc-media-movies.yaml
|
- pvc-media-movies.yaml
|
||||||
- pvc-mediafs.yaml
|
- pvc-mediafs.yaml
|
||||||
|
- pvc-mediastore.yaml
|
||||||
|
- mediastore-bootstrap-job.yaml
|
||||||
- media-bucket.yaml
|
- media-bucket.yaml
|
||||||
- backups-bucket.yaml
|
- backups-bucket.yaml
|
||||||
- postgres
|
- postgres
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
# Seeds the directory skeleton on the freshly created mediastore subvolume so
|
||||||
|
# the arrs, nzbget and both jellyfins mount subPaths that already exist and are
|
||||||
|
# owned by uid/gid 1000 (the uid every arrstack media pod runs as). mkdir -p is
|
||||||
|
# idempotent, so re-running it on every sync is harmless and self-heals a tree
|
||||||
|
# someone deleted by hand.
|
||||||
|
#
|
||||||
|
# Sync hook with BeforeHookCreation delete: ArgoCD replaces the completed Job
|
||||||
|
# each sync instead of failing on the immutable pod template. No sync-wave is
|
||||||
|
# needed -- the PVC applies in the same wave and the pod simply stays Pending
|
||||||
|
# until it binds.
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: mediastore-bootstrap
|
||||||
|
namespace: arrstack
|
||||||
|
annotations:
|
||||||
|
argocd.argoproj.io/hook: Sync
|
||||||
|
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
|
||||||
|
spec:
|
||||||
|
backoffLimit: 6
|
||||||
|
ttlSecondsAfterFinished: 600
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: mediastore-bootstrap
|
||||||
|
spec:
|
||||||
|
serviceAccountName: default
|
||||||
|
automountServiceAccountToken: false
|
||||||
|
restartPolicy: Never
|
||||||
|
securityContext:
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 1000
|
||||||
|
runAsGroup: 1000
|
||||||
|
fsGroup: 1000
|
||||||
|
fsGroupChangePolicy: OnRootMismatch
|
||||||
|
seccompProfile:
|
||||||
|
type: RuntimeDefault
|
||||||
|
containers:
|
||||||
|
- name: mkdir
|
||||||
|
image: docker.io/library/alpine:3
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
set -eu
|
||||||
|
mkdir -p \
|
||||||
|
/media/fafflix/tvseries \
|
||||||
|
/media/fafflix/movies \
|
||||||
|
/media/cheeztv/tvseries \
|
||||||
|
/media/cheeztv/movies \
|
||||||
|
/media/nzbget/downloads/complete
|
||||||
|
ls -la /media
|
||||||
|
volumeMounts:
|
||||||
|
- name: mediastore
|
||||||
|
mountPath: /media
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 32Mi
|
||||||
|
limits:
|
||||||
|
cpu: 200m
|
||||||
|
memory: 128Mi
|
||||||
|
volumes:
|
||||||
|
- name: mediastore
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: mediastore
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
# Static PV for the shared MEDIASTORE CephFS subvolume: one 10Ti filesystem
|
||||||
|
# holding every library plus the nzbget download tree, so arr imports are
|
||||||
|
# same-filesystem hardlink moves across tv AND movies. Same rootPath as the
|
||||||
|
# fafflix/cheeztv mediastore PVs; each namespace gets its own PV (unique name +
|
||||||
|
# volumeHandle) pinned by claimRef.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: arrstack-mediastore
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: 10Ti
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
persistentVolumeReclaimPolicy: Retain
|
||||||
|
storageClassName: ""
|
||||||
|
volumeMode: Filesystem
|
||||||
|
claimRef:
|
||||||
|
namespace: arrstack
|
||||||
|
name: mediastore
|
||||||
|
csi:
|
||||||
|
driver: cephfs.csi.ceph.com
|
||||||
|
volumeHandle: arrstack-mediastore-static
|
||||||
|
nodeStageSecretRef:
|
||||||
|
name: csi-cephfs-secret
|
||||||
|
namespace: csi-cephfs
|
||||||
|
volumeAttributes:
|
||||||
|
staticVolume: "true"
|
||||||
|
clusterID: cephfs_csi_ssd_ec_4_1
|
||||||
|
fsName: cephfs
|
||||||
|
rootPath: /volumes/csi_ssd_ec_4_1/mediastore/a0152dac-a51b-4b95-ac5e-ecdd99bfe3f1
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
# Whole media tree (/fafflix, /cheeztv, /nzbget) on one RWX filesystem, shared
|
||||||
|
# across the sonarr/radarr/nzbget pods. Statically bound to the
|
||||||
|
# arrstack-mediastore PV (the same CephFS subvolume fafflix and cheeztv mount).
|
||||||
|
# storageClassName "" + volumeName disables dynamic provisioning and binds the
|
||||||
|
# pre-created static PV.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: mediastore
|
||||||
|
namespace: arrstack
|
||||||
|
annotations:
|
||||||
|
k8up.io/backup: "false"
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Ti
|
||||||
|
storageClassName: ""
|
||||||
|
volumeName: arrstack-mediastore
|
||||||
|
volumeMode: Filesystem
|
||||||
@@ -15,8 +15,10 @@ resources:
|
|||||||
- pvc-transcode.yaml
|
- pvc-transcode.yaml
|
||||||
- pv-media-tv.yaml
|
- pv-media-tv.yaml
|
||||||
- pv-media-movies.yaml
|
- pv-media-movies.yaml
|
||||||
|
- pv-mediastore.yaml
|
||||||
- pvc-media-tv.yaml
|
- pvc-media-tv.yaml
|
||||||
- pvc-media-movies.yaml
|
- pvc-media-movies.yaml
|
||||||
|
- pvc-mediastore.yaml
|
||||||
- statefulset.yaml
|
- statefulset.yaml
|
||||||
- plugin-configmap.yaml
|
- plugin-configmap.yaml
|
||||||
- pdb.yaml
|
- pdb.yaml
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
# Static PV for the shared MEDIASTORE CephFS subvolume. Same rootPath as
|
||||||
|
# arrstack's mediastore PV so the arrs write and cheeztv reads the identical
|
||||||
|
# library tree (cheeztv scans /cheeztv/{tvseries,movies}); each namespace gets
|
||||||
|
# its own PV (unique name + volumeHandle) pinned by claimRef.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: cheeztv-mediastore
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: 10Ti
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
persistentVolumeReclaimPolicy: Retain
|
||||||
|
storageClassName: ""
|
||||||
|
volumeMode: Filesystem
|
||||||
|
claimRef:
|
||||||
|
namespace: cheeztv
|
||||||
|
name: cheeztv-mediastore
|
||||||
|
csi:
|
||||||
|
driver: cephfs.csi.ceph.com
|
||||||
|
volumeHandle: cheeztv-mediastore-static
|
||||||
|
nodeStageSecretRef:
|
||||||
|
name: csi-cephfs-secret
|
||||||
|
namespace: csi-cephfs
|
||||||
|
volumeAttributes:
|
||||||
|
staticVolume: "true"
|
||||||
|
clusterID: cephfs_csi_ssd_ec_4_1
|
||||||
|
fsName: cephfs
|
||||||
|
rootPath: /volumes/csi_ssd_ec_4_1/mediastore/a0152dac-a51b-4b95-ac5e-ecdd99bfe3f1
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
# Shared media tree, read-many across replicas. Statically bound to the
|
||||||
|
# cheeztv-mediastore PV (the CephFS subvolume also used by arrstack and
|
||||||
|
# fafflix). storageClassName "" + volumeName disables dynamic provisioning and
|
||||||
|
# binds the pre-created static PV.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: cheeztv-mediastore
|
||||||
|
namespace: cheeztv
|
||||||
|
annotations:
|
||||||
|
# Exclude from the cheeztv-config k8up Schedule (skipWithoutAnnotation is
|
||||||
|
# false cluster-wide, so unannotated PVCs are swept in). Only cheeztv-config
|
||||||
|
# is backed up; the media library is not restic-backup material.
|
||||||
|
k8up.io/backup: "false"
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Ti
|
||||||
|
storageClassName: ""
|
||||||
|
volumeName: cheeztv-mediastore
|
||||||
|
volumeMode: Filesystem
|
||||||
@@ -15,8 +15,10 @@ resources:
|
|||||||
- pvc-transcode.yaml
|
- pvc-transcode.yaml
|
||||||
- pv-media-tv.yaml
|
- pv-media-tv.yaml
|
||||||
- pv-media-movies.yaml
|
- pv-media-movies.yaml
|
||||||
|
- pv-mediastore.yaml
|
||||||
- pvc-media-tv.yaml
|
- pvc-media-tv.yaml
|
||||||
- pvc-media-movies.yaml
|
- pvc-media-movies.yaml
|
||||||
|
- pvc-mediastore.yaml
|
||||||
- statefulset.yaml
|
- statefulset.yaml
|
||||||
- plugin-configmap.yaml
|
- plugin-configmap.yaml
|
||||||
- pdb.yaml
|
- pdb.yaml
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
# Static PV for the shared MEDIASTORE CephFS subvolume. Same rootPath as
|
||||||
|
# arrstack's mediastore PV so the arrs write and fafflix reads the identical
|
||||||
|
# library tree (fafflix scans /fafflix/{tvseries,movies}); each namespace gets
|
||||||
|
# its own PV (unique name + volumeHandle) pinned by claimRef.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: fafflix-mediastore
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: 10Ti
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
persistentVolumeReclaimPolicy: Retain
|
||||||
|
storageClassName: ""
|
||||||
|
volumeMode: Filesystem
|
||||||
|
claimRef:
|
||||||
|
namespace: fafflix
|
||||||
|
name: fafflix-mediastore
|
||||||
|
csi:
|
||||||
|
driver: cephfs.csi.ceph.com
|
||||||
|
volumeHandle: fafflix-mediastore-static
|
||||||
|
nodeStageSecretRef:
|
||||||
|
name: csi-cephfs-secret
|
||||||
|
namespace: csi-cephfs
|
||||||
|
volumeAttributes:
|
||||||
|
staticVolume: "true"
|
||||||
|
clusterID: cephfs_csi_ssd_ec_4_1
|
||||||
|
fsName: cephfs
|
||||||
|
rootPath: /volumes/csi_ssd_ec_4_1/mediastore/a0152dac-a51b-4b95-ac5e-ecdd99bfe3f1
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
# Shared media tree, read-many across replicas. Statically bound to the
|
||||||
|
# fafflix-mediastore PV (the CephFS subvolume also used by arrstack and
|
||||||
|
# cheeztv). storageClassName "" + volumeName disables dynamic provisioning and
|
||||||
|
# binds the pre-created static PV.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: fafflix-mediastore
|
||||||
|
namespace: fafflix
|
||||||
|
annotations:
|
||||||
|
# Exclude from the fafflix-config k8up Schedule (skipWithoutAnnotation is
|
||||||
|
# false cluster-wide, so unannotated PVCs are swept in). Only fafflix-config
|
||||||
|
# is backed up; the media library is not restic-backup material.
|
||||||
|
k8up.io/backup: "false"
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Ti
|
||||||
|
storageClassName: ""
|
||||||
|
volumeName: fafflix-mediastore
|
||||||
|
volumeMode: Filesystem
|
||||||
Reference in New Issue
Block a user