deployment/production/production-deployment.yml
Rama main · réplica de lectura del repositorio original.
# Janua Platform - Production Deployment Configuration
# Phase 3: Production Deployment & Validation
# Enterprise-grade Kubernetes deployment with zero-downtime patterns
apiVersion: v1
kind: Namespace
metadata:
name: janua-production
labels:
app: janua
environment: production
version: "1.0.0"
---
# ConfigMap for production configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: janua-config
namespace: janua-production
data:
ENVIRONMENT: "production"
DEBUG: "false"
ENABLE_DOCS: "false"
LOG_LEVEL: "INFO"
CORS_ORIGINS: "https://janua.dev,https://dashboard.janua.dev,https://docs.janua.dev"
TRUSTED_HOSTS: "janua.dev,*.janua.dev"
# Database configuration
DATABASE_POOL_SIZE: "20"
DATABASE_MAX_OVERFLOW: "30"
DATABASE_POOL_TIMEOUT: "30"
DATABASE_POOL_RECYCLE: "1800"
# Performance settings
CACHE_DEFAULT_TTL: "300"
PERFORMANCE_MONITORING: "true"
SLOW_QUERY_THRESHOLD_MS: "100"
# Enterprise features
ENABLE_ORGANIZATIONS: "true"
ENABLE_SCIM: "true"
ENABLE_WEBHOOKS: "true"
ENABLE_SSO: "true"
ENABLE_MFA: "true"
ENABLE_AUDIT_LOGS: "true"
---
# Secret for sensitive configuration
apiVersion: v1
kind: Secret
metadata:
name: janua-secrets
namespace: janua-production
type: Opaque
stringData:
DATABASE_URL: "postgresql://janua:REPLACE_WITH_ACTUAL_PASSWORD@postgres-service:5432/janua"
REDIS_URL: "redis://:REPLACE_WITH_ACTUAL_PASSWORD@redis-service:6379/0"
JWT_SECRET_KEY: "REPLACE_WITH_ACTUAL_JWT_SECRET"
SECRET_KEY: "REPLACE_WITH_ACTUAL_SECRET_KEY"
SENDGRID_API_KEY: "REPLACE_WITH_ACTUAL_SENDGRID_KEY"
SENTRY_DSN: "REPLACE_WITH_ACTUAL_SENTRY_DSN"
GRAFANA_PASSWORD: "REPLACE_WITH_ACTUAL_GRAFANA_PASSWORD"
# Email (Resend) - Centralized for all MADFAM apps
RESEND_API_KEY: "REPLACE_WITH_ACTUAL_RESEND_KEY"
# Internal API - Service-to-service communication for MADFAM apps
INTERNAL_API_KEY: "REPLACE_WITH_ACTUAL_INTERNAL_API_KEY"
---
# API Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: janua-api
namespace: janua-production
labels:
app: janua-api
component: backend
version: "1.0.0"
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # Zero-downtime deployment
selector:
matchLabels:
app: janua-api
template:
metadata:
labels:
app: janua-api
component: backend
version: "1.0.0"
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8000"
prometheus.io/path: "/metrics/performance"
spec:
# Security Context
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
# Init Container for Database Migrations
initContainers:
- name: database-migration
image: janua/api:latest
command: ["python", "-m", "alembic", "upgrade", "head"]
envFrom:
- configMapRef:
name: janua-config
- secretRef:
name: janua-secrets
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
containers:
- name: api
image: janua/api:latest
ports:
- containerPort: 8000
name: http
protocol: TCP
# Environment Configuration
envFrom:
- configMapRef:
name: janua-config
- secretRef:
name: janua-secrets
# Resource Limits and Requests
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 2000m
memory: 4Gi
# Health Checks
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
# Startup Probe (for slower startup)
startupProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 12 # 2 minutes max startup time
# Security Context
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# Volume Mounts (for read-only filesystem)
volumeMounts:
- name: tmp-volume
mountPath: /tmp
- name: cache-volume
mountPath: /app/.cache
# Volumes
volumes:
- name: tmp-volume
emptyDir: {}
- name: cache-volume
emptyDir: {}
# Node Selection and Anti-Affinity
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- janua-api
topologyKey: kubernetes.io/hostname
# Termination Grace Period
terminationGracePeriodSeconds: 60
---
# API Service
apiVersion: v1
kind: Service
metadata:
name: janua-api-service
namespace: janua-production
labels:
app: janua-api
component: backend
annotations: {}
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8000
protocol: TCP
name: http
selector:
app: janua-api
---
# Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: janua-api-hpa
namespace: janua-production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: janua-api
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
---
# Pod Disruption Budget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: janua-api-pdb
namespace: janua-production
spec:
minAvailable: 2
selector:
matchLabels:
app: janua-api
---
# Network Policy (Security)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: janua-api-netpol
namespace: janua-production
spec:
podSelector:
matchLabels:
app: janua-api
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: janua-ingress
- podSelector:
matchLabels:
app: janua-nginx
ports:
- protocol: TCP
port: 8000
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
- to: [] # Allow all outbound for external services (SendGrid, etc.)
ports:
- protocol: TCP
port: 443
- protocol: TCP
port: 80
---
# Ingress for API
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: janua-api-ingress
namespace: janua-production
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"
nginx.ingress.kubernetes.io/proxy-send-timeout: "30"
nginx.ingress.kubernetes.io/proxy-read-timeout: "30"
nginx.ingress.kubernetes.io/rate-limit: "60"
nginx.ingress.kubernetes.io/rate-limit-window: "1m"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Frame-Options: DENY";
more_set_headers "X-Content-Type-Options: nosniff";
more_set_headers "X-XSS-Protection: 1; mode=block";
more_set_headers "Referrer-Policy: strict-origin-when-cross-origin";
more_set_headers "Permissions-Policy: geolocation=(), microphone=(), camera=()";
spec:
tls:
- hosts:
- api.janua.dev
secretName: janua-api-tls
rules:
- host: api.janua.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: janua-api-service
port:
number: 80