feat: Thêm cấu hình và script triển khai Kubernetes cục bộ cho dịch vụ IAM.
This commit is contained in:
@@ -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
|
||||
|
||||
213
deployments/local/kubernetes/README.md
Normal file
213
deployments/local/kubernetes/README.md
Normal file
@@ -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 <pod-name> -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:<PORT>/health/live
|
||||
curl http://localhost:<PORT>/health/ready
|
||||
|
||||
# Test registration
|
||||
curl -X POST http://localhost:<PORT>/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="<your-neon-db-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 <pod-name> -n iam-local
|
||||
|
||||
# Check pod description
|
||||
kubectl describe pod <pod-name> -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 <database-url>`
|
||||
|
||||
## 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/)
|
||||
128
deployments/local/kubernetes/deploy.sh
Executable file
128
deployments/local/kubernetes/deploy.sh
Executable file
@@ -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 <pod-name>"
|
||||
echo " - Delete all: kubectl delete namespace iam-local"
|
||||
32
deployments/local/kubernetes/iam-service-configmap.yaml
Normal file
32
deployments/local/kubernetes/iam-service-configmap.yaml
Normal file
@@ -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"
|
||||
102
deployments/local/kubernetes/iam-service-deployment.yaml
Normal file
102
deployments/local/kubernetes/iam-service-deployment.yaml
Normal file
@@ -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
|
||||
17
deployments/local/kubernetes/iam-service-service.yaml
Normal file
17
deployments/local/kubernetes/iam-service-service.yaml
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user