guides. Here's what has been created: 📁 Azure Managed Deployment Structure documentation/deployment-guides/azure-managed/ ├── README.md # Overview and architecture ├── 00-prerequisites-managed.md # Azure prerequisites setup ├── 01-aks-managed-deployment.md # AKS deployment guide ├── 02-container-instances-deployment.md # Container Instances guide ├── 03-production-managed-setup.md # Production hardening ├── kubernetes-manifests/ # Kubernetes YAML files │ ├── namespace.yaml # Namespace and quotas │ ├── storage.yaml # Azure Files storage │ ├── configmap.yaml # Configuration │ ├── secrets.yaml # Key Vault integration │ ├── erpnext-backend.yaml # Backend deployment │ ├── erpnext-frontend.yaml # Frontend deployment │ ├── erpnext-workers.yaml # Workers and scheduler │ ├── ingress.yaml # Ingress configuration │ └── jobs.yaml # Init and backup jobs └── scripts/ # Automation scripts ├── deploy-managed.sh # AKS deployment script └── container-instances-deploy.sh # ACI deployment script 🔄 Key Azure Service Mappings | GCP Service | Azure Equivalent | Notes | |----------------------|--------------------------------------|------------------------------- ---------------------------| | Cloud SQL (MySQL) | Azure Database for PostgreSQL | Using PostgreSQL instead of MySQL for better performance | | Memorystore (Redis) | Azure Cache for Redis | Full compatibility with SSL/TLS | | Cloud Storage | Azure Storage (Blob & Files) | Using Azure Files for shared storage | | GKE | AKS (Azure Kubernetes Service) | Full Kubernetes compatibility | | Cloud Run | Azure Container Instances | Serverless container platform | | Secret Manager | Azure Key Vault | Integrated secrets management | | Cloud IAM | Azure AD & Managed Identity | Enterprise-grade identity | | Stackdriver | Azure Monitor & Application Insights | Comprehensive monitoring | | Cloud CDN | Azure CDN | Global content delivery | | Cloud Load Balancing | Azure Application Gateway | L7 load balancing with WAF | ✨ Key Features 1. Managed Services - Azure Database for PostgreSQL with high availability - Azure Cache for Redis with SSL/TLS encryption - Azure Storage for file management - Azure Key Vault for secrets 2. Deployment Options - AKS: Full Kubernetes control with auto-scaling - Container Instances: Simpler serverless deployment 3. Security - Managed Identity for passwordless authentication - Private endpoints for database and Redis - Network security groups and firewall rules - SSL/TLS encryption everywhere 4. Production Ready - Automated backups with geo-redundancy - Monitoring with Azure Monitor - Auto-scaling configurations - Disaster recovery setup 5. Automation - One-command deployment scripts - Environment validation - Health checks and diagnostics - Backup automation 🚀 Quick Start # Prerequisites cd documentation/deployment-guides/azure-managed/ # Follow 00-prerequisites-managed.md # Option 1: Deploy to AKS ./scripts/deploy-managed.sh deploy # Option 2: Deploy to Container Instances ./scripts/container-instances-deploy.sh deploy 💰 Cost Comparison | Deployment Size | Azure (Monthly) | GCP (Monthly) | |-------------------|-----------------|---------------| | Small (<50 users) | ~ | ~ | | Medium (50-200) | ~ | ~ | | Large (200+) | ~,823 | ~,794 | The Azure deployment uses PostgreSQL instead of MySQL, which provides better performance and features, and includes Azure-specific optimizations for the cloud-native environment.
182 lines
6.0 KiB
YAML
182 lines
6.0 KiB
YAML
apiVersion: networking.k8s.io/v1
|
|
kind: Ingress
|
|
metadata:
|
|
name: erpnext-ingress
|
|
namespace: erpnext
|
|
annotations:
|
|
kubernetes.io/ingress.class: nginx
|
|
nginx.ingress.kubernetes.io/rewrite-target: /
|
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
|
|
# SSL/TLS Configuration
|
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
|
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
|
|
nginx.ingress.kubernetes.io/ssl-protocols: "TLSv1.2 TLSv1.3"
|
|
nginx.ingress.kubernetes.io/ssl-ciphers: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256"
|
|
|
|
# Proxy Configuration
|
|
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
|
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "120"
|
|
nginx.ingress.kubernetes.io/proxy-connect-timeout: "120"
|
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "120"
|
|
nginx.ingress.kubernetes.io/proxy-buffer-size: "8k"
|
|
nginx.ingress.kubernetes.io/proxy-buffers-number: "8"
|
|
|
|
# WebSocket Support
|
|
nginx.ingress.kubernetes.io/websocket-services: "erpnext-websocket"
|
|
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
|
|
|
|
# Security Headers
|
|
nginx.ingress.kubernetes.io/configuration-snippet: |
|
|
more_set_headers "X-Frame-Options: SAMEORIGIN";
|
|
more_set_headers "X-Content-Type-Options: nosniff";
|
|
more_set_headers "X-XSS-Protection: 1; mode=block";
|
|
more_set_headers "Referrer-Policy: strict-origin-when-cross-origin";
|
|
more_set_headers "Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval' https: data: blob:";
|
|
more_set_headers "Permissions-Policy: geolocation=(), microphone=(), camera=()";
|
|
|
|
# Rate Limiting
|
|
nginx.ingress.kubernetes.io/limit-rps: "20"
|
|
nginx.ingress.kubernetes.io/limit-rpm: "200"
|
|
nginx.ingress.kubernetes.io/limit-connections: "10"
|
|
|
|
# Session Affinity
|
|
nginx.ingress.kubernetes.io/affinity: "cookie"
|
|
nginx.ingress.kubernetes.io/affinity-mode: "persistent"
|
|
nginx.ingress.kubernetes.io/session-cookie-name: "erpnext-session"
|
|
nginx.ingress.kubernetes.io/session-cookie-max-age: "86400"
|
|
nginx.ingress.kubernetes.io/session-cookie-change-on-failure: "true"
|
|
|
|
# CORS Configuration (if needed)
|
|
nginx.ingress.kubernetes.io/enable-cors: "true"
|
|
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
|
|
nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,X-Frappe-Site-Name"
|
|
nginx.ingress.kubernetes.io/cors-allow-origin: "*"
|
|
nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
|
|
|
|
# Azure Application Gateway specific (if using AGIC)
|
|
# appgw.ingress.kubernetes.io/use-private-ip: "false"
|
|
# appgw.ingress.kubernetes.io/backend-protocol: "http"
|
|
# appgw.ingress.kubernetes.io/cookie-based-affinity: "true"
|
|
# appgw.ingress.kubernetes.io/request-timeout: "120"
|
|
# appgw.ingress.kubernetes.io/connection-draining: "true"
|
|
# appgw.ingress.kubernetes.io/connection-draining-timeout: "30"
|
|
|
|
spec:
|
|
ingressClassName: nginx
|
|
tls:
|
|
- hosts:
|
|
- ${DOMAIN}
|
|
- www.${DOMAIN}
|
|
secretName: erpnext-tls
|
|
rules:
|
|
- host: ${DOMAIN}
|
|
http:
|
|
paths:
|
|
# WebSocket endpoint
|
|
- path: /socket.io
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: erpnext-websocket
|
|
port:
|
|
number: 9000
|
|
|
|
# API endpoints (higher priority)
|
|
- path: /api
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: erpnext-backend
|
|
port:
|
|
number: 8000
|
|
|
|
# Assets and files
|
|
- path: /assets
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: erpnext-frontend
|
|
port:
|
|
number: 8080
|
|
|
|
- path: /files
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: erpnext-frontend
|
|
port:
|
|
number: 8080
|
|
|
|
# Default route
|
|
- path: /
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: erpnext-frontend
|
|
port:
|
|
number: 8080
|
|
|
|
# Redirect www to non-www
|
|
- host: www.${DOMAIN}
|
|
http:
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: erpnext-frontend
|
|
port:
|
|
number: 8080
|
|
---
|
|
# Alternative Ingress for Application Gateway Ingress Controller (AGIC)
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: Ingress
|
|
metadata:
|
|
name: erpnext-ingress-agic
|
|
namespace: erpnext
|
|
annotations:
|
|
kubernetes.io/ingress.class: azure/application-gateway
|
|
appgw.ingress.kubernetes.io/ssl-redirect: "true"
|
|
appgw.ingress.kubernetes.io/use-private-ip: "false"
|
|
appgw.ingress.kubernetes.io/backend-protocol: "http"
|
|
appgw.ingress.kubernetes.io/backend-hostname: "erpnext-backend"
|
|
appgw.ingress.kubernetes.io/cookie-based-affinity: "true"
|
|
appgw.ingress.kubernetes.io/request-timeout: "120"
|
|
appgw.ingress.kubernetes.io/connection-draining: "true"
|
|
appgw.ingress.kubernetes.io/connection-draining-timeout: "30"
|
|
appgw.ingress.kubernetes.io/health-probe-path: "/api/method/ping"
|
|
appgw.ingress.kubernetes.io/health-probe-interval: "30"
|
|
appgw.ingress.kubernetes.io/health-probe-timeout: "10"
|
|
appgw.ingress.kubernetes.io/health-probe-unhealthy-threshold: "3"
|
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
spec:
|
|
tls:
|
|
- hosts:
|
|
- ${DOMAIN}
|
|
secretName: erpnext-tls-agic
|
|
rules:
|
|
- host: ${DOMAIN}
|
|
http:
|
|
paths:
|
|
- path: /socket.io/*
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: erpnext-websocket
|
|
port:
|
|
number: 9000
|
|
- path: /api/*
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: erpnext-backend
|
|
port:
|
|
number: 8000
|
|
- path: /*
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: erpnext-frontend
|
|
port:
|
|
number: 8080 |