- Configure workspace Git repository with comprehensive .gitignore - Add Odoo as submodule for ERP reference code - Include documentation: SETUP.md, GIT-STRUCTURE.md - Add gitignore templates for projects (backend, frontend, database) - Structure supports independent repos per project/subproject level Workspace includes: - core/ - Reusable patterns, modules, orchestration system - projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.) - knowledge-base/ - Reference code and patterns (includes Odoo submodule) - devtools/ - Development tools and templates - customers/ - Client implementations template 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
302 lines
8.1 KiB
YAML
302 lines
8.1 KiB
YAML
# =============================================================================
|
|
# GAMILIT Backend - Kubernetes Deployment
|
|
# =============================================================================
|
|
# Purpose: Deploys GAMILIT backend API with horizontal pod autoscaling
|
|
# Namespace: gamilit-production
|
|
# Replicas: 3 (min) - 10 (max with HPA)
|
|
# Resources: CPU 250m-500m, Memory 512Mi-1Gi per pod
|
|
# =============================================================================
|
|
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: gamilit-backend
|
|
namespace: gamilit-production
|
|
labels:
|
|
app: gamilit
|
|
component: backend
|
|
tier: api
|
|
version: v1.0.0
|
|
annotations:
|
|
deployment.kubernetes.io/revision: "1"
|
|
description: "GAMILIT Platform Backend API - Node.js + Express + TypeScript"
|
|
spec:
|
|
replicas: 3
|
|
revisionHistoryLimit: 10
|
|
|
|
strategy:
|
|
type: RollingUpdate
|
|
rollingUpdate:
|
|
maxSurge: 1 # Maximum number of pods that can be created above desired replicas
|
|
maxUnavailable: 0 # Ensure zero-downtime during rolling updates
|
|
|
|
selector:
|
|
matchLabels:
|
|
app: gamilit
|
|
component: backend
|
|
tier: api
|
|
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: gamilit
|
|
component: backend
|
|
tier: api
|
|
version: v1.0.0
|
|
annotations:
|
|
prometheus.io/scrape: "true"
|
|
prometheus.io/port: "3006"
|
|
prometheus.io/path: "/metrics"
|
|
|
|
spec:
|
|
# Security context for pod
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
runAsUser: 1001
|
|
fsGroup: 1001
|
|
seccompProfile:
|
|
type: RuntimeDefault
|
|
|
|
# Service account (for RBAC)
|
|
serviceAccountName: gamilit-backend
|
|
|
|
# Init containers (run before main container)
|
|
initContainers:
|
|
# Check database connectivity before starting
|
|
- name: wait-for-database
|
|
image: postgres:16-alpine
|
|
command:
|
|
- sh
|
|
- -c
|
|
- |
|
|
echo "Waiting for PostgreSQL to be ready..."
|
|
until pg_isready -h gamilit-postgres -p 5432 -U gamilit_user; do
|
|
echo "PostgreSQL is unavailable - sleeping"
|
|
sleep 2
|
|
done
|
|
echo "PostgreSQL is ready!"
|
|
env:
|
|
- name: PGPASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: gamilit-db-secret
|
|
key: password
|
|
|
|
# Main application containers
|
|
containers:
|
|
- name: backend
|
|
image: ghcr.io/gamilit/backend:1.0.0 # Replace with your registry
|
|
imagePullPolicy: Always
|
|
|
|
# Security context for container
|
|
securityContext:
|
|
allowPrivilegeEscalation: false
|
|
readOnlyRootFilesystem: true
|
|
runAsNonRoot: true
|
|
runAsUser: 1001
|
|
capabilities:
|
|
drop:
|
|
- ALL
|
|
|
|
ports:
|
|
- name: http
|
|
containerPort: 3006
|
|
protocol: TCP
|
|
|
|
# Environment variables
|
|
env:
|
|
# General
|
|
- name: NODE_ENV
|
|
value: "production"
|
|
- name: PORT
|
|
value: "3006"
|
|
- name: LOG_LEVEL
|
|
value: "info"
|
|
|
|
# Database - Connection from ConfigMap
|
|
- name: DB_HOST
|
|
valueFrom:
|
|
configMapKeyRef:
|
|
name: gamilit-backend-config
|
|
key: db_host
|
|
- name: DB_PORT
|
|
valueFrom:
|
|
configMapKeyRef:
|
|
name: gamilit-backend-config
|
|
key: db_port
|
|
- name: DB_NAME
|
|
valueFrom:
|
|
configMapKeyRef:
|
|
name: gamilit-backend-config
|
|
key: db_name
|
|
- name: DB_USER
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: gamilit-db-secret
|
|
key: username
|
|
- name: DB_PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: gamilit-db-secret
|
|
key: password
|
|
- name: DB_POOL_MIN
|
|
value: "10"
|
|
- name: DB_POOL_MAX
|
|
value: "100"
|
|
- name: DB_SSL
|
|
value: "true"
|
|
|
|
# JWT Secrets
|
|
- name: JWT_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: gamilit-jwt-secret
|
|
key: jwt_secret
|
|
- name: JWT_EXPIRES_IN
|
|
value: "7d"
|
|
- name: JWT_REFRESH_EXPIRES_IN
|
|
value: "30d"
|
|
|
|
# CORS
|
|
- name: CORS_ORIGIN
|
|
valueFrom:
|
|
configMapKeyRef:
|
|
name: gamilit-backend-config
|
|
key: cors_origin
|
|
|
|
# Redis (optional)
|
|
- name: REDIS_URL
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: gamilit-redis-secret
|
|
key: redis_url
|
|
optional: true
|
|
|
|
# Resource limits and requests
|
|
resources:
|
|
requests:
|
|
cpu: 250m
|
|
memory: 512Mi
|
|
limits:
|
|
cpu: 500m
|
|
memory: 1Gi
|
|
|
|
# Liveness probe - restart if unhealthy
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /api/health
|
|
port: http
|
|
scheme: HTTP
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 10
|
|
timeoutSeconds: 5
|
|
successThreshold: 1
|
|
failureThreshold: 3
|
|
|
|
# Readiness probe - remove from service if not ready
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /api/health
|
|
port: http
|
|
scheme: HTTP
|
|
initialDelaySeconds: 15
|
|
periodSeconds: 5
|
|
timeoutSeconds: 3
|
|
successThreshold: 1
|
|
failureThreshold: 2
|
|
|
|
# Startup probe - for slow-starting containers
|
|
startupProbe:
|
|
httpGet:
|
|
path: /api/health
|
|
port: http
|
|
initialDelaySeconds: 0
|
|
periodSeconds: 5
|
|
timeoutSeconds: 3
|
|
failureThreshold: 30 # 30 * 5s = 150s max startup time
|
|
|
|
# Volume mounts
|
|
volumeMounts:
|
|
- name: tmp
|
|
mountPath: /tmp
|
|
- name: logs
|
|
mountPath: /app/logs
|
|
|
|
# Volumes
|
|
volumes:
|
|
- name: tmp
|
|
emptyDir: {}
|
|
- name: logs
|
|
emptyDir: {}
|
|
|
|
# Node affinity (prefer different nodes for HA)
|
|
affinity:
|
|
podAntiAffinity:
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
|
- weight: 100
|
|
podAffinityTerm:
|
|
labelSelector:
|
|
matchExpressions:
|
|
- key: component
|
|
operator: In
|
|
values:
|
|
- backend
|
|
topologyKey: kubernetes.io/hostname
|
|
|
|
# Tolerations (if using tainted nodes)
|
|
tolerations:
|
|
- key: "app"
|
|
operator: "Equal"
|
|
value: "gamilit"
|
|
effect: "NoSchedule"
|
|
|
|
# DNS policy
|
|
dnsPolicy: ClusterFirst
|
|
|
|
# Restart policy
|
|
restartPolicy: Always
|
|
|
|
# Termination grace period
|
|
terminationGracePeriodSeconds: 30
|
|
|
|
---
|
|
# =============================================================================
|
|
# GAMILIT Backend - Service Account
|
|
# =============================================================================
|
|
|
|
apiVersion: v1
|
|
kind: ServiceAccount
|
|
metadata:
|
|
name: gamilit-backend
|
|
namespace: gamilit-production
|
|
labels:
|
|
app: gamilit
|
|
component: backend
|
|
|
|
---
|
|
# =============================================================================
|
|
# GAMILIT Backend - ConfigMap
|
|
# =============================================================================
|
|
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: gamilit-backend-config
|
|
namespace: gamilit-production
|
|
labels:
|
|
app: gamilit
|
|
component: backend
|
|
data:
|
|
# Database
|
|
db_host: "gamilit-postgres.gamilit-production.svc.cluster.local"
|
|
db_port: "5432"
|
|
db_name: "gamilit_platform"
|
|
|
|
# CORS
|
|
cors_origin: "https://gamilit.com,https://www.gamilit.com"
|
|
|
|
# Application
|
|
node_env: "production"
|
|
port: "3006"
|
|
log_level: "info"
|