feat: add complete Kubernetes deployment infrastructure

Add production-ready deployment configuration for Raspberry Pi cluster with comprehensive documentation and automation scripts.

Kubernetes Manifests (deploy/k8s/):
- namespace.yaml - Dedicated namespace for the application
- configmap.yaml - Environment configuration (MongoDB URI, ports, URLs)
- secrets.yaml.example - Template for sensitive credentials (JWT, Cloudinary, Stripe)
- mongodb-statefulset.yaml - MongoDB with persistent storage, placed on Pi 5 nodes (ARM64)
- backend-deployment.yaml - Backend with 2 replicas, prefers Pi 5 nodes, health checks
- frontend-deployment.yaml - Frontend with 2 replicas, can run on any node, nginx-based
- ingress.yaml - Traefik/NGINX ingress for API, Socket.IO, and frontend routing

Docker Configuration:
- backend/Dockerfile - Multi-stage build for ARM64/ARMv7 with health checks
- backend/.dockerignore - Excludes tests, coverage, node_modules from build
- frontend/Dockerfile - Multi-stage build with nginx, optimized for ARM
- frontend/.dockerignore - Excludes dev files from production build
- frontend/nginx.conf - Production nginx config with gzip, caching, React Router support

Resource Optimization for Pi Cluster:
- MongoDB: 512Mi-2Gi RAM, 250m-1000m CPU (Pi 5 only, ARM64 affinity)
- Backend: 256Mi-512Mi RAM, 100m-500m CPU (prefers Pi 5, ARM64)
- Frontend: 64Mi-128Mi RAM, 50m-200m CPU (any node, lightweight)
- Total: ~3.5GB RAM minimum, perfect for 2x Pi 5 (8GB) + 1x Pi 3B+ (1GB)

Automation Scripts (deploy/scripts/):
- build.sh - Build multi-arch images (ARM64/ARMv7) and push to registry
- deploy.sh - Deploy all Kubernetes resources with health checks and status reporting
- Both scripts include error handling, color output, and comprehensive logging

Documentation (deploy/README.md):
- Complete deployment guide with prerequisites
- Step-by-step instructions for building and deploying
- Verification commands and troubleshooting guide
- Scaling, updating, and rollback procedures
- Resource monitoring and cleanup instructions
- Security best practices and performance optimization tips

Health Endpoints:
- Backend: GET /api/health (status, uptime, MongoDB connection)
- Frontend: GET /health (nginx health check)
- Used by Kubernetes liveness and readiness probes

Key Features:
- Multi-architecture support (ARM64 for Pi 5, ARMv7 for Pi 3B+)
- NodeAffinity places heavy workloads (MongoDB, backend) on Pi 5 nodes
- Persistent storage for MongoDB (10Gi PVC)
- Horizontal pod autoscaling ready
- Zero-downtime deployments with rolling updates
- Comprehensive health monitoring
- Production-grade nginx with security headers
- Ingress routing for API, WebSocket, and static assets

Security:
- Secrets management with Kubernetes Secrets
- secrets.yaml excluded from Git (.gitignore)
- Minimal container images (alpine-based)
- Health checks prevent unhealthy pods from serving traffic
- Security headers in nginx (X-Frame-Options, X-Content-Type-Options, etc.)

Usage:
1. Build images: ./deploy/scripts/build.sh
2. Configure secrets: cp deploy/k8s/secrets.yaml.example deploy/k8s/secrets.yaml
3. Deploy: ./deploy/scripts/deploy.sh
4. Monitor: kubectl get all -n adopt-a-street

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
William Valentin
2025-11-01 11:08:19 -07:00
parent 2df5a303ed
commit ae791ae8b1
17 changed files with 1125 additions and 0 deletions

95
deploy/scripts/build.sh Executable file
View File

@@ -0,0 +1,95 @@
#!/bin/bash
# Adopt-a-Street Multi-Arch Docker Build Script
# Builds images for ARM64 (Pi 5) and ARMv7 (Pi 3B+)
set -e # Exit on error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
REGISTRY="${DOCKER_REGISTRY:-your-registry}"
TAG="${DOCKER_TAG:-latest}"
echo -e "${GREEN}🐳 Adopt-a-Street Multi-Arch Docker Build${NC}"
echo "================================================"
echo "Registry: ${REGISTRY}"
echo "Tag: ${TAG}"
echo "Project Root: ${PROJECT_ROOT}"
echo ""
# Check if docker buildx is available
if ! docker buildx version &> /dev/null; then
echo -e "${RED}❌ Docker buildx not found. Please install Docker with buildx support.${NC}"
exit 1
fi
# Create buildx builder if it doesn't exist
if ! docker buildx inspect multiarch-builder &> /dev/null; then
echo "🔨 Creating buildx builder..."
docker buildx create --use --name multiarch-builder
echo -e "${GREEN}${NC} Builder created"
else
echo "🔨 Using existing buildx builder..."
docker buildx use multiarch-builder
fi
echo ""
# Prompt for registry if using default
if [ "${REGISTRY}" = "your-registry" ]; then
echo -e "${YELLOW}⚠️ Using default registry 'your-registry'${NC}"
echo -e "${YELLOW}Set DOCKER_REGISTRY environment variable to use a different registry:${NC}"
echo " export DOCKER_REGISTRY=docker.io/username"
echo " export DOCKER_REGISTRY=ghcr.io/username"
echo ""
read -p "Continue with 'your-registry'? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted."
exit 1
fi
fi
# Build backend
echo "🔧 Building backend image..."
echo " Platforms: linux/arm64, linux/arm/v7"
echo " Image: ${REGISTRY}/adopt-a-street-backend:${TAG}"
docker buildx build --platform linux/arm64,linux/arm/v7 \
-t "${REGISTRY}/adopt-a-street-backend:${TAG}" \
--push \
"${PROJECT_ROOT}/backend"
echo -e "${GREEN}${NC} Backend image built and pushed"
echo ""
# Build frontend
echo "🎨 Building frontend image..."
echo " Platforms: linux/arm64, linux/arm/v7"
echo " Image: ${REGISTRY}/adopt-a-street-frontend:${TAG}"
docker buildx build --platform linux/arm64,linux/arm/v7 \
-t "${REGISTRY}/adopt-a-street-frontend:${TAG}" \
--push \
"${PROJECT_ROOT}/frontend"
echo -e "${GREEN}${NC} Frontend image built and pushed"
echo ""
echo "================================================"
echo -e "${GREEN}✅ Build Complete!${NC}"
echo "================================================"
echo ""
echo "Images pushed:"
echo " 📦 ${REGISTRY}/adopt-a-street-backend:${TAG}"
echo " 📦 ${REGISTRY}/adopt-a-street-frontend:${TAG}"
echo ""
echo -e "${YELLOW}📝 Next Steps:${NC}"
echo "1. Update image references in deployment files:"
echo " sed -i 's|your-registry|${REGISTRY}|g' deploy/k8s/*.yaml"
echo ""
echo "2. Deploy to Kubernetes:"
echo " ./deploy/scripts/deploy.sh"
echo ""
echo -e "${GREEN}🎉 Happy deploying!${NC}"

127
deploy/scripts/deploy.sh Executable file
View File

@@ -0,0 +1,127 @@
#!/bin/bash
# Adopt-a-Street Kubernetes Deployment Script
# This script deploys the Adopt-a-Street application to a Kubernetes cluster
set -e # Exit on error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration
NAMESPACE="adopt-a-street"
DEPLOY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
K8S_DIR="${DEPLOY_DIR}/k8s"
echo -e "${GREEN}🚀 Adopt-a-Street Kubernetes Deployment${NC}"
echo "================================================"
echo ""
# Check if kubectl is installed
if ! command -v kubectl &> /dev/null; then
echo -e "${RED}❌ kubectl not found. Please install kubectl first.${NC}"
exit 1
fi
# Check if secrets.yaml exists
if [ ! -f "${K8S_DIR}/secrets.yaml" ]; then
echo -e "${RED}❌ secrets.yaml not found!${NC}"
echo -e "${YELLOW}Please copy secrets.yaml.example to secrets.yaml and fill in your secrets:${NC}"
echo " cp ${K8S_DIR}/secrets.yaml.example ${K8S_DIR}/secrets.yaml"
echo " nano ${K8S_DIR}/secrets.yaml"
exit 1
fi
echo -e "${GREEN}${NC} Prerequisites check passed"
echo ""
# Function to wait for pods
wait_for_pods() {
local label=$1
local timeout=${2:-120}
echo -e "${YELLOW}⏳ Waiting for pods with label ${label} to be ready...${NC}"
kubectl wait --for=condition=ready pod -l "${label}" -n "${NAMESPACE}" --timeout="${timeout}s" || {
echo -e "${RED}❌ Timeout waiting for pods${NC}"
kubectl get pods -n "${NAMESPACE}" -l "${label}"
return 1
}
echo -e "${GREEN}${NC} Pods ready"
}
# Create namespace
echo "📦 Creating namespace..."
kubectl apply -f "${K8S_DIR}/namespace.yaml"
echo -e "${GREEN}${NC} Namespace created"
echo ""
# Apply secrets
echo "🔐 Applying secrets..."
kubectl apply -f "${K8S_DIR}/secrets.yaml"
echo -e "${GREEN}${NC} Secrets applied"
echo ""
# Apply configmap
echo "⚙️ Applying ConfigMap..."
kubectl apply -f "${K8S_DIR}/configmap.yaml"
echo -e "${GREEN}${NC} ConfigMap applied"
echo ""
# Deploy MongoDB
echo "🗄️ Deploying MongoDB..."
kubectl apply -f "${K8S_DIR}/mongodb-statefulset.yaml"
wait_for_pods "app=mongodb" 180
echo -e "${GREEN}${NC} MongoDB deployed"
echo ""
# Deploy backend
echo "🔧 Deploying backend..."
kubectl apply -f "${K8S_DIR}/backend-deployment.yaml"
wait_for_pods "app=backend" 120
echo -e "${GREEN}${NC} Backend deployed"
echo ""
# Deploy frontend
echo "🎨 Deploying frontend..."
kubectl apply -f "${K8S_DIR}/frontend-deployment.yaml"
wait_for_pods "app=frontend" 120
echo -e "${GREEN}${NC} Frontend deployed"
echo ""
# Deploy ingress
echo "🌐 Deploying ingress..."
kubectl apply -f "${K8S_DIR}/ingress.yaml"
echo -e "${GREEN}${NC} Ingress deployed"
echo ""
# Show deployment status
echo "================================================"
echo -e "${GREEN}✅ Deployment Complete!${NC}"
echo "================================================"
echo ""
echo "📊 Current Status:"
kubectl get all -n "${NAMESPACE}"
echo ""
echo "🌐 Ingress:"
kubectl get ingress -n "${NAMESPACE}"
echo ""
echo -e "${YELLOW}📝 Next Steps:${NC}"
echo "1. Check pod logs:"
echo " kubectl logs -f deployment/adopt-a-street-backend -n ${NAMESPACE}"
echo ""
echo "2. Access the application through your ingress URL"
echo ""
echo "3. Or port-forward for testing:"
echo " kubectl port-forward svc/adopt-a-street-frontend 3000:80 -n ${NAMESPACE}"
echo " Then open http://localhost:3000"
echo ""
echo "4. Monitor resources:"
echo " kubectl top pods -n ${NAMESPACE}"
echo ""
echo -e "${GREEN}🎉 Happy deploying!${NC}"