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:
395
deploy/README.md
Normal file
395
deploy/README.md
Normal file
@@ -0,0 +1,395 @@
|
||||
# Adopt-a-Street Deployment
|
||||
|
||||
This directory contains deployment configurations for the Adopt-a-Street application on Kubernetes (Raspberry Pi cluster).
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
deploy/
|
||||
├── k8s/ # Kubernetes manifests
|
||||
│ ├── namespace.yaml # Namespace definition
|
||||
│ ├── configmap.yaml # Environment configuration
|
||||
│ ├── secrets.yaml.example # Secret template (COPY TO secrets.yaml)
|
||||
│ ├── mongodb-statefulset.yaml # MongoDB StatefulSet with PVC
|
||||
│ ├── backend-deployment.yaml # Backend Deployment + Service
|
||||
│ ├── frontend-deployment.yaml # Frontend Deployment + Service
|
||||
│ └── ingress.yaml # Ingress for routing
|
||||
├── README.md # This file
|
||||
└── scripts/ # Deployment helper scripts
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Cluster Requirements
|
||||
- Kubernetes cluster with 3 nodes:
|
||||
- 2x Raspberry Pi 5 (8GB RAM) - ARM64
|
||||
- 1x Raspberry Pi 3B+ (1GB RAM) - ARMv7
|
||||
- kubectl configured to access your cluster
|
||||
- Container registry accessible from cluster
|
||||
- Ingress controller installed (Traefik or NGINX Ingress)
|
||||
- Persistent storage provisioner (local-path, NFS, or Longhorn)
|
||||
|
||||
### Local Requirements
|
||||
- Docker with buildx for multi-arch builds
|
||||
- kubectl CLI tool
|
||||
- Access to container registry (Docker Hub, GitHub Container Registry, or private registry)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Build Multi-Arch Docker Images
|
||||
|
||||
Build images for both ARM64 (Pi 5) and ARMv7 (Pi 3B+):
|
||||
|
||||
```bash
|
||||
# From project root
|
||||
cd /home/will/Code/adopt-a-street
|
||||
|
||||
# Create buildx builder (one-time setup)
|
||||
docker buildx create --use --name multiarch-builder
|
||||
|
||||
# Build and push backend
|
||||
docker buildx build --platform linux/arm64,linux/arm/v7 \
|
||||
-t your-registry/adopt-a-street-backend:latest \
|
||||
--push ./backend
|
||||
|
||||
# Build and push frontend
|
||||
docker buildx build --platform linux/arm64,linux/arm/v7 \
|
||||
-t your-registry/adopt-a-street-frontend:latest \
|
||||
--push ./frontend
|
||||
```
|
||||
|
||||
**Note:** Replace `your-registry` with your actual registry (e.g., `docker.io/username` or `ghcr.io/username`)
|
||||
|
||||
### 2. Configure Secrets
|
||||
|
||||
```bash
|
||||
# Copy secrets template
|
||||
cp deploy/k8s/secrets.yaml.example deploy/k8s/secrets.yaml
|
||||
|
||||
# Edit secrets with your actual values
|
||||
nano deploy/k8s/secrets.yaml
|
||||
|
||||
# IMPORTANT: Add secrets.yaml to .gitignore if not already there
|
||||
echo "deploy/k8s/secrets.yaml" >> .gitignore
|
||||
```
|
||||
|
||||
**Required Secrets:**
|
||||
- `JWT_SECRET` - Strong random string for JWT signing
|
||||
- `CLOUDINARY_CLOUD_NAME` - Your Cloudinary cloud name
|
||||
- `CLOUDINARY_API_KEY` - Your Cloudinary API key
|
||||
- `CLOUDINARY_API_SECRET` - Your Cloudinary API secret
|
||||
|
||||
### 3. Update Image References
|
||||
|
||||
Update the image references in deployment files:
|
||||
|
||||
```bash
|
||||
# Update backend image reference
|
||||
nano deploy/k8s/backend-deployment.yaml
|
||||
# Change: image: your-registry/adopt-a-street-backend:latest
|
||||
|
||||
# Update frontend image reference
|
||||
nano deploy/k8s/frontend-deployment.yaml
|
||||
# Change: image: your-registry/adopt-a-street-frontend:latest
|
||||
```
|
||||
|
||||
### 4. Update Domain Name
|
||||
|
||||
Update the ingress host:
|
||||
|
||||
```bash
|
||||
nano deploy/k8s/ingress.yaml
|
||||
# Change: host: adopt-a-street.local
|
||||
# To your actual domain or IP
|
||||
```
|
||||
|
||||
### 5. Deploy to Kubernetes
|
||||
|
||||
```bash
|
||||
# Create namespace
|
||||
kubectl apply -f deploy/k8s/namespace.yaml
|
||||
|
||||
# Create secrets (IMPORTANT: Make sure you've edited secrets.yaml!)
|
||||
kubectl apply -f deploy/k8s/secrets.yaml
|
||||
|
||||
# Create ConfigMap
|
||||
kubectl apply -f deploy/k8s/configmap.yaml
|
||||
|
||||
# Deploy MongoDB
|
||||
kubectl apply -f deploy/k8s/mongodb-statefulset.yaml
|
||||
|
||||
# Wait for MongoDB to be ready (this may take 1-2 minutes)
|
||||
kubectl wait --for=condition=ready pod -l app=mongodb -n adopt-a-street --timeout=120s
|
||||
|
||||
# Deploy backend
|
||||
kubectl apply -f deploy/k8s/backend-deployment.yaml
|
||||
|
||||
# Wait for backend to be ready
|
||||
kubectl wait --for=condition=ready pod -l app=backend -n adopt-a-street --timeout=120s
|
||||
|
||||
# Deploy frontend
|
||||
kubectl apply -f deploy/k8s/frontend-deployment.yaml
|
||||
|
||||
# Deploy ingress
|
||||
kubectl apply -f deploy/k8s/ingress.yaml
|
||||
|
||||
# Check deployment status
|
||||
kubectl get all -n adopt-a-street
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
### Check Pod Status
|
||||
|
||||
```bash
|
||||
# View all resources
|
||||
kubectl get all -n adopt-a-street
|
||||
|
||||
# Check pod status
|
||||
kubectl get pods -n adopt-a-street
|
||||
|
||||
# Expected output:
|
||||
# NAME READY STATUS RESTARTS AGE
|
||||
# adopt-a-street-backend-xxxxxxxxxx-xxxxx 1/1 Running 0 5m
|
||||
# adopt-a-street-backend-xxxxxxxxxx-xxxxx 1/1 Running 0 5m
|
||||
# adopt-a-street-frontend-xxxxxxxxx-xxxxx 1/1 Running 0 5m
|
||||
# adopt-a-street-frontend-xxxxxxxxx-xxxxx 1/1 Running 0 5m
|
||||
# adopt-a-street-mongodb-0 1/1 Running 0 10m
|
||||
```
|
||||
|
||||
### Check Logs
|
||||
|
||||
```bash
|
||||
# Backend logs
|
||||
kubectl logs -f deployment/adopt-a-street-backend -n adopt-a-street
|
||||
|
||||
# Frontend logs
|
||||
kubectl logs -f deployment/adopt-a-street-frontend -n adopt-a-street
|
||||
|
||||
# MongoDB logs
|
||||
kubectl logs -f adopt-a-street-mongodb-0 -n adopt-a-street
|
||||
```
|
||||
|
||||
### Check Services
|
||||
|
||||
```bash
|
||||
kubectl get svc -n adopt-a-street
|
||||
|
||||
# Expected output:
|
||||
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
# adopt-a-street-backend ClusterIP 10.43.x.x <none> 5000/TCP 5m
|
||||
# adopt-a-street-frontend ClusterIP 10.43.x.x <none> 80/TCP 5m
|
||||
# adopt-a-street-mongodb ClusterIP None <none> 27017/TCP 10m
|
||||
```
|
||||
|
||||
### Check Ingress
|
||||
|
||||
```bash
|
||||
kubectl get ingress -n adopt-a-street
|
||||
|
||||
# Get ingress details
|
||||
kubectl describe ingress adopt-a-street-ingress -n adopt-a-street
|
||||
```
|
||||
|
||||
### Access the Application
|
||||
|
||||
```bash
|
||||
# Port forward for testing (if ingress not working)
|
||||
kubectl port-forward svc/adopt-a-street-frontend 3000:80 -n adopt-a-street
|
||||
|
||||
# Then open http://localhost:3000 in your browser
|
||||
```
|
||||
|
||||
## Resource Allocation
|
||||
|
||||
The deployment is optimized for Raspberry Pi hardware:
|
||||
|
||||
### MongoDB (Pi 5 nodes only)
|
||||
- **Requests:** 512Mi RAM, 250m CPU
|
||||
- **Limits:** 2Gi RAM, 1000m CPU
|
||||
- **Storage:** 10Gi persistent volume
|
||||
|
||||
### Backend (prefers Pi 5 nodes)
|
||||
- **Requests:** 256Mi RAM, 100m CPU
|
||||
- **Limits:** 512Mi RAM, 500m CPU
|
||||
- **Replicas:** 2 pods
|
||||
|
||||
### Frontend (any node)
|
||||
- **Requests:** 64Mi RAM, 50m CPU
|
||||
- **Limits:** 128Mi RAM, 200m CPU
|
||||
- **Replicas:** 2 pods
|
||||
|
||||
### Total Cluster Requirements
|
||||
- **Minimum RAM:** ~3.5 GB (1.5GB MongoDB + 1GB backend + 200MB frontend + 800MB system)
|
||||
- **Recommended:** 2x Pi 5 (8GB each) handles this comfortably
|
||||
|
||||
## Scaling
|
||||
|
||||
### Scale Deployments
|
||||
|
||||
```bash
|
||||
# Scale backend
|
||||
kubectl scale deployment adopt-a-street-backend --replicas=3 -n adopt-a-street
|
||||
|
||||
# Scale frontend
|
||||
kubectl scale deployment adopt-a-street-frontend --replicas=3 -n adopt-a-street
|
||||
```
|
||||
|
||||
**Note:** MongoDB is a StatefulSet with 1 replica. Scaling MongoDB requires configuring replication.
|
||||
|
||||
## Updating
|
||||
|
||||
### Update Images
|
||||
|
||||
```bash
|
||||
# Build and push new version
|
||||
docker buildx build --platform linux/arm64,linux/arm/v7 \
|
||||
-t your-registry/adopt-a-street-backend:v2.0 \
|
||||
--push ./backend
|
||||
|
||||
# Update deployment
|
||||
kubectl set image deployment/adopt-a-street-backend \
|
||||
backend=your-registry/adopt-a-street-backend:v2.0 \
|
||||
-n adopt-a-street
|
||||
|
||||
# Check rollout status
|
||||
kubectl rollout status deployment/adopt-a-street-backend -n adopt-a-street
|
||||
```
|
||||
|
||||
### Rollback
|
||||
|
||||
```bash
|
||||
# Rollback to previous version
|
||||
kubectl rollout undo deployment/adopt-a-street-backend -n adopt-a-street
|
||||
|
||||
# Rollback to specific revision
|
||||
kubectl rollout undo deployment/adopt-a-street-backend --to-revision=2 -n adopt-a-street
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Resource Usage
|
||||
|
||||
```bash
|
||||
# Node resource usage
|
||||
kubectl top nodes
|
||||
|
||||
# Pod resource usage
|
||||
kubectl top pods -n adopt-a-street
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
```bash
|
||||
# View recent events
|
||||
kubectl get events -n adopt-a-street --sort-by='.lastTimestamp'
|
||||
```
|
||||
|
||||
### Describe Resources
|
||||
|
||||
```bash
|
||||
# Describe pod (useful for troubleshooting)
|
||||
kubectl describe pod <pod-name> -n adopt-a-street
|
||||
|
||||
# Describe deployment
|
||||
kubectl describe deployment adopt-a-street-backend -n adopt-a-street
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Pod Not Starting
|
||||
|
||||
```bash
|
||||
# Check pod events
|
||||
kubectl describe pod <pod-name> -n adopt-a-street
|
||||
|
||||
# Check logs
|
||||
kubectl logs <pod-name> -n adopt-a-street
|
||||
|
||||
# Check previous logs (if pod crashed)
|
||||
kubectl logs <pod-name> -n adopt-a-street --previous
|
||||
```
|
||||
|
||||
### Image Pull Errors
|
||||
|
||||
- Verify image exists in registry
|
||||
- Check image name and tag in deployment
|
||||
- Verify cluster can access registry
|
||||
- Check if imagePullSecrets are needed
|
||||
|
||||
### MongoDB Connection Issues
|
||||
|
||||
```bash
|
||||
# Shell into backend pod
|
||||
kubectl exec -it <backend-pod-name> -n adopt-a-street -- sh
|
||||
|
||||
# Test MongoDB connection
|
||||
wget -qO- http://adopt-a-street-mongodb:27017
|
||||
```
|
||||
|
||||
### Persistent Volume Issues
|
||||
|
||||
```bash
|
||||
# Check PVCs
|
||||
kubectl get pvc -n adopt-a-street
|
||||
|
||||
# Check PVs
|
||||
kubectl get pv
|
||||
|
||||
# Describe PVC
|
||||
kubectl describe pvc mongodb-data-adopt-a-street-mongodb-0 -n adopt-a-street
|
||||
```
|
||||
|
||||
## Cleanup
|
||||
|
||||
### Delete Everything
|
||||
|
||||
```bash
|
||||
# Delete all resources in namespace
|
||||
kubectl delete namespace adopt-a-street
|
||||
|
||||
# Or delete resources individually
|
||||
kubectl delete -f deploy/k8s/ingress.yaml
|
||||
kubectl delete -f deploy/k8s/frontend-deployment.yaml
|
||||
kubectl delete -f deploy/k8s/backend-deployment.yaml
|
||||
kubectl delete -f deploy/k8s/mongodb-statefulset.yaml
|
||||
kubectl delete -f deploy/k8s/configmap.yaml
|
||||
kubectl delete -f deploy/k8s/secrets.yaml
|
||||
kubectl delete -f deploy/k8s/namespace.yaml
|
||||
|
||||
# Note: This will also delete the persistent volume data!
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Never commit secrets.yaml** - Always use secrets.yaml.example
|
||||
2. **Use strong JWT_SECRET** - Generate with: `openssl rand -base64 32`
|
||||
3. **Enable TLS/HTTPS** - Uncomment TLS section in ingress.yaml and use cert-manager
|
||||
4. **Restrict ingress** - Use network policies to limit pod communication
|
||||
5. **Use image digests** - Pin images to specific SHA256 digests for production
|
||||
6. **Enable RBAC** - Create service accounts with minimal permissions
|
||||
7. **Scan images** - Use tools like Trivy to scan for vulnerabilities
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
1. **Use imagePullPolicy: IfNotPresent** - After initial deployment to save bandwidth
|
||||
2. **Implement HPA** - Horizontal Pod Autoscaler for dynamic scaling
|
||||
3. **Add Redis** - For caching to reduce MongoDB load
|
||||
4. **Use CDN** - For frontend static assets
|
||||
5. **Enable compression** - Nginx already configured with gzip
|
||||
6. **Monitor resources** - Use Prometheus + Grafana for metrics
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Kubernetes Documentation](https://kubernetes.io/docs/)
|
||||
- [Raspberry Pi Kubernetes Guide](https://github.com/alexellis/k8s-on-raspbian)
|
||||
- [Helm Charts](https://helm.sh/) - Consider migrating to Helm for easier management
|
||||
- [ArgoCD](https://argoproj.github.io/cd/) - GitOps continuous delivery for Kubernetes
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
1. Check pod logs: `kubectl logs <pod-name> -n adopt-a-street`
|
||||
2. Check events: `kubectl get events -n adopt-a-street`
|
||||
3. Describe resources: `kubectl describe <resource> -n adopt-a-street`
|
||||
4. Review application logs in the backend
|
||||
Reference in New Issue
Block a user