Files
UmbraDatastore/k8s/base/03-web-deployment.yaml
umbra audio cce2ce55dc Initial commit: UmbraDatastore configuration and deployment scripts
- Database deployment script (scripts/deploy-database.sh)
- Kubernetes manifests for CNPG PostgreSQL cluster (k8s/base/)
- Documentation (docs/DEPLOYMENT_GUIDE.md, DATASTORE_SPEC.md, INTEGRATION_SPEC.md)
- README with architecture overview and operations guide
2026-10-31 19:47:27 -07:00

285 lines
6.9 KiB
YAML

# Copyright 2026 Umbra Tools (umbratools.com)
# SPDX-License-Identifier: Apache-2.0
---
# ServiceAccount for web
apiVersion: v1
kind: ServiceAccount
metadata:
name: umbratrans-web
namespace: umbratrans
labels:
app: umbratrans-web
---
# Role for web (minimal permissions)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: umbratrans-web
namespace: umbratrans
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: ["postgresql.cnpg.io"]
resources: ["clusters", "backups"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: umbratrans-web
namespace: umbratrans
subjects:
- kind: ServiceAccount
name: umbratrans-web
namespace: umbratrans
roleRef:
kind: Role
name: umbratrans-web
apiGroup: rbac.authorization.k8s.io
---
# ConfigMap for static files (served by web)
apiVersion: v1
kind: ConfigMap
metadata:
name: umbratrans-static
namespace: umbratrans
labels:
app: umbratrans-web
data:
# Static files are built into the image, this is for runtime config
nginx.conf: |
# Not used - FastAPI serves static files directly
---
# Secret for web configuration (non-sensitive)
apiVersion: v1
kind: Secret
metadata:
name: umbratrans-web-config
namespace: umbratrans
type: Opaque
stringData:
UMBRA_ENV: "production"
UMBRATRANS_REPOSITORY: "postgres"
UMBRATRANS_PRODUCTIONS_ROOT: "/mnt/productions"
UMBRATRANS_STATIC_ROOT: "/app/web"
UMBRATRANS_ALLOWED_HOSTS: "umbratrans.bdmurphy.me,localhost"
UMBRATRANS_VERIFICATION_BASE_URL: "https://umbratrans.bdmurphy.me"
UMBRATRANS_COOKIE_SECURE: "true"
UMBRATRANS_REQUIRE_CSRF: "true"
UMBRATRANS_MAILER: "smtp"
UMBRATRANS_SMTP_HOST: "smtp.example.com"
UMBRATRANS_SMTP_PORT: "587"
UMBRATRANS_SMTP_USERNAME: "umbra@example.com"
UMBRATRANS_SMTP_SENDER: "umbratrans@umbra.example.com"
UMBRATRANS_SMTP_STARTTLS: "true"
# Sensitive values in separate secret
# UMBRATRANS_DATABASE_DSN: from umbratrans-db-credentials
# UMBRATRANS_SMTP_PASSWORD: from umbratrans-smtp
# UMBRATRANS_TOKEN_PEPPER: from umbratrans-secrets
---
# Deployment for web API + UI
apiVersion: apps/v1
kind: Deployment
metadata:
name: umbratrans-web
namespace: umbratrans
labels:
app: umbratrans-web
component: web
spec:
replicas: 2
selector:
matchLabels:
app: umbratrans-web
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: umbratrans-web
component: web
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8442"
prometheus.io/path: "/api/metrics"
spec:
serviceAccountName: umbratrans-web
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
initContainers:
- name: migrate
image: umbratrans-web:latest
imagePullPolicy: IfNotPresent
command: ["python", "-m", "umbratrans_web.cli", "migrate"]
envFrom:
- secretRef:
name: umbratrans-web-config
- secretRef:
name: umbratrans-db-credentials
- secretRef:
name: umbratrans-smtp
- secretRef:
name: umbratrans-secrets
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
containers:
- name: web
image: umbratrans-web:latest
imagePullPolicy: IfNotPresent
command: ["python", "-m", "umbratrans_web", "serve"]
args:
- "--host"
- "0.0.0.0"
- "--port"
- "8442"
- "--repository"
- "postgres"
- "--allow-production"
ports:
- name: http
containerPort: 8442
protocol: TCP
envFrom:
- secretRef:
name: umbratrans-web-config
- secretRef:
name: umbratrans-db-credentials
- secretRef:
name: umbratrans-smtp
- secretRef:
name: umbratrans-secrets
volumeMounts:
- name: productions
mountPath: /mnt/productions
readOnly: true
- name: artifacts
mountPath: /mnt/artifacts
readOnly: false
- name: tmp
mountPath: /tmp
livenessProbe:
httpGet:
path: /api/health
port: 8442
initialDelaySeconds: 15
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/health
port: 8442
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
startupProbe:
httpGet:
path: /api/health
port: 8442
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 30
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "1000m"
volumes:
- name: productions
persistentVolumeClaim:
claimName: umbratrans-productions
- name: artifacts
persistentVolumeClaim:
claimName: umbratrans-artifacts
- name: tmp
emptyDir:
sizeLimit: 1Gi
---
# Service for web
apiVersion: v1
kind: Service
metadata:
name: umbratrans-web
namespace: umbratrans
labels:
app: umbratrans-web
spec:
selector:
app: umbratrans-web
ports:
- name: http
port: 80
targetPort: 8442
protocol: TCP
type: ClusterIP
---
# HorizontalPodAutoscaler for web
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: umbratrans-web
namespace: umbratrans
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: umbratrans-web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 50
periodSeconds: 60
- type: Pods
value: 2
periodSeconds: 60
selectPolicy: Max