# Deployment Guide Complete guide for deploying B12 SIS to production environments. ## Architecture Overview ``` ┌─────────────────────────────────────────────────────────────┐ │ Load Balancer │ │ (HTTPS Termination) │ └─────────────────────┬───────────────────────────────────────┘ │ ┌─────────────┴─────────────┐ ▼ ▼ ┌───────────────┐ ┌───────────────┐ │ Frontend │ │ Backend │ │ (Nginx) │──────────▶│ (Go/Gin) │ │ Port 80 │ │ Port 8080 │ └───────────────┘ └───────┬───────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ MySQL │ │ Redis │ │ S3 │ │ Database │ │ Cache │ │ Storage │ └──────────┘ └──────────┘ └──────────┘ ``` ## Prerequisites - Docker & Docker Compose - Kubernetes cluster (for k8s deployment) - Helm 3+ (for k8s deployment) - MySQL 8.0+ - Redis (optional, for caching) - S3-compatible storage (AWS S3, MinIO) ## Environment Configuration ### Backend Environment Variables Create `.env` file in `b12-backend/`: ```bash # Server PORT=8080 GIN_MODE=release # Database DB_HOST=mysql DB_PORT=3306 DB_USER=b12 DB_PASSWORD=secure_password DB_NAME=b12_sis # JWT Authentication JWT_SECRET=your-secure-jwt-secret-min-32-chars JWT_ACCESS_EXPIRY=15m JWT_REFRESH_EXPIRY=7d # S3 Storage AWS_ACCESS_KEY_ID=your-access-key AWS_SECRET_ACCESS_KEY=your-secret-key AWS_REGION=us-east-1 S3_BUCKET=b12-uploads S3_ENDPOINT=https://s3.amazonaws.com # CORS ALLOWED_ORIGINS=https://sis.school.edu # Email (optional) SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_USER=noreply@school.edu SMTP_PASSWORD=app-password # Canvas LMS (optional) CANVAS_API_URL=https://school.instructure.com CANVAS_API_TOKEN=canvas-api-token # LTI 1.3 (optional) LTI_ISSUER=https://sis.school.edu LTI_PLATFORM_ID=canvas.instructure.com LTI_CLIENT_ID=your-client-id LTI_DEPLOYMENT_ID=deployment-id LTI_JWK_URL=https://sis.school.edu/.well-known/jwks.json LTI_AUTH_URL=https://school.instructure.com/api/lti/authorize_redirect LTI_TOKEN_URL=https://school.instructure.com/login/oauth2/token ``` ### Frontend Environment Configure `b12-frontend/src/environments/environment.prod.ts`: ```typescript export const environment = { production: true, apiUrl: 'https://api.school.edu/api' }; ``` ## Docker Deployment ### Backend Dockerfile ```dockerfile # b12-backend/Dockerfile FROM golang:1.23-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o main ./cmd/api FROM alpine:latest RUN apk --no-cache add ca-certificates tzdata WORKDIR /root/ COPY --from=builder /app/main . COPY --from=builder /app/locales ./locales EXPOSE 8080 CMD ["./main"] ``` ### Frontend Dockerfile ```dockerfile # b12-frontend/Dockerfile FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build:prod FROM nginx:alpine COPY --from=builder /app/dist/b12-frontend /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] ``` ### Frontend Nginx Config ```nginx # b12-frontend/nginx.conf server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; # Gzip compression gzip on; gzip_types text/plain text/css application/json application/javascript; # Angular routing location / { try_files $uri $uri/ /index.html; } # API proxy (if same domain) location /api { proxy_pass http://backend:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # Cache static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; } } ``` ### Docker Compose ```yaml # docker-compose.yml version: '3.8' services: frontend: build: ./b12-frontend ports: - "80:80" depends_on: - backend networks: - b12-network backend: build: ./b12-backend ports: - "8080:8080" env_file: - ./b12-backend/.env depends_on: - mysql - redis networks: - b12-network mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root_password MYSQL_DATABASE: b12_sis MYSQL_USER: b12 MYSQL_PASSWORD: secure_password volumes: - mysql_data:/var/lib/mysql networks: - b12-network redis: image: redis:alpine networks: - b12-network volumes: mysql_data: networks: b12-network: ``` ### Deploy with Docker Compose ```bash # Build and start docker-compose up -d --build # View logs docker-compose logs -f # Stop services docker-compose down ``` ## Kubernetes Deployment ### Directory Structure ``` k8s/ ├── helm/ │ ├── b12-backend/ │ │ ├── Chart.yaml │ │ ├── values.yaml │ │ └── templates/ │ │ ├── deployment.yaml │ │ ├── service.yaml │ │ ├── configmap.yaml │ │ └── secret.yaml │ └── b12-frontend/ │ ├── Chart.yaml │ ├── values.yaml │ └── templates/ │ ├── deployment.yaml │ ├── service.yaml │ └── ingress.yaml ``` ### Backend Helm Values ```yaml # k8s/helm/b12-backend/values.yaml replicaCount: 3 image: repository: your-registry/b12-backend tag: latest pullPolicy: Always service: type: ClusterIP port: 8080 resources: limits: cpu: 500m memory: 512Mi requests: cpu: 100m memory: 128Mi env: GIN_MODE: release DB_HOST: mysql.database.svc.cluster.local secrets: JWT_SECRET: "" DB_PASSWORD: "" AWS_SECRET_ACCESS_KEY: "" ``` ### Frontend Helm Values ```yaml # k8s/helm/b12-frontend/values.yaml replicaCount: 2 image: repository: your-registry/b12-frontend tag: latest pullPolicy: Always service: type: ClusterIP port: 80 ingress: enabled: true className: nginx hosts: - host: sis.school.edu paths: - path: / pathType: Prefix tls: - secretName: sis-tls hosts: - sis.school.edu resources: limits: cpu: 200m memory: 128Mi requests: cpu: 50m memory: 64Mi ``` ### Deploy with Helm ```bash # Backend cd b12-backend make deploy # Select environment when prompted # Frontend cd b12-frontend make deploy # Select environment when prompted ``` ### Manual Helm Commands ```bash # Create namespace kubectl create namespace b12-sis # Deploy backend helm upgrade --install b12-backend ./k8s/helm/b12-backend \ --namespace b12-sis \ --values ./k8s/helm/b12-backend/values-prod.yaml # Deploy frontend helm upgrade --install b12-frontend ./k8s/helm/b12-frontend \ --namespace b12-sis \ --values ./k8s/helm/b12-frontend/values-prod.yaml ``` ## Database Setup ### Initial Migration The backend automatically runs migrations on startup. For manual migration: ```bash # Connect to backend container kubectl exec -it deployment/b12-backend -n b12-sis -- sh # Or with Docker docker exec -it b12-backend sh # Migrations run automatically, but can force: ./main migrate ``` ### Database Backup ```bash # MySQL backup mysqldump -h $DB_HOST -u $DB_USER -p$DB_PASSWORD $DB_NAME > backup.sql # Kubernetes CronJob for automated backups kubectl apply -f k8s/backup-cronjob.yaml ``` ## SSL/TLS Configuration ### Using cert-manager ```yaml # k8s/certificate.yaml apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: sis-tls namespace: b12-sis spec: secretName: sis-tls issuerRef: name: letsencrypt-prod kind: ClusterIssuer dnsNames: - sis.school.edu - api.school.edu ``` ### Manual TLS Secret ```bash kubectl create secret tls sis-tls \ --cert=fullchain.pem \ --key=privkey.pem \ -n b12-sis ``` ## Health Checks ### Backend Health Endpoint ```bash # Health check curl http://localhost:8080/api/health # Response {"status":"ok","database":"connected","redis":"connected"} ``` ### Kubernetes Probes ```yaml livenessProbe: httpGet: path: /api/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /api/health port: 8080 initialDelaySeconds: 5 periodSeconds: 5 ``` ## Monitoring & Logging ### Application Logs ```bash # Kubernetes logs kubectl logs -f deployment/b12-backend -n b12-sis # Docker logs docker logs -f b12-backend ``` ### Metrics (Prometheus) Backend exposes metrics at `/metrics`: ```bash curl http://localhost:8080/metrics ``` ### Log Aggregation Configure Fluentd/Filebeat to ship logs to Elasticsearch or similar. ## Scaling ### Horizontal Pod Autoscaler ```yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: b12-backend-hpa namespace: b12-sis spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: b12-backend minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 ``` ### Database Scaling For high availability: - Use MySQL replication (primary-replica) - Consider Amazon RDS or Google Cloud SQL - Implement read replicas for query distribution ## Troubleshooting ### Common Issues **Backend won't start** ```bash # Check logs kubectl logs deployment/b12-backend -n b12-sis # Common causes: # - Database connection failed # - Missing environment variables # - Port already in use ``` **Frontend returns 404** ```bash # Ensure nginx config has try_files for Angular routing location / { try_files $uri $uri/ /index.html; } ``` **CORS errors** ```bash # Check ALLOWED_ORIGINS in backend .env ALLOWED_ORIGINS=https://sis.school.edu,https://www.school.edu ``` **Database connection issues** ```bash # Test connectivity kubectl run mysql-client --rm -it --image=mysql:8.0 -- \ mysql -h mysql.database.svc.cluster.local -u b12 -p ``` ### Debug Mode ```bash # Enable debug logging in backend GIN_MODE=debug # Check nginx config docker exec b12-frontend nginx -t ``` ## Rollback ### Kubernetes Rollback ```bash # View history kubectl rollout history deployment/b12-backend -n b12-sis # Rollback to previous kubectl rollout undo deployment/b12-backend -n b12-sis # Rollback to specific revision kubectl rollout undo deployment/b12-backend -n b12-sis --to-revision=2 ``` ### Helm Rollback ```bash # View history helm history b12-backend -n b12-sis # Rollback helm rollback b12-backend 1 -n b12-sis ``` ## Security Checklist - [ ] Use HTTPS everywhere - [ ] Set secure JWT secret (32+ characters) - [ ] Configure CORS properly - [ ] Use Kubernetes secrets for sensitive data - [ ] Enable network policies - [ ] Regular security updates - [ ] Database backups encrypted - [ ] Access logs enabled - [ ] Rate limiting configured