Deployment Guide
Complete guide for deploying CODITECT DMS to production on Google Cloud Platform.
Architecture Overview
┌─────────────────┐
│ Cloud Load │
│ Balancer │
│ (HTTPS/SSL) │
└────────┬────────┘
│
┌────────▼────────┐
│ Cloud Armor │
│ (WAF/DDoS) │
└────────┬────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌────────▼────┐ ┌──────▼─────┐ ┌─────▼──────┐
│ GKE Cluster │ │ GKE │ │ GKE │
│ API Pods │ │ Worker │ │ Frontend │
│ (3-20) │ │ Pods │ │ (Nginx) │
└──────┬───────┘ └─────┬──────┘ └────────────┘
│ │
└────────┬───────┘
│
┌────────────┼────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ Cloud │ │ Memory- │ │ Cloud │
│ SQL │ │ store │ │ Storage │
│ (PG15) │ │ (Redis) │ │ (GCS) │
└─────────┘ └─────────┘ └─────────┘
Prerequisites
Required Tools
# Install required CLI tools
brew install google-cloud-sdk kubectl helm
# Authenticate with GCP
gcloud auth login
gcloud auth application-default login
# Set project
gcloud config set project coditect-prod
GCP Services
Enable required APIs:
gcloud services enable \
container.googleapis.com \
sqladmin.googleapis.com \
redis.googleapis.com \
secretmanager.googleapis.com \
cloudarmor.googleapis.com \
monitoring.googleapis.com \
logging.googleapis.com
Infrastructure Setup
1. Create GKE Cluster
# Create cluster
gcloud container clusters create coditect-prod-cluster \
--zone=us-central1-a \
--machine-type=n2-standard-4 \
--num-nodes=3 \
--min-nodes=2 \
--max-nodes=10 \
--enable-autoscaling \
--enable-network-policy \
--enable-ip-alias \
--enable-shielded-nodes \
--workload-pool=coditect-prod.svc.id.goog
# Get credentials
gcloud container clusters get-credentials coditect-prod-cluster \
--zone=us-central1-a
2. Create Cloud SQL Instance
# Create PostgreSQL instance
gcloud sql instances create coditect-dms-db \
--database-version=POSTGRES_15 \
--tier=db-custom-4-16384 \
--region=us-central1 \
--availability-type=regional \
--storage-size=100GB \
--storage-auto-increase \
--backup-start-time=02:00 \
--enable-point-in-time-recovery
# Create database
gcloud sql databases create dms \
--instance=coditect-dms-db
# Create user
gcloud sql users create dms_user \
--instance=coditect-dms-db \
--password=$(openssl rand -base64 32)
# Enable pgvector extension
gcloud sql connect coditect-dms-db --user=postgres
# SQL> CREATE EXTENSION IF NOT EXISTS vector;
3. Create Memorystore Redis
# Create Redis instance
gcloud redis instances create coditect-dms-cache \
--size=2 \
--region=us-central1 \
--redis-version=redis_7_0 \
--tier=standard
4. Create Cloud Storage Bucket
# Create bucket for document storage
gsutil mb -l us-central1 gs://coditect-dms-documents
# Set lifecycle policy
gsutil lifecycle set lifecycle.json gs://coditect-dms-documents
Secrets Management
Create Secrets in Secret Manager
# Database URL
echo -n "postgresql://dms_user:PASSWORD@/dms?host=/cloudsql/coditect-prod:us-central1:coditect-dms-db" | \
gcloud secrets create dms-database-url --data-file=-
# JWT Secret
openssl rand -base64 64 | \
gcloud secrets create dms-jwt-secret --data-file=-
# OpenAI API Key
echo -n "sk-..." | \
gcloud secrets create dms-openai-key --data-file=-
# Stripe Secret Key
echo -n "sk_live_..." | \
gcloud secrets create dms-stripe-secret --data-file=-
Grant Access to GKE
# Create Kubernetes service account
kubectl create namespace coditect-dms
kubectl create serviceaccount dms-api -n coditect-dms
# Bind to GCP service account
gcloud iam service-accounts create dms-api-sa
gcloud iam service-accounts add-iam-policy-binding \
dms-api-sa@coditect-prod.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:coditect-prod.svc.id.goog[coditect-dms/dms-api]"
# Grant secret access
gcloud secrets add-iam-policy-binding dms-database-url \
--role="roles/secretmanager.secretAccessor" \
--member="serviceAccount:dms-api-sa@coditect-prod.iam.gserviceaccount.com"
# Repeat for other secrets...
Kubernetes Deployment
1. Create Namespace and ConfigMap
# deploy/kubernetes/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: coditect-dms
labels:
name: coditect-dms
---
apiVersion: v1
kind: ConfigMap
metadata:
name: dms-config
namespace: coditect-dms
data:
APP_NAME: "CODITECT DMS"
APP_VERSION: "1.0.0"
ENVIRONMENT: "production"
LOG_LEVEL: "INFO"
CORS_ORIGINS: "https://dms.coditect.ai"
REDIS_URL: "redis://10.0.0.1:6379"
2. Deploy API
# deploy/kubernetes/api-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: coditect-dms-api
namespace: coditect-dms
spec:
replicas: 3
selector:
matchLabels:
app: dms-api
template:
metadata:
labels:
app: dms-api
spec:
serviceAccountName: dms-api
containers:
- name: api
image: gcr.io/coditect-prod/dms-api:1.0.0
ports:
- containerPort: 8000
envFrom:
- configMapRef:
name: dms-config
env:
- name: API_DATABASE_URL
valueFrom:
secretKeyRef:
name: dms-secrets
key: database-url
- name: API_JWT_SECRET
valueFrom:
secretKeyRef:
name: dms-secrets
key: jwt-secret
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi
livenessProbe:
httpGet:
path: /health/live
port: 8000
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: dms-api
namespace: coditect-dms
spec:
selector:
app: dms-api
ports:
- port: 80
targetPort: 8000
type: ClusterIP
3. Deploy HPA
# deploy/kubernetes/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: dms-api-hpa
namespace: coditect-dms
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: coditect-dms-api
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
4. Deploy Ingress
# deploy/kubernetes/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dms-ingress
namespace: coditect-dms
annotations:
kubernetes.io/ingress.class: "gce"
kubernetes.io/ingress.global-static-ip-name: "dms-api-ip"
networking.gke.io/managed-certificates: "dms-cert"
kubernetes.io/ingress.allow-http: "false"
spec:
rules:
- host: dms-api.coditect.ai
http:
paths:
- path: /*
pathType: ImplementationSpecific
backend:
service:
name: dms-api
port:
number: 80
---
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: dms-cert
namespace: coditect-dms
spec:
domains:
- dms-api.coditect.ai
5. Apply Manifests
# Apply all manifests
kubectl apply -f deploy/kubernetes/namespace.yaml
kubectl apply -f deploy/kubernetes/
Celery Workers
Deploy Workers
# deploy/kubernetes/workers.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dms-worker
namespace: coditect-dms
spec:
replicas: 3
selector:
matchLabels:
app: dms-worker
template:
metadata:
labels:
app: dms-worker
spec:
serviceAccountName: dms-api
containers:
- name: worker
image: gcr.io/coditect-prod/dms-api:1.0.0
command: ["celery", "-A", "src.backend.tasks", "worker", "-l", "info"]
envFrom:
- configMapRef:
name: dms-config
env:
- name: CELERY_BROKER_URL
value: "redis://10.0.0.1:6379/0"
- name: CELERY_RESULT_BACKEND
value: "redis://10.0.0.1:6379/0"
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 2000m
memory: 4Gi
Monitoring Setup
Install Prometheus Stack
# Add Helm repo
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# Install kube-prometheus-stack
helm install monitoring prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--values deploy/kubernetes/monitoring-values.yaml
Apply ServiceMonitor
kubectl apply -f deploy/kubernetes/monitoring.yaml
Access Grafana
# Get admin password
kubectl get secret -n monitoring monitoring-grafana \
-o jsonpath="{.data.admin-password}" | base64 --decode
# Port forward
kubectl port-forward -n monitoring svc/monitoring-grafana 3000:80
# Open http://localhost:3000
Database Migrations
Run Migrations
# Connect to Cloud SQL via proxy
cloud_sql_proxy -instances=coditect-prod:us-central1:coditect-dms-db=tcp:5432 &
# Run migrations
DATABASE_URL="postgresql://dms_user:PASSWORD@localhost:5432/dms" \
alembic upgrade head
Rollback Migrations
# Rollback one step
alembic downgrade -1
# Rollback to specific revision
alembic downgrade <revision_id>
CI/CD Pipeline
GitHub Actions Workflow
# .github/workflows/deploy.yaml
name: Deploy to Production
on:
push:
branches: [main]
paths:
- 'src/**'
- 'deploy/**'
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Auth to GCP
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}
- name: Setup gcloud
uses: google-github-actions/setup-gcloud@v2
- name: Configure Docker
run: gcloud auth configure-docker gcr.io
- name: Build and Push
run: |
docker build -t gcr.io/coditect-prod/dms-api:${{ github.sha }} .
docker push gcr.io/coditect-prod/dms-api:${{ github.sha }}
- name: Deploy to GKE
run: |
gcloud container clusters get-credentials coditect-prod-cluster \
--zone us-central1-a
kubectl set image deployment/coditect-dms-api \
api=gcr.io/coditect-prod/dms-api:${{ github.sha }} \
-n coditect-dms
kubectl rollout status deployment/coditect-dms-api \
-n coditect-dms --timeout=300s
Health Checks
Verify Deployment
# Check pods
kubectl get pods -n coditect-dms
# Check logs
kubectl logs -f deployment/coditect-dms-api -n coditect-dms
# Check health
curl https://dms-api.coditect.ai/health
# Check readiness
curl https://dms-api.coditect.ai/health/ready
Troubleshooting
# Describe pod
kubectl describe pod <pod-name> -n coditect-dms
# Check events
kubectl get events -n coditect-dms --sort-by='.lastTimestamp'
# Port forward for local testing
kubectl port-forward svc/dms-api 8000:80 -n coditect-dms
curl http://localhost:8000/health
Scaling
Manual Scaling
# Scale API pods
kubectl scale deployment coditect-dms-api --replicas=10 -n coditect-dms
# Scale workers
kubectl scale deployment dms-worker --replicas=5 -n coditect-dms
Adjust HPA
# Edit HPA
kubectl edit hpa dms-api-hpa -n coditect-dms
Security Hardening
Network Policies
# deploy/kubernetes/network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dms-api-policy
namespace: coditect-dms
spec:
podSelector:
matchLabels:
app: dms-api
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8000
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/8 # Allow internal
- ports:
- protocol: TCP
port: 5432 # PostgreSQL
- protocol: TCP
port: 6379 # Redis
- protocol: TCP
port: 443 # External HTTPS
Cloud Armor Policy
# Create security policy
gcloud compute security-policies create dms-security-policy \
--description="DMS API Security Policy"
# Add rate limiting rule
gcloud compute security-policies rules create 1000 \
--security-policy=dms-security-policy \
--expression="true" \
--action=rate-based-ban \
--rate-limit-threshold-count=1000 \
--rate-limit-threshold-interval-sec=60 \
--ban-duration-sec=600
# Add SQL injection protection
gcloud compute security-policies rules create 2000 \
--security-policy=dms-security-policy \
--expression="evaluatePreconfiguredExpr('sqli-stable')" \
--action=deny-403
Backup and Recovery
Database Backups
Automated backups are enabled with 7-day retention. For additional backups:
# Manual backup
gcloud sql backups create \
--instance=coditect-dms-db \
--description="Pre-deployment backup"
# List backups
gcloud sql backups list --instance=coditect-dms-db
Disaster Recovery
See disaster-recovery-runbook.md for complete DR procedures.
Support
- Documentation: https://docs.coditect.ai/deployment
- Infrastructure Issues: infra@az1.ai
- Emergency Contact: oncall@az1.ai (PagerDuty)