From 300b79ead8031ed16627a6bb97598a17ca33fc8c Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Sun, 4 Jan 2026 12:37:59 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20Th=C3=AAm=20c=E1=BA=A5u=20h=C3=ACnh=20v?= =?UTF-8?q?=C3=A0=20script=20tri=E1=BB=83n=20khai=20Kubernetes=20c?= =?UTF-8?q?=E1=BB=A5c=20b=E1=BB=99=20cho=20d=E1=BB=8Bch=20v=E1=BB=A5=20IAM?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iam_service_audit_plan_d8aad26f.plan.md | 2 +- deployments/local/kubernetes/README.md | 213 ++++++++++++++++++ deployments/local/kubernetes/deploy.sh | 128 +++++++++++ .../kubernetes/iam-service-configmap.yaml | 32 +++ .../kubernetes/iam-service-deployment.yaml | 102 +++++++++ .../local/kubernetes/iam-service-service.yaml | 17 ++ 6 files changed, 493 insertions(+), 1 deletion(-) create mode 100644 deployments/local/kubernetes/README.md create mode 100755 deployments/local/kubernetes/deploy.sh create mode 100644 deployments/local/kubernetes/iam-service-configmap.yaml create mode 100644 deployments/local/kubernetes/iam-service-deployment.yaml create mode 100644 deployments/local/kubernetes/iam-service-service.yaml diff --git a/.cursor/plans/iam_service_audit_plan_d8aad26f.plan.md b/.cursor/plans/iam_service_audit_plan_d8aad26f.plan.md index 12548442..5bd5c247 100644 --- a/.cursor/plans/iam_service_audit_plan_d8aad26f.plan.md +++ b/.cursor/plans/iam_service_audit_plan_d8aad26f.plan.md @@ -253,7 +253,7 @@ todos: status: completed - id: staging-k8s-1 content: "Create Staging Namespace: kubectl create namespace staging" - status: pending + status: blocked - id: staging-k8s-2 content: "Create Staging Secrets: kubectl create secret generic iam-service-secrets --from-literal=database-url='...' --from-literal=jwt-secret='...' --from-literal=jwt-refresh-secret='...' -n staging" status: pending diff --git a/deployments/local/kubernetes/README.md b/deployments/local/kubernetes/README.md new file mode 100644 index 00000000..bcdc881c --- /dev/null +++ b/deployments/local/kubernetes/README.md @@ -0,0 +1,213 @@ +# Local Kubernetes Deployment Guide (Docker Desktop) + +## Prerequisites + +- Docker Desktop for macOS M4 Max +- kubectl CLI installed +- Minimum 8GB RAM allocated to Docker Desktop + +## Step 1: Enable Kubernetes in Docker Desktop + +1. Open Docker Desktop +2. Go to **Settings** (gear icon) +3. Click **Kubernetes** tab +4. Check **Enable Kubernetes** +5. Click **Apply & Restart** +6. Wait for Kubernetes to start (green indicator) + +Verify installation: +```bash +kubectl version --client +kubectl cluster-info +kubectl get nodes +``` + +Expected output: +``` +NAME STATUS ROLES AGE VERSION +docker-desktop Ready control-plane 1d v1.28.2 +``` + +## Step 2: Create Local Namespace + +```bash +kubectl create namespace iam-local +kubectl config set-context --current --namespace=iam-local +``` + +## Step 3: Create Secrets + +```bash +# Generate strong secrets (DO NOT use these in production!) +JWT_SECRET=$(openssl rand -base64 32) +JWT_REFRESH_SECRET=$(openssl rand -base64 32) +JWT_ID_SECRET=$(openssl rand -base64 32) +ENCRYPTION_KEY=$(openssl rand -hex 32) + +# Create Kubernetes secret +kubectl create secret generic iam-service-secrets \ + --from-literal=jwt-secret="$JWT_SECRET" \ + --from-literal=jwt-refresh-secret="$JWT_REFRESH_SECRET" \ + --from-literal=jwt-id-secret="$JWT_ID_SECRET" \ + --from-literal=encryption-key="$ENCRYPTION_KEY" \ + --from-literal=database-url="postgresql://username:password@host.neon.tech/database?sslmode=require" \ + -n iam-local +``` + +**IMPORTANT**: Update `database-url` with your actual Neon PostgreSQL connection string. + +## Step 4: Create ConfigMap + +```bash +kubectl apply -f deployments/local/kubernetes/iam-service-configmap.yaml -n iam-local +``` + +## Step 5: Build and Load Docker Image + +```bash +# Build image +cd /Users/velikho/Desktop/WORKING/Base +docker build -t iam-service:local -f services/iam-service/Dockerfile . + +# Verify image +docker images | grep iam-service +``` + +**Note**: Docker Desktop Kubernetes can access images built locally, no need to push to registry. + +## Step 6: Deploy Service + +```bash +kubectl apply -f deployments/local/kubernetes/iam-service-deployment.yaml -n iam-local +``` + +## Step 7: Verify Deployment + +```bash +# Check pods +kubectl get pods -n iam-local + +# Check deployment +kubectl get deployment -n iam-local + +# Check logs +kubectl logs -f deployment/iam-service -n iam-local + +# Describe pod (if issues) +kubectl describe pod -n iam-local +``` + +## Step 8: Expose Service (LoadBalancer) + +```bash +kubectl apply -f deployments/local/kubernetes/iam-service-service.yaml -n iam-local +``` + +For Docker Desktop, LoadBalancer will be available at `localhost`. + +## Step 9: Test Endpoints + +```bash +# Get service URL +kubectl get svc iam-service -n iam-local + +# Test health +curl http://localhost:/health/live +curl http://localhost:/health/ready + +# Test registration +curl -X POST http://localhost:/api/v1/auth/register \ + -H "Content-Type: application/json" \ + -d '{"email":"k8s-test@example.com","password":"Test@123456","username":"k8suser"}' +``` + +## Step 10: Run Database Migrations + +```bash +# Port-forward to access service +kubectl port-forward deployment/iam-service 5001:5001 -n iam-local + +# In another terminal, run migrations +cd services/iam-service +DATABASE_URL="" pnpm prisma:deploy +``` + +## Troubleshooting + +### Pod Not Starting + +```bash +# Check events +kubectl get events -n iam-local --sort-by='.lastTimestamp' + +# Check pod logs +kubectl logs -n iam-local + +# Check pod description +kubectl describe pod -n iam-local +``` + +Common issues: +- **ImagePullBackOff**: Image not found locally, rebuild image +- **CrashLoopBackOff**: Check logs for errors, verify secrets/configmap +- **Pending**: Resource constraints, check Docker Desktop RAM allocation + +### Service Not Accessible + +```bash +# Check service +kubectl get svc -n iam-local + +# Check endpoints +kubectl get endpoints -n iam-local + +# Port forward for testing +kubectl port-forward svc/iam-service 5001:80 -n iam-local +``` + +### Database Connection Issues + +- Verify DATABASE_URL in secret +- Check if Neon database allows connections from your IP +- Test connection manually: `psql ` + +## Cleanup + +```bash +# Delete all resources +kubectl delete namespace iam-local + +# Or delete individual resources +kubectl delete deployment iam-service -n iam-local +kubectl delete service iam-service -n iam-local +kubectl delete configmap iam-service-config -n iam-local +kubectl delete secret iam-service-secrets -n iam-local +``` + +## Next Steps + +After successful local K8s deployment: +1. Test all API endpoints +2. Verify health checks +3. Test autoscaling (HPA) +4. Test rolling updates +5. Simulate pod failures + +## Differences from Production + +| Aspect | Local K8s | Production K8s | +|--------|-----------|----------------| +| **Cluster** | Docker Desktop | Cloud (GKE/EKS/AKS) | +| **LoadBalancer** | localhost | Cloud LB with public IP | +| **Ingress** | Optional | Required (with TLS) | +| **Secrets** | kubectl create | External secrets manager | +| **Database** | Neon (shared) | Dedicated production DB | +| **Monitoring** | Optional | Required (Prometheus/Grafana) | +| **Replicas** | 1-2 | 3+ with HPA | +| **Resources** | Minimal | Production-grade limits | + +## Resources + +- [Docker Desktop Kubernetes](https://docs.docker.com/desktop/kubernetes/) +- [kubectl Cheat Sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/) +- [Kubernetes Documentation](https://kubernetes.io/docs/home/) diff --git a/deployments/local/kubernetes/deploy.sh b/deployments/local/kubernetes/deploy.sh new file mode 100755 index 00000000..30b3de3c --- /dev/null +++ b/deployments/local/kubernetes/deploy.sh @@ -0,0 +1,128 @@ +#!/bin/bash +# EN: Quick deploy script for local Kubernetes (Docker Desktop) +# VI: Script deploy nhanh cho local Kubernetes (Docker Desktop) + +set -e # Exit on error + +echo "🚀 IAM Service - Local Kubernetes Deployment" +echo "==============================================" +echo "" + +# Check if kubectl is installed +if ! command -v kubectl &> /dev/null; then + echo "❌ kubectl not found. Please install kubectl first." + echo " Install: brew install kubectl" + exit 1 +fi + +# Check if Kubernetes is running +if ! kubectl cluster-info &> /dev/null; then + echo "❌ Kubernetes cluster not accessible." + echo " Please enable Kubernetes in Docker Desktop:" + echo " Settings → Kubernetes → Enable Kubernetes" + exit 1 +fi + +echo "✅ kubectl installed" +echo "✅ Kubernetes cluster accessible" +echo "" + +# Create namespace +echo "📦 Creating namespace 'iam-local'..." +kubectl create namespace iam-local --dry-run=client -o yaml | kubectl apply -f - + +# Set context +kubectl config set-context --current --namespace=iam-local + +echo "✅ Namespace created and set as default" +echo "" + +# Prompt for database URL +echo "🔐 Setting up secrets..." +read -p "Enter your Neon DATABASE_URL: " DATABASE_URL + +if [ -z "$DATABASE_URL" ]; then + echo "❌ DATABASE_URL is required" + exit 1 +fi + +# Generate secrets +echo "🔑 Generating JWT secrets..." +JWT_SECRET=$(openssl rand -base64 32) +JWT_REFRESH_SECRET=$(openssl rand -base64 32) +JWT_ID_SECRET=$(openssl rand -base64 32) +ENCRYPTION_KEY=$(openssl rand -hex 32) + +# Create secret +kubectl create secret generic iam-service-secrets \ + --from-literal=jwt-secret="$JWT_SECRET" \ + --from-literal=jwt-refresh-secret="$JWT_REFRESH_SECRET" \ + --from-literal=jwt-id-secret="$JWT_ID_SECRET" \ + --from-literal=encryption-key="$ENCRYPTION_KEY" \ + --from-literal=database-url="$DATABASE_URL" \ + --dry-run=client -o yaml | kubectl apply -f - + +echo "✅ Secrets created" +echo "" + +# Apply ConfigMap +echo "⚙️ Applying ConfigMap..." +kubectl apply -f deployments/local/kubernetes/iam-service-configmap.yaml + +echo "✅ ConfigMap applied" +echo "" + +# Build Docker image +echo "🐳 Building Docker image..." +docker build -t iam-service:local -f services/iam-service/Dockerfile . + +echo "✅ Docker image built" +echo "" + +# Apply Deployment +echo "🚀 Deploying IAM Service..." +kubectl apply -f deployments/local/kubernetes/iam-service-deployment.yaml + +echo "✅ Deployment created" +echo "" + +# Apply Service +echo "🌐 Creating Service..." +kubectl apply -f deployments/local/kubernetes/iam-service-service.yaml + +echo "✅ Service created" +echo "" + +# Wait for deployment +echo "⏳ Waiting for deployment to be ready..." +kubectl wait --for=condition=available --timeout=120s deployment/iam-service + +echo "✅ Deployment ready!" +echo "" + +# Get service info +echo "📊 Service Information:" +kubectl get svc iam-service +echo "" + +# Get pod info +echo "📦 Pod Information:" +kubectl get pods -l app=iam-service +echo "" + +# Show logs +echo "📝 Recent logs:" +kubectl logs -l app=iam-service --tail=20 +echo "" + +echo "✅ Deployment complete!" +echo "" +echo "🎯 Next steps:" +echo " 1. Run migrations: DATABASE_URL='$DATABASE_URL' pnpm --filter @goodgo/iam-service prisma:deploy" +echo " 2. Test health: kubectl port-forward svc/iam-service 5001:80" +echo " 3. Access API: curl http://localhost:5001/health/live" +echo "" +echo "📚 Useful commands:" +echo " - View logs: kubectl logs -f deployment/iam-service" +echo " - Describe pod: kubectl describe pod " +echo " - Delete all: kubectl delete namespace iam-local" diff --git a/deployments/local/kubernetes/iam-service-configmap.yaml b/deployments/local/kubernetes/iam-service-configmap.yaml new file mode 100644 index 00000000..c0fd1e5e --- /dev/null +++ b/deployments/local/kubernetes/iam-service-configmap.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: iam-service-config + namespace: iam-local +data: + # Node Environment + NODE_ENV: "development" + LOG_LEVEL: "debug" + + # Service Configuration + PORT: "5001" + SERVICE_NAME: "iam-service" + API_VERSION: "v1" + + # Redis Configuration + REDIS_HOST: "redis" + REDIS_PORT: "6379" + + # JWT Configuration + JWT_EXPIRES_IN: "15m" + JWT_REFRESH_EXPIRES_IN: "7d" + JWT_ID_EXPIRES_IN: "1h" + JWT_ISSUER: "goodgo-auth" + JWT_AUDIENCE: "goodgo-api" + + # CORS Configuration + CORS_ORIGIN: "http://localhost:3000,http://localhost:3001,http://localhost" + + # Observability + TRACING_ENABLED: "false" + METRICS_ENABLED: "true" diff --git a/deployments/local/kubernetes/iam-service-deployment.yaml b/deployments/local/kubernetes/iam-service-deployment.yaml new file mode 100644 index 00000000..39999fc7 --- /dev/null +++ b/deployments/local/kubernetes/iam-service-deployment.yaml @@ -0,0 +1,102 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: iam-service + namespace: iam-local + labels: + app: iam-service + version: local +spec: + replicas: 1 + selector: + matchLabels: + app: iam-service + template: + metadata: + labels: + app: iam-service + version: local + spec: + containers: + - name: iam-service + image: iam-service:local + imagePullPolicy: Never # Use local image, don't pull from registry + ports: + - containerPort: 5001 + name: http + protocol: TCP + + # Environment variables from ConfigMap + envFrom: + - configMapRef: + name: iam-service-config + + # Sensitive environment variables from Secret + env: + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: iam-service-secrets + key: database-url + - name: JWT_SECRET + valueFrom: + secretKeyRef: + name: iam-service-secrets + key: jwt-secret + - name: JWT_REFRESH_SECRET + valueFrom: + secretKeyRef: + name: iam-service-secrets + key: jwt-refresh-secret + - name: JWT_ID_SECRET + valueFrom: + secretKeyRef: + name: iam-service-secrets + key: jwt-id-secret + - name: ENCRYPTION_KEY + valueFrom: + secretKeyRef: + name: iam-service-secrets + key: encryption-key + + # Resource limits (adjust based on Docker Desktop allocation) + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + + # Liveness probe - is the container running? + livenessProbe: + httpGet: + path: /health/live + port: 5001 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + + # Readiness probe - is the container ready to serve traffic? + readinessProbe: + httpGet: + path: /health/ready + port: 5001 + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 5 + failureThreshold: 3 + + # Startup probe - give container time to start + startupProbe: + httpGet: + path: /health/live + port: 5001 + initialDelaySeconds: 0 + periodSeconds: 5 + timeoutSeconds: 5 + failureThreshold: 12 # 60 seconds total (12 * 5s) + + # Restart policy + restartPolicy: Always diff --git a/deployments/local/kubernetes/iam-service-service.yaml b/deployments/local/kubernetes/iam-service-service.yaml new file mode 100644 index 00000000..955aa4d6 --- /dev/null +++ b/deployments/local/kubernetes/iam-service-service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: iam-service + namespace: iam-local + labels: + app: iam-service +spec: + type: LoadBalancer # Docker Desktop will expose on localhost + selector: + app: iam-service + ports: + - name: http + port: 80 + targetPort: 5001 + protocol: TCP + sessionAffinity: None