# Kubernetes Configuration Review and Fixes ## Date: December 5, 2025 ## Summary Comprehensive review and fixes applied to all Kubernetes deployment configurations in `/deploy/k8s/` directory to address namespace configuration, missing resources, environment variables, and other configuration issues. --- ## Issues Fixed ### 1. **HIGH PRIORITY - Namespace Configuration** #### Issue - No `namespace.yaml` file existed - No namespace specified in any resource metadata - Documentation described manifests as "namespace-agnostic" which was error-prone #### Resolution - ✅ **Created** `namespace.yaml` to create `adopt-a-street` namespace - ✅ **Added** `namespace: adopt-a-street` to all resource metadata in: - `backend-deployment.yaml` (Service and Deployment) - `frontend-deployment.yaml` (Service and Deployment) - `couchdb-statefulset.yaml` (Service and StatefulSet) - `configmap.yaml` - `secrets.yaml.example` - `image-pull-secret.yaml` - `ingress.yaml` --- ### 2. **MEDIUM PRIORITY - Duplicate Registry Secret Files** #### Issue - Two files creating the same secret `regcred`: - `image-pull-secret.yaml` (template with placeholders) - `registry-secret.yaml` (actual credentials - security risk!) #### Resolution - ✅ **Deleted** `registry-secret.yaml` (contained actual credentials) - ✅ **Kept** `image-pull-secret.yaml` as template - ✅ **Updated** documentation to guide users on creating the secret properly --- ### 3. **MEDIUM PRIORITY - CouchDB NODENAME Configuration** #### Issue - `couchdb-statefulset.yaml` line 71 had incorrect NODENAME format: ```yaml value: couchdb@0.adopt-a-street-couchdb # INCORRECT ``` - Should follow StatefulSet pod naming: `-.` #### Resolution - ✅ **Fixed** NODENAME to proper format: ```yaml value: couchdb@adopt-a-street-couchdb-0.adopt-a-street-couchdb ``` --- ### 4. **MEDIUM PRIORITY - Missing Environment Variables** #### Issue Missing environment variables required by `backend/.env.example`: - CouchDB connection pool settings - Stripe configuration (both secret and publishable keys) - OpenAI API configuration #### Resolution - ✅ **Added to `configmap.yaml`** (non-sensitive values): - `COUCHDB_MAX_CONNECTIONS: "10"` - `COUCHDB_REQUEST_TIMEOUT: "30000"` - `STRIPE_PUBLISHABLE_KEY: "your-stripe-publishable-key"` - `OPENAI_MODEL: "gpt-3.5-turbo"` - ✅ **Added to `secrets.yaml.example`** (sensitive values): - `STRIPE_SECRET_KEY: "your-stripe-secret-key"` - `OPENAI_API_KEY: "your-openai-api-key"` --- ### 5. **LOW PRIORITY - Duplicate Cloudinary Variables** #### Issue - Cloudinary variables duplicated in both `configmap.yaml` and `secrets.yaml.example` - `CLOUDINARY_CLOUD_NAME` and `CLOUDINARY_API_KEY` in both locations #### Resolution - ✅ **Removed** `CLOUDINARY_CLOUD_NAME` from `secrets.yaml.example` (kept in ConfigMap) - ✅ **Removed** `CLOUDINARY_API_KEY` comment from ConfigMap - ✅ **Organized** properly: - **ConfigMap**: `CLOUDINARY_CLOUD_NAME` (non-sensitive) - **Secrets**: `CLOUDINARY_API_KEY`, `CLOUDINARY_API_SECRET` (sensitive) --- ### 6. **LOW PRIORITY - Unused CouchDB ConfigMap** #### Issue - `couchdb-configmap.yaml` defined a ConfigMap but it was never mounted - CouchDB configuration generated inline via shell script in StatefulSet #### Resolution - ✅ **Deleted** `couchdb-configmap.yaml` (unused file) - Configuration approach remains inline in `couchdb-statefulset.yaml` (lines 102-124) --- ### 7. **MEDIUM PRIORITY - Documentation Updates** #### Issue - `DEPLOYMENT_GUIDE.md` described namespace-agnostic approach - Instructions required manual namespace specification with `-n` flag - No clear guidance on default namespace #### Resolution - ✅ **Updated** `DEPLOYMENT_GUIDE.md` with: - Clear explanation that `adopt-a-street` is the default namespace - Step-by-step deployment process including namespace creation - Updated all example commands to use default namespace - Comprehensive environment variables documentation - Multi-environment deployment guidance - Updated troubleshooting commands --- ## Files Modified | File | Changes | |------|---------| | `namespace.yaml` | **CREATED** - Defines `adopt-a-street` namespace | | `backend-deployment.yaml` | Added namespace to Service and Deployment metadata | | `frontend-deployment.yaml` | Added namespace to Service and Deployment metadata | | `couchdb-statefulset.yaml` | Added namespace to Service and StatefulSet metadata; Fixed NODENAME | | `configmap.yaml` | Added namespace; Added missing env vars; Removed duplicate Cloudinary vars | | `secrets.yaml.example` | Added namespace; Added missing env vars; Removed duplicate Cloudinary vars; Updated comments | | `image-pull-secret.yaml` | Added namespace | | `ingress.yaml` | Added namespace | | `DEPLOYMENT_GUIDE.md` | Complete rewrite with namespace-aware instructions | | `registry-secret.yaml` | **DELETED** - Duplicate file with security risk | | `couchdb-configmap.yaml` | **DELETED** - Unused file | --- ## Configuration Summary ### Namespace Structure All resources now deploy to the `adopt-a-street` namespace by default. Alternative namespaces can still be used by overriding at deploy time. ### ConfigMap Variables (`configmap.yaml`) ```yaml # CouchDB COUCHDB_URL: "http://adopt-a-street-couchdb:5984" COUCHDB_DB_NAME: "adopt-a-street" COUCHDB_MAX_CONNECTIONS: "10" COUCHDB_REQUEST_TIMEOUT: "30000" # Application PORT: "5000" NODE_ENV: "production" FRONTEND_URL: "http://adopt-a-street.local" # Integrations (non-sensitive) CLOUDINARY_CLOUD_NAME: "your-cloudinary-cloud-name" STRIPE_PUBLISHABLE_KEY: "your-stripe-publishable-key" OPENAI_MODEL: "gpt-3.5-turbo" ``` ### Secret Variables (`secrets.yaml.example`) ```yaml # Authentication JWT_SECRET: "your-jwt-secret" # CouchDB COUCHDB_USER: "admin" COUCHDB_PASSWORD: "admin" COUCHDB_SECRET: "couchdb-secret" # Integrations (sensitive) CLOUDINARY_API_KEY: "your-api-key" CLOUDINARY_API_SECRET: "your-api-secret" STRIPE_SECRET_KEY: "your-stripe-secret" OPENAI_API_KEY: "your-openai-key" ``` --- ## Deployment Process ### Quick Start (Recommended) ```bash # 1. Create namespace kubectl apply -f deploy/k8s/namespace.yaml # 2. Create secrets file from example cp deploy/k8s/secrets.yaml.example deploy/k8s/secrets.yaml # Edit secrets.yaml with actual values # 3. Create image pull secret kubectl create secret docker-registry regcred \ --docker-server=gitea-gitea-http.taildb3494.ts.net \ --docker-username=will \ --docker-password=YOUR_PASSWORD \ --namespace=adopt-a-street # 4. Apply all configurations kubectl apply -f deploy/k8s/ # 5. Verify deployment kubectl get all -n adopt-a-street ``` --- ## Verification ### Health Checks All health check endpoints verified: - ✅ Backend: `/api/health` exists in `backend/server.js:150` - ✅ Frontend: `/health` exists in `frontend/nginx.conf:14` - ✅ CouchDB: `/_up` (standard CouchDB endpoint) ### Service Discovery All service references verified: - ✅ ConfigMap references `adopt-a-street-couchdb:5984` - ✅ Ingress routes to `adopt-a-street-backend:5000` and `adopt-a-street-frontend:80` - ✅ Backend references ConfigMap `adopt-a-street-config` and Secret `adopt-a-street-secrets` --- ## Testing Checklist Before deploying to production: - [ ] Update `secrets.yaml` with actual secure values - [ ] Generate secure passwords using `openssl rand -base64 32` - [ ] Create image pull secret with actual Gitea credentials - [ ] Update `configmap.yaml` with actual Cloudinary cloud name - [ ] Update `ingress.yaml` with actual domain name - [ ] Verify storage class for CouchDB persistent volumes - [ ] Test deployment in development namespace first - [ ] Verify all pods reach Ready state - [ ] Test health endpoints - [ ] Verify CouchDB persistence after pod restart - [ ] Test ingress routing --- ## Security Notes 1. **secrets.yaml** is in `.gitignore` - never commit to version control 2. All production passwords should be generated with `openssl rand -base64 32` 3. Image pull secrets contain credentials - handle securely 4. Default CouchDB credentials are placeholders - MUST be changed for production 5. Removed `registry-secret.yaml` which contained actual credentials --- ## Architecture Notes ### Resource Placement - **CouchDB**: Required on ARM64 nodes (Pi 5) - uses `requiredDuringSchedulingIgnoredDuringExecution` - **Backend**: Preferred on ARM64 nodes (Pi 5) - uses `preferredDuringSchedulingIgnoredDuringExecution` - **Frontend**: No node affinity (lightweight, can run anywhere) ### Storage - CouchDB uses StatefulSet with `volumeClaimTemplates` - 10Gi persistent storage per CouchDB pod - Storage class can be specified in `couchdb-statefulset.yaml:135` --- ## Next Steps (Optional) Consider adding these resources for production: 1. **NetworkPolicy** - Restrict pod-to-pod communication 2. **HorizontalPodAutoscaler** - Auto-scale based on metrics 3. **PodDisruptionBudget** - Ensure availability during updates 4. **ServiceAccount** - Dedicated service accounts per component 5. **ResourceQuota** - Limit namespace resource usage 6. **LimitRange** - Default resource limits for pods --- ## Rollback Plan If issues occur after deployment: ```bash # Delete all resources kubectl delete -f deploy/k8s/ # Or delete namespace (removes everything) kubectl delete namespace adopt-a-street # Revert to previous configuration git checkout HEAD~1 deploy/k8s/ kubectl apply -f deploy/k8s/ ``` --- ## References - Kubernetes Documentation: https://kubernetes.io/docs/ - CouchDB Docker: https://hub.docker.com/_/couchdb - StatefulSet Best Practices: https://kubernetes.io/docs/tutorials/stateful-application/ - Raspberry Pi Kubernetes: https://ubuntu.com/tutorials/how-to-kubernetes-cluster-on-raspberry-pi --- **Review Completed By**: AI Assistant **Review Date**: December 5, 2025 **Configuration Version**: v1.1.0