arrstack: run sonarr/radarr/prowlarr active-active on shared Postgres (#arrstack)
Convert the three *arr apps from single-replica upstream LinuxServer/SQLite to our -unkin2 forks running active-active (replicas 3) on a shared CNPG Postgres cluster. - new apps/base/arrstack/postgres/: CNPG Cluster arrstack-postgres (3 instances, 3 managed login roles sonarr/radarr/prowlarr, throwaway initdb owner), per-app Database CRDs (<app>-main), cephrgw backup bucket + nightly ScheduledBackup, and 3 VaultStaticSecrets syncing the <app>-db role credentials. - per app: image -> docker-internal/<app>:v<base>-unkin2; replicas 3 + RollingUpdate; drop the apikey-init initContainer + PUID/PGID/TZ; run the binary directly with -nosingleinstancecheck; env from a new ConfigMap plus Postgres user/password and the API key from Secrets; /config PVC -> RWX cephfs-raid5-retain (shared).
This commit is contained in:
@@ -5,10 +5,13 @@ metadata:
|
||||
name: sonarr
|
||||
namespace: arrstack
|
||||
spec:
|
||||
replicas: 1
|
||||
# Active-active: the -unkin2 fork keeps all state in the shared Postgres
|
||||
# (arrstack-postgres) and coordinates via Postgres advisory locks, so N
|
||||
# replicas run concurrently behind the sonarr Service. RollingUpdate is safe —
|
||||
# no SQLite, no RWO lock.
|
||||
replicas: 3
|
||||
strategy:
|
||||
# RWO config PVC + single stateful SQLite DB: never run two pods at once.
|
||||
type: Recreate
|
||||
type: RollingUpdate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: sonarr
|
||||
@@ -18,99 +21,49 @@ spec:
|
||||
app: sonarr
|
||||
spec:
|
||||
securityContext:
|
||||
# LinuxServer images init as root via s6 then step down to PUID/PGID.
|
||||
# fsGroup makes the shared CephFS group-writable for that user;
|
||||
# Fork image has no USER (runs as root by default); pin it to a non-root
|
||||
# UID and group-write the shared RWX CephFS /config (MediaCover etc.).
|
||||
# OnRootMismatch avoids a recursive chown of the whole media tree.
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
fsGroup: 1000
|
||||
fsGroupChangePolicy: OnRootMismatch
|
||||
initContainers:
|
||||
# Enforce the Vault-sourced API key and the reverse-proxy URL base in
|
||||
# /config/config.xml before the app starts. Vault is source of truth
|
||||
# (override bootstrap): the key is minted in Vault, synced by VSO into the
|
||||
# sonarr-apikey Secret, and written here. UrlBase=/sonarr lets arrproxy
|
||||
# forward arrstack.unkin.net/sonarr/... with the prefix preserved (no 307).
|
||||
# Runs as root to fix ownership; touches only <ApiKey>, <UrlBase>, and
|
||||
# <AuthenticationMethod>=External + <AuthenticationRequired>. External makes
|
||||
# the *arr defer UI login to arrproxy/oauth2-proxy (same no-auth request
|
||||
# handler as None, but permits remote access without prompting).
|
||||
- name: apikey-init
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/library/busybox:1.37.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
env:
|
||||
- name: API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: sonarr-apikey
|
||||
key: apitoken
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
set -eu
|
||||
case "$API_KEY" in
|
||||
"" | *[!0-9a-fA-F]*)
|
||||
echo "config-init: API_KEY missing or not hex; refusing" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
URL_BASE=/sonarr
|
||||
AUTH_METHOD=External
|
||||
AUTH_REQUIRED=Enabled
|
||||
CFG=/config/config.xml
|
||||
if [ ! -f "$CFG" ]; then
|
||||
printf '<Config>\n <ApiKey>%s</ApiKey>\n <UrlBase>%s</UrlBase>\n <AuthenticationMethod>%s</AuthenticationMethod>\n <AuthenticationRequired>%s</AuthenticationRequired>\n</Config>\n' "$API_KEY" "$URL_BASE" "$AUTH_METHOD" "$AUTH_REQUIRED" > "$CFG"
|
||||
else
|
||||
if grep -q '<ApiKey>' "$CFG"; then
|
||||
sed -i "s|<ApiKey>[^<]*</ApiKey>|<ApiKey>${API_KEY}</ApiKey>|" "$CFG"
|
||||
else
|
||||
sed -i "s|<Config>|<Config>\n <ApiKey>${API_KEY}</ApiKey>|" "$CFG"
|
||||
fi
|
||||
if grep -q '<UrlBase>' "$CFG"; then
|
||||
sed -i "s|<UrlBase>[^<]*</UrlBase>|<UrlBase>${URL_BASE}</UrlBase>|" "$CFG"
|
||||
else
|
||||
sed -i "s|<Config>|<Config>\n <UrlBase>${URL_BASE}</UrlBase>|" "$CFG"
|
||||
fi
|
||||
if grep -q '<AuthenticationMethod>' "$CFG"; then
|
||||
sed -i "s|<AuthenticationMethod>[^<]*</AuthenticationMethod>|<AuthenticationMethod>${AUTH_METHOD}</AuthenticationMethod>|" "$CFG"
|
||||
else
|
||||
sed -i "s|<Config>|<Config>\n <AuthenticationMethod>${AUTH_METHOD}</AuthenticationMethod>|" "$CFG"
|
||||
fi
|
||||
if grep -q '<AuthenticationRequired>' "$CFG"; then
|
||||
sed -i "s|<AuthenticationRequired>[^<]*</AuthenticationRequired>|<AuthenticationRequired>${AUTH_REQUIRED}</AuthenticationRequired>|" "$CFG"
|
||||
else
|
||||
sed -i "s|<Config>|<Config>\n <AuthenticationRequired>${AUTH_REQUIRED}</AuthenticationRequired>|" "$CFG"
|
||||
fi
|
||||
fi
|
||||
chown 1000:1000 "$CFG"
|
||||
chmod 600 "$CFG"
|
||||
echo "config-init: <ApiKey>, <UrlBase>=${URL_BASE}, <AuthenticationMethod>=${AUTH_METHOD}, <AuthenticationRequired>=${AUTH_REQUIRED} enforced from Vault"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 32Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 64Mi
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /config
|
||||
containers:
|
||||
- name: sonarr
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/linuxserver/sonarr:4.0.19
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/sonarr:v5.0.0-unkin2
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /app/Sonarr
|
||||
args:
|
||||
- -nobrowser
|
||||
- -data=/config
|
||||
# Required: bypass the single-instance guard so multiple replicas
|
||||
# can share one /config. Cross-replica safety is the Postgres layer,
|
||||
# not a local lock file.
|
||||
- -nosingleinstancecheck
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8989
|
||||
protocol: TCP
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: sonarr-env
|
||||
env:
|
||||
- name: PUID
|
||||
value: "1000"
|
||||
- name: PGID
|
||||
value: "1000"
|
||||
- name: TZ
|
||||
value: Australia/Sydney
|
||||
- name: Sonarr__Postgres__User
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: sonarr-db
|
||||
key: username
|
||||
- name: Sonarr__Postgres__Password
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: sonarr-db
|
||||
key: password
|
||||
- name: Sonarr__Auth__ApiKey
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: sonarr-apikey
|
||||
key: apitoken
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /sonarr/ping
|
||||
|
||||
Reference in New Issue
Block a user