This repository contains deployment manifests and configuration for running the SAIF (Secure AI Foundations) application on Azure Local in disconnected operations mode. The deployment includes a 3-tier containerized application with local monitoring and SQL Server VM backend.
graph TB
subgraph "Azure Local - Disconnected Operations"
subgraph "AKS Arc Cluster"
Web[Web Frontend<br/>PHP 8.2<br/>2 replicas]
API[API Backend<br/>FastAPI Python<br/>2 replicas]
Ingress[NGINX Ingress<br/>Controller]
subgraph "Monitoring Stack"
Prom[Prometheus<br/>Metrics]
Graf[Grafana<br/>Dashboards]
end
Ingress --> Web
Ingress --> API
API --> Prom
Web --> Prom
Prom --> Graf
end
subgraph "Infrastructure"
ACR[Local Azure<br/>Container Registry]
SQL[SQL Server VM<br/>SQL Authentication]
end
Web --> API
API --> SQL
AKS --> ACR
end
User[User] --> Ingress
style User fill:#f96,stroke:#333,color:#000
style Web fill:#0078D4,stroke:#005A9E,color:#fff
style API fill:#0078D4,stroke:#005A9E,color:#fff
style SQL fill:#CC2927,stroke:#A52A2A,color:#fff
style Prom fill:#E6522C,stroke:#C63928,color:#fff
style Graf fill:#F46800,stroke:#D15D00,color:#fff
style ACR fill:#0089D6,stroke:#006BB8,color:#fff
style Ingress fill:#009639,stroke:#007A2F,color:#fff
- ✅ Disconnected Operations: No dependency on Azure cloud services
- ✅ SQL Authentication: Uses SQL Server authentication (Entra ID not supported in disconnected mode)
- ✅ Local Monitoring: Prometheus & Grafana for metrics and dashboards
- ✅ Local Container Registry: Pre-loaded images in Azure Container Registry
- ✅ Static Networking: Configured for Azure Local logical networks
- ✅ High Availability: 2 replicas each for web and API pods
- Azure Local cluster deployed with disconnected operations enabled
- AKS Arc cluster created (see
boilerplates/aks-arc/create-cluster.md) - Supported Kubernetes versions: 1.27.7, 1.27.9, 1.28.5, 1.28.9, 1.29.2, 1.29.4 (recommended)
- SQL Server VM deployed on Azure Local (see
boilerplates/sql-server/sql-vm-setup.md) - SQL Server 2022 (Standard or Enterprise)
- SQL Server Authentication enabled
- Database:
saifdb - User:
saifadminwith appropriate permissions
- Local Azure Container Registry configured
- All container images pre-loaded (see
container-registry/README.md)
kubectlconfigured with AKS Arc cluster credentials- Azure CLI with AKS Arc extensions
- PowerShell 7+ (for deployment scripts)
graph LR
Root[saif-aldo] --> K8s[kubernetes/]
Root --> Boot[boilerplates/]
Root --> Reg[container-registry/]
Root --> SQL[sql-scripts/]
Root --> Docs[docs/]
K8s --> Manifests[manifests/]
K8s --> Config[config/]
Manifests --> Web[web/]
Manifests --> API[api/]
Manifests --> Mon[monitoring/]
Config --> NS[namespace.yaml]
Config --> CM[configmap.yaml]
Config --> Sec[secret.yaml]
Boot --> AKS[aks-arc/]
Boot --> SQLBoot[sql-server/]
style Root fill:#0078D4,stroke:#005A9E,color:#fff
style K8s fill:#326CE5,stroke:#2A5DC7,color:#fff
style Boot fill:#FFB900,stroke:#D99E00,color:#000
style Reg fill:#0089D6,stroke:#006BB8,color:#fff
style SQL fill:#CC2927,stroke:#A52A2A,color:#fff
Before starting deployment, ensure you have:
- Azure Local cluster deployed in disconnected mode
- AKS Arc cluster created (Kubernetes 1.27.7 - 1.29.4)
- SQL Server VM deployed with SQL authentication enabled
- Local Azure Container Registry accessible
- Docker installed for building images
- kubectl configured with AKS Arc credentials
- Azure CLI installed with aksarc extension
- PowerShell 7+ installed
- Network connectivity between AKS Arc and SQL Server VM
- SQL Server credentials ready (username/password)
# Clone repository (if not already done)
git clone <repository-url>
cd saif-aldo
# Get AKS Arc credentials
az aksarc get-credentials \
--resource-group <your-rg> \
--name <your-aks-cluster> \
--admin
# Verify cluster access
kubectl get nodes# Pre-load and push images to local registry
cd container-registry
.\prepare-images.ps1
# Select option 5 for full workflow (Build and Export)# Deploy database schema
cd ..\sql-scripts
$password = Read-Host "Enter SQL password" -AsSecureString
.\deploy-database.ps1 `
-SqlServerInstance "sql-server-vm.local" `
-Username "saifadmin" `
-Password $passwordEdit kubernetes/config/configmap.yaml:
data:
SQL_SERVER: "sql-server-vm.local" # Update with actual SQL Server hostname/IP
SQL_DATABASE: "saifdb"Edit kubernetes/config/secret.yaml:
stringData:
SQL_USERNAME: "saifadmin"
SQL_PASSWORD: "YourSecurePassword" # Update with actual passwordTip: If you need Base64-encoded values instead of stringData, use:
# Base64 encode credentials
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("your-sql-server.local"))
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("saifdb"))
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("saifadmin"))
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("YourSecurePassword"))cd ..\kubernetes
# Create namespace and configuration
kubectl apply -f config/namespace.yaml
kubectl apply -f config/configmap.yaml
kubectl apply -f config/secret.yaml
# Deploy application
kubectl apply -f manifests/api/
kubectl apply -f manifests/web/
# Deploy monitoring
kubectl apply -f manifests/monitoring/
# Verify deployment
kubectl get pods -n saif
kubectl get services -n saifflowchart TD
Start([Start Deployment]) --> CheckPrereq{Prerequisites<br/>Met?}
CheckPrereq -->|No| FixPrereq[Fix Prerequisites]
FixPrereq --> CheckPrereq
CheckPrereq -->|Yes| PrepImages[Prepare Container Images]
PrepImages --> Build[Build SAIF Images]
Build --> Export[Export to TAR Files]
Export --> Transfer[Transfer to Disconnected]
Transfer --> Load[Load Images]
Load --> Push[Push to Local ACR]
Push --> DeploySQL[Deploy SQL Database]
DeploySQL --> InitDB[Initialize Database Schema]
InitDB --> UpdateConfig[Update Kubernetes Config]
UpdateConfig --> CreateNS[Create Namespace]
CreateNS --> CreateCM[Apply ConfigMap]
CreateCM --> CreateSec[Apply Secrets]
CreateSec --> DeployAPI[Deploy API Pods]
DeployAPI --> DeployWeb[Deploy Web Pods]
DeployWeb --> DeployMon[Deploy Monitoring]
DeployMon --> Verify{All Pods<br/>Running?}
Verify -->|No| Troubleshoot[Troubleshoot Issues]
Troubleshoot --> Verify
Verify -->|Yes| TestApp[Test Application]
TestApp --> Success([Deployment Complete])
style Start fill:#4CAF50,stroke:#2E7D32,color:#fff
style Success fill:#4CAF50,stroke:#2E7D32,color:#fff
style CheckPrereq fill:#FFC107,stroke:#F57C00,color:#000
style Verify fill:#FFC107,stroke:#F57C00,color:#000
style Troubleshoot fill:#F44336,stroke:#C62828,color:#fff
# Get web frontend URL
kubectl get service saif-web -n saif
# Get API service URL
kubectl get service saif-api -n saif
# Get Grafana dashboard URL
kubectl get service grafana -n saif# Test API health check
kubectl run -it --rm test --image=curlimages/curl --restart=Never -n saif -- \
curl http://saif-api:8000/api/healthcheck
# Test web frontend
kubectl run -it --rm test --image=curlimages/curl --restart=Never -n saif -- \
curl http://saif-web/# Port-forward to Prometheus
kubectl port-forward -n saif svc/prometheus-server 9090:9090
# Open http://localhost:9090# Port-forward to Grafana
kubectl port-forward -n saif svc/grafana 3000:3000
# Open http://localhost:3000
# Default credentials: admin/admin (change on first login)graph LR
subgraph "Application Pods"
API[API Pods]
Web[Web Pods]
end
subgraph "Monitoring Stack"
Prom[Prometheus]
Store[Time Series DB]
Graf[Grafana]
Dash[Dashboards]
end
API -->|/metrics| Prom
Web -->|/metrics| Prom
Prom --> Store
Store --> Graf
Graf --> Dash
User[User] --> Graf
style API fill:#0078D4,stroke:#005A9E,color:#fff
style Web fill:#0078D4,stroke:#005A9E,color:#fff
style Prom fill:#E6522C,stroke:#C63928,color:#fff
style Graf fill:#F46800,stroke:#D15D00,color:#fff
style User fill:#f96,stroke:#333,color:#000
# Check pod status
kubectl get pods -n saif
# Get pod logs
kubectl logs -n saif <pod-name>
# Describe pod for events
kubectl describe pod -n saif <pod-name># Verify ACR secret
kubectl get secret acr-secret -n saif
# Test image pull
kubectl run test --image=<registry>/saif/api:latest --image-pull-policy=IfNotPresent -n saif# Test SQL connection from pod
kubectl run -it --rm sqltest --image=mcr.microsoft.com/mssql-tools --restart=Never -n saif -- /bin/bash
# Inside pod
/opt/mssql-tools/bin/sqlcmd -S sql-server-vm.local -U saifadmin -P 'password' -Q "SELECT @@VERSION"# Test DNS from pod
kubectl run -it --rm dnstest --image=busybox --restart=Never -n saif -- nslookup sql-server-vm.local
# If DNS fails, update /etc/hosts or use IP address directly# Build new version
cd container-registry
.\prepare-images.ps1
# Update deployment
kubectl set image deployment/saif-api api=<registry>/saif/api:v2 -n saif
kubectl set image deployment/saif-web web=<registry>/saif/web:v2 -n saif
# Monitor rollout
kubectl rollout status deployment/saif-api -n saif
kubectl rollout status deployment/saif-web -n saif# Scale API pods
kubectl scale deployment saif-api --replicas=3 -n saif
# Scale web pods
kubectl scale deployment saif-web --replicas=3 -n saif-- Run on SQL Server VM
BACKUP DATABASE [saifdb]
TO DISK = 'E:\SQLBackups\saifdb_full.bak'
WITH INIT, COMPRESSION;- ✅ No Microsoft Entra ID (use SQL authentication)
- ✅ Secrets stored in Kubernetes secrets (consider encryption at rest)
- ✅ Network policies to restrict pod-to-pod communication
- ✅ SQL Server authentication with strong passwords
- ✅ Local monitoring (no Azure Monitor telemetry)
- Rotate SQL credentials regularly
- Enable Kubernetes RBAC for access control
- Use NetworkPolicies to restrict traffic
- Encrypt secrets at rest in etcd
- Regular security updates for container images
- Monitor security events in Prometheus/Grafana
- ❌ No Microsoft Entra ID: Must use local authentication
- ❌ No Azure Monitor: Use Prometheus/Grafana instead
- ❌ No Key Vault: Secrets managed in Kubernetes
- ❌ No Managed Identity: Use service accounts and credentials
- ❌ No App Insights: Local logging and monitoring only
⚠️ GPU not supported in disconnected AKS⚠️ CLI-based management (portal limitations)
- Azure Local Disconnected Operations Overview
- AKS Arc Disconnected Operations
- AKS Arc Cluster Architecture
- Azure Container Registry for Disconnected
Contributions are welcome! Please ensure:
- All diagrams use Mermaid format
- No dependencies on Azure cloud services
- Testing in disconnected environment
- Documentation updates included
MIT License - see LICENSE file for details
For issues and questions:
- Check Troubleshooting section
- Review official documentation
- Open an issue in this repository
Note: This deployment is optimized for Azure Local in disconnected operations mode. For cloud-based deployments, refer to the original SAIF repository.