feat: add paperless-ngx with OIDC, CNPG db, cp-3 HDD media, MinIO backup
Fixes controlplane.tftpl's install.wipe:true (should be false, live CPs already run false) and syncs coredns Corefile back to what's actually deployed (drops an unrolled-out, stale Kong-era rewrite).
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
# Nightly: pg_dump the paperless DB + mirror the media PVC into the scoped
|
||||
# `paperless` MinIO bucket (see minio-provision-paperless-job.yaml). This is a
|
||||
# BACKUP target, not live storage - paperless-ngx has no native S3 backend, it
|
||||
# only ever reads/writes the local media PVC directly.
|
||||
#
|
||||
# Pinned to talos-cp-3, same as deployment.yaml: media is a ReadWriteOnce
|
||||
# Longhorn volume with a single replica physically on that node's disk -
|
||||
# mounting it read-only here from a different node would conflict with the
|
||||
# live webserver's attachment.
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: paperless-backup
|
||||
spec:
|
||||
schedule: "0 3 * * *" # 03:00 daily, low-traffic window
|
||||
jobTemplate:
|
||||
spec:
|
||||
backoffLimit: 2
|
||||
template:
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: talos-cp-3
|
||||
initContainers:
|
||||
- name: pg-dump
|
||||
image: postgres:16-alpine
|
||||
env:
|
||||
- name: PGHOST
|
||||
value: paperless-db-rw
|
||||
- name: PGDATABASE
|
||||
value: paperless
|
||||
- name: PGUSER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-db-app
|
||||
key: username
|
||||
- name: PGPASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-db-app
|
||||
key: password
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- pg_dump --format=custom --file=/backup/paperless-db.dump
|
||||
volumeMounts:
|
||||
- name: backup
|
||||
mountPath: /backup
|
||||
containers:
|
||||
- name: mc-mirror
|
||||
image: minio/mc:latest
|
||||
env:
|
||||
- name: ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-minio-creds
|
||||
key: ACCESS_KEY
|
||||
- name: SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-minio-creds
|
||||
key: SECRET_KEY
|
||||
- name: BUCKET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-minio-creds
|
||||
key: BUCKET
|
||||
- name: ENDPOINT
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-minio-creds
|
||||
key: ENDPOINT
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
mc alias set b "$ENDPOINT" "$ACCESS_KEY" "$SECRET_KEY"
|
||||
mc cp /backup/paperless-db.dump "b/$BUCKET/db/paperless-db-$(date +%Y%m%d).dump"
|
||||
mc mirror --overwrite /media "b/$BUCKET/media"
|
||||
echo "Backup done."
|
||||
volumeMounts:
|
||||
- name: backup
|
||||
mountPath: /backup
|
||||
- name: media
|
||||
mountPath: /media
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: backup
|
||||
emptyDir: {}
|
||||
- name: media
|
||||
persistentVolumeClaim:
|
||||
claimName: paperless-media
|
||||
readOnly: true
|
||||
@@ -0,0 +1,17 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: paperless-config
|
||||
data:
|
||||
PAPERLESS_URL: "https://paperless.riotpiao.com"
|
||||
PAPERLESS_TIME_ZONE: "America/Los_Angeles"
|
||||
PAPERLESS_OCR_LANGUAGE: "eng"
|
||||
PAPERLESS_DBHOST: "paperless-db-rw"
|
||||
PAPERLESS_DBNAME: "paperless"
|
||||
PAPERLESS_REDIS: "redis://paperless-redis:6379"
|
||||
# django-allauth generic OIDC provider. The client_id/secret/server_url
|
||||
# bundle itself lives in the paperless-oidc Secret
|
||||
# (SOCIALACCOUNT_PROVIDERS_JSON key, composed by authentik-provision.py) -
|
||||
# env vars can't be split across a ConfigMap + Secret for the same key, so
|
||||
# this whole value is sourced from the Secret in deployment.yaml instead.
|
||||
PAPERLESS_APPS: "allauth.socialaccount.providers.openid_connect"
|
||||
@@ -0,0 +1,86 @@
|
||||
# Single container runs webserver + consumer + scheduler (paperless-ngx's
|
||||
# stock entrypoint does this internally) - no need to split into separate
|
||||
# Deployments. replicas: 1 only: paperless-media is ReadWriteOnce, and the
|
||||
# consumer polling the media dir doesn't benefit from horizontal scaling here.
|
||||
#
|
||||
# Pinned to talos-cp-3: paperless-media's disk physically lives there. Longhorn
|
||||
# RWO volumes can only be attached from one node at a time, and the nightly
|
||||
# backup-cronjob.yaml also mounts this same PVC (read-only) to mirror it into
|
||||
# MinIO - pinning both to the same node avoids a cross-node attach conflict,
|
||||
# and keeps the 3.5Ti read/write path off the network entirely.
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: paperless
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate # ReadWriteOnce media PVC - avoid two pods fighting over it
|
||||
selector:
|
||||
matchLabels:
|
||||
app: paperless
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: paperless
|
||||
spec:
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: talos-cp-3
|
||||
containers:
|
||||
- name: paperless
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:2.13
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: paperless-config
|
||||
env:
|
||||
- name: PAPERLESS_DBUSER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-db-app
|
||||
key: username
|
||||
- name: PAPERLESS_DBPASS
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-db-app
|
||||
key: password
|
||||
- name: PAPERLESS_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-secrets
|
||||
key: PAPERLESS_SECRET_KEY
|
||||
- name: PAPERLESS_ADMIN_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-secrets
|
||||
key: PAPERLESS_ADMIN_USER
|
||||
- name: PAPERLESS_ADMIN_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-secrets
|
||||
key: PAPERLESS_ADMIN_PASSWORD
|
||||
- name: PAPERLESS_SOCIALACCOUNT_PROVIDERS
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-oidc
|
||||
key: SOCIALACCOUNT_PROVIDERS_JSON
|
||||
resources:
|
||||
requests: { cpu: "500m", memory: "1Gi" }
|
||||
limits: { cpu: "2", memory: "4Gi" }
|
||||
volumeMounts:
|
||||
- name: media
|
||||
mountPath: /usr/src/paperless/media
|
||||
- name: data
|
||||
mountPath: /usr/src/paperless/data
|
||||
- name: consume
|
||||
mountPath: /usr/src/paperless/consume
|
||||
volumes:
|
||||
- name: media
|
||||
persistentVolumeClaim:
|
||||
claimName: paperless-media
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: paperless-data
|
||||
- name: consume
|
||||
emptyDir: {}
|
||||
@@ -0,0 +1,24 @@
|
||||
# Direct nginx ingress to the paperless Service - not routed via the Go
|
||||
# api-gateway (api.riotpiao.com), which has no WebSocket upgrade support and
|
||||
# paperless-ngx keeps a long-lived /ws/ connection open for live task status.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: paperless
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "0" # large scanned PDF uploads
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: paperless.riotpiao.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: paperless
|
||||
port:
|
||||
number: 8000
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: paperless
|
||||
resources:
|
||||
- pvc.yaml
|
||||
- configmap.yaml
|
||||
- redis.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ingress.yaml
|
||||
- backup-cronjob.yaml
|
||||
# postgres: paperless-db CNPG Cluster, deployed by k8s/infra/databases (wave 2,
|
||||
# before this app at wave 8) - not duplicated here. Same for the paperless-oidc
|
||||
# and paperless-minio-creds Secrets, written by PostSync provisioning Jobs in
|
||||
# k8s/infra/iam and k8s/infra/minio respectively.
|
||||
@@ -0,0 +1,33 @@
|
||||
# Two volumes, deliberately separate storage classes:
|
||||
#
|
||||
# - media: the actual documents (originals + OCR'd archive PDFs + thumbnails).
|
||||
# Grows to multi-TB, lives on the cp-3 USB HDD, single replica (see
|
||||
# k8s/infra/longhorn/longhorn-paperless-storageclass.yaml). Sized 3500Gi to
|
||||
# leave headroom on the 4TB disk rather than claiming it to 100%.
|
||||
# - data: the SQLite classification model + search index. Small (low GB),
|
||||
# frequently rewritten, and disposable (rebuilds from the DB + media on
|
||||
# next consume) - stays on the default 3-replica pool instead of the
|
||||
# single-disk HDD.
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: paperless-media
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: longhorn-paperless-media
|
||||
resources:
|
||||
requests:
|
||||
storage: 3500Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: paperless-data
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: longhorn
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
@@ -0,0 +1,37 @@
|
||||
# Task queue broker + websocket channel layer for paperless-ngx. No PVC:
|
||||
# queued/scheduled task state is disposable - a lost queue on restart just
|
||||
# means re-triggering consumption, not data loss (documents themselves live
|
||||
# on paperless-media).
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: paperless-redis
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: paperless-redis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: paperless-redis
|
||||
spec:
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
resources:
|
||||
requests: { cpu: "50m", memory: "64Mi" }
|
||||
limits: { cpu: "250m", memory: "256Mi" }
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: paperless-redis
|
||||
spec:
|
||||
selector:
|
||||
app: paperless-redis
|
||||
ports:
|
||||
- port: 6379
|
||||
targetPort: 6379
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: paperless
|
||||
spec:
|
||||
selector:
|
||||
app: paperless
|
||||
ports:
|
||||
- port: 8000
|
||||
targetPort: 8000
|
||||
Reference in New Issue
Block a user