feat(roswell): despliegue GitOps del corpus — bot+scheduler, backup pg_dump diario, postgres adoptado
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# Se aplica una vez imperativamente (kubectl apply -f); después ArgoCD
|
||||
# se autogestiona porque este fichero también vive en k8s-manifests/roswell/.
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: roswell
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://git.chemavx.xyz/chemavx/k8s-manifests
|
||||
targetRevision: HEAD
|
||||
path: roswell
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: roswell
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
@@ -0,0 +1,78 @@
|
||||
# Backup diario de la BD del corpus con retención 7 (PLAN §Fase 6).
|
||||
# pg_dump en formato custom (-Fc, comprimido) verificado con pg_restore --list.
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
annotations:
|
||||
# ArgoCD no debe podar este PVC: contiene los backups de la BD.
|
||||
argocd.argoproj.io/sync-options: Prune=false
|
||||
name: roswell-backups
|
||||
namespace: roswell
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: roswell-db-backup
|
||||
namespace: roswell
|
||||
labels:
|
||||
app: roswell
|
||||
spec:
|
||||
schedule: "30 3 * * *"
|
||||
timeZone: "Europe/Madrid"
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
backoffLimit: 2
|
||||
activeDeadlineSeconds: 900
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: roswell-db-backup
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: backup
|
||||
# Misma imagen que el postgres del corpus: pg_dump 16 garantizado
|
||||
# y ya está en la caché del nodo.
|
||||
image: pgvector/pgvector:pg16
|
||||
env:
|
||||
- name: PGPASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: roswell-pg-secret
|
||||
key: password
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
set -eu
|
||||
STAMP=$(date +%Y%m%d)
|
||||
DEST="/backups/roswell-${STAMP}.dump"
|
||||
echo "pg_dump roswell -> ${DEST}"
|
||||
pg_dump -h postgres.roswell.svc.cluster.local -U roswell \
|
||||
-d roswell -Fc -f "${DEST}"
|
||||
# Verificación: un dump corrupto no lista su TOC.
|
||||
pg_restore --list "${DEST}" > /dev/null
|
||||
echo "Dump verificado ($(du -h ${DEST} | cut -f1))"
|
||||
# Retención: conservar los 7 más recientes
|
||||
for f in $(ls -1t /backups/roswell-*.dump | tail -n +8); do
|
||||
echo "Podando backup antiguo ${f}"
|
||||
rm -f "${f}"
|
||||
done
|
||||
ls -lh /backups/
|
||||
volumeMounts:
|
||||
- name: backups
|
||||
mountPath: /backups
|
||||
volumes:
|
||||
- name: backups
|
||||
persistentVolumeClaim:
|
||||
claimName: roswell-backups
|
||||
@@ -0,0 +1,117 @@
|
||||
# Bot + scheduler en un pod (PLAN §Fase 6). El Secret roswell-secrets se crea
|
||||
# imperativamente (nunca en git):
|
||||
# kubectl create secret generic roswell-secrets -n roswell \
|
||||
# --from-literal=telegram-bot-token=... \
|
||||
# --from-literal=telegram-allowed-users=... \
|
||||
# --from-literal=anthropic-api-key=... \
|
||||
# --from-literal=database-url=postgresql://roswell:<pass>@postgres.roswell.svc.cluster.local:5432/roswell \
|
||||
# --from-literal=podcastindex-key=... \
|
||||
# --from-literal=podcastindex-secret=...
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
annotations:
|
||||
# ArgoCD no debe podar este PVC: guarda la caché del modelo Whisper (~3GB)
|
||||
# y los PDFs subidos por Telegram.
|
||||
argocd.argoproj.io/sync-options: Prune=false
|
||||
name: roswell-data
|
||||
namespace: roswell
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: roswell
|
||||
namespace: roswell
|
||||
labels:
|
||||
app: roswell
|
||||
spec:
|
||||
replicas: 1
|
||||
# Dos pods haciendo polling del mismo token de Telegram entran en conflicto:
|
||||
# Recreate garantiza que el viejo muere antes de arrancar el nuevo.
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: roswell
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: roswell
|
||||
spec:
|
||||
containers:
|
||||
- name: roswell
|
||||
# Tag placeholder: el workflow de Gitea Actions lo sustituye por el
|
||||
# sha8 real en cada build (ver .gitea/workflows/build.yml del repo).
|
||||
image: git.chemavx.xyz/chemavx/roswell-corpus:00000000
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: TELEGRAM_BOT_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: roswell-secrets
|
||||
key: telegram-bot-token
|
||||
- name: TELEGRAM_ALLOWED_USERS
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: roswell-secrets
|
||||
key: telegram-allowed-users
|
||||
- name: ANTHROPIC_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: roswell-secrets
|
||||
key: anthropic-api-key
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: roswell-secrets
|
||||
key: database-url
|
||||
- name: PODCASTINDEX_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: roswell-secrets
|
||||
key: podcastindex-key
|
||||
- name: PODCASTINDEX_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: roswell-secrets
|
||||
key: podcastindex-secret
|
||||
- name: OLLAMA_URL
|
||||
value: "http://ollama.ollama.svc.cluster.local:11434"
|
||||
- name: SEARXNG_URL
|
||||
value: "http://searxng-svc.researchowl.svc.cluster.local:8080"
|
||||
- name: WHISPER_MODEL
|
||||
value: "large-v3"
|
||||
- name: WHISPER_DEVICE
|
||||
value: "cpu"
|
||||
- name: DATA_DIR
|
||||
value: "/data"
|
||||
- name: FEED_CHECK_HOURS
|
||||
value: "24"
|
||||
# Caché de modelos Whisper en el PVC: la descarga (~3GB) sobrevive
|
||||
# a los redeploys.
|
||||
- name: HF_HOME
|
||||
value: "/data/hf-cache"
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
resources:
|
||||
requests:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
limits:
|
||||
# Whisper large-v3 en CPU ≈ 3-4 GB (PLAN §10). Sin límite de CPU:
|
||||
# la transcripción aprovecha los cores libres del nodo.
|
||||
memory: "4Gi"
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: roswell-data
|
||||
imagePullSecrets:
|
||||
- name: gitea-registry
|
||||
@@ -0,0 +1,27 @@
|
||||
apiVersion: secrets.infisical.com/v1beta1
|
||||
kind: InfisicalStaticSecret
|
||||
metadata:
|
||||
name: gitea-registry
|
||||
namespace: roswell
|
||||
spec:
|
||||
# Referencia la capa de auth COMPARTIDA en infisical-operator (cross-namespace).
|
||||
infisicalAuthRef:
|
||||
name: infisical-auth
|
||||
namespace: infisical-operator
|
||||
sources:
|
||||
- projectId: 17e98e9d-70f5-43d1-8382-7da818dfcdd0 # project "homelab"
|
||||
environmentSlug: prod
|
||||
secretPath: /registry
|
||||
syncOptions:
|
||||
refreshInterval: 60s
|
||||
targets:
|
||||
- kind: Secret
|
||||
name: gitea-registry
|
||||
namespace: roswell
|
||||
creationPolicy: Owner
|
||||
secretType: kubernetes.io/dockerconfigjson
|
||||
template:
|
||||
engineVersion: v1
|
||||
data:
|
||||
# Reconstruye el pull-secret docker desde los dos campos de /registry.
|
||||
.dockerconfigjson: '{"auths":{"git.chemavx.xyz":{"username":"{{ .GITEA_REGISTRY_USERNAME.Value }}","password":"{{ .GITEA_REGISTRY_TOKEN.Value }}","auth":"{{ printf "%s:%s" .GITEA_REGISTRY_USERNAME.Value .GITEA_REGISTRY_TOKEN.Value | encodeBase64 }}"}}}'
|
||||
@@ -0,0 +1,90 @@
|
||||
# Postgres 16 + pgvector para el corpus (namespace roswell).
|
||||
# El Secret roswell-pg-secret se crea imperativamente (PLAN Fase 6):
|
||||
# kubectl create secret generic roswell-pg-secret -n roswell \
|
||||
# --from-literal=password=<pass>
|
||||
# NodePort 30432 para acceso desde la LAN/nodos durante el desarrollo local.
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: roswell
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
annotations:
|
||||
# ArgoCD no debe podar nunca este PVC: contiene la BD del corpus.
|
||||
argocd.argoproj.io/sync-options: Prune=false
|
||||
name: roswell-pgdata
|
||||
namespace: roswell
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 20Gi
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: postgres
|
||||
namespace: roswell
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: roswell-postgres
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: roswell-postgres
|
||||
spec:
|
||||
containers:
|
||||
- name: postgres
|
||||
image: pgvector/pgvector:pg16
|
||||
env:
|
||||
- name: POSTGRES_DB
|
||||
value: roswell
|
||||
- name: POSTGRES_USER
|
||||
value: roswell
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: roswell-pg-secret
|
||||
key: password
|
||||
- name: PGDATA
|
||||
value: /var/lib/postgresql/data/pgdata
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
volumeMounts:
|
||||
- name: pgdata
|
||||
mountPath: /var/lib/postgresql/data
|
||||
resources:
|
||||
requests:
|
||||
memory: 256Mi
|
||||
cpu: 100m
|
||||
limits:
|
||||
memory: 1Gi
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["pg_isready", "-U", "roswell", "-d", "roswell"]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
volumes:
|
||||
- name: pgdata
|
||||
persistentVolumeClaim:
|
||||
claimName: roswell-pgdata
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: postgres
|
||||
namespace: roswell
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: roswell-postgres
|
||||
ports:
|
||||
- port: 5432
|
||||
targetPort: 5432
|
||||
nodePort: 30432
|
||||
Reference in New Issue
Block a user