Azure Deployment Architecture¶
Deployment Flow¶
┌─────────────────────────────────────────────────────────────────┐
│ Developer │
│ │
│ 1. Edit files in website/ folder │
│ 2. git commit -m "Update website" │
│ 3. git push origin main │
└─────────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ GitHub Repository │
│ (Stig-Johnny/nutri-e) │
│ │
│ • Detects push to 'main' branch │
│ • Checks if website/ folder changed │
│ • Triggers workflow: deploy-website.yml │
└─────────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ GitHub Actions Runner │
│ (Ubuntu Latest) │
│ │
│ Step 1: Checkout code │
│ Step 2: Login to Azure with service principal │
│ Step 3: Upload website/ to Azure Storage │
│ Step 4: (Optional) Purge CDN cache │
└─────────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Azure Storage Account │
│ (nutriewebsite.z16.web.core.windows.net) │
│ │
│ Resource Group: nutri-e-rg │
│ Location: West Europe │
│ Container: $web (public read access) │
│ │
│ Files: │
│ ├── index.html │
│ ├── privacy.html │
│ ├── terms.html │
│ ├── 404.html │
│ ├── styles.css │
│ ├── script.js │
│ └── images/ │
└─────────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Live Website │
│ https://nutriewebsite.z16.web.core.windows.net │
│ │
│ ✅ Publicly accessible │
│ ✅ HTTPS enabled │
│ ✅ Custom 404 page │
│ ✅ Updates in ~30 seconds │
└─────────────────────────────────────────────────────────────────┘
Optional: CDN Layer¶
┌─────────────────────────────────────────────────────────────────┐
│ Azure CDN │
│ (nutri-e.azureedge.net) │
│ │
│ • Global content delivery │
│ • Caching at edge locations │
│ • Custom domain support │
│ • SSL/TLS certificates │
│ • DDoS protection │
└─────────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Custom Domain │
│ https://www.nutri-e.com │
│ │
│ CNAME → nutri-e.azureedge.net │
│ ✅ HTTPS with managed certificate │
│ ✅ Automatic renewal │
└─────────────────────────────────────────────────────────────────┘
Security Layer¶
┌─────────────────────────────────────────────────────────────────┐
│ GitHub Secrets (Encrypted) │
│ │
│ AZURE_CREDENTIALS → Service Principal JSON │
│ AZURE_STORAGE_ACCOUNT → Storage account name │
│ AZURE_RESOURCE_GROUP → Resource group name │
│ AZURE_CDN_PROFILE → CDN profile (optional) │
│ AZURE_CDN_ENDPOINT → CDN endpoint (optional) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Azure Service Principal │
│ │
│ Role: Contributor │
│ Scope: /subscriptions/.../resourceGroups/nutri-e-rg │
│ Permissions: │
│ ✅ Read storage account │
│ ✅ Write blobs to $web container │
│ ✅ List containers │
│ ❌ Delete resources (restricted) │
│ ❌ Modify subscriptions (restricted) │
└─────────────────────────────────────────────────────────────────┘
Timeline¶
0s Developer pushes to GitHub
│
├─ 5s GitHub detects push, checks path filter
│
├─ 10s Workflow starts, Ubuntu runner spins up
│
├─ 15s Code checked out, Azure CLI installed
│
├─ 20s Azure login with service principal
│
├─ 25s Files uploaded to $web container
│
├─ 30s (Optional) CDN cache purged
│
└─ 30s ✅ Website live with updates!
File Structure¶
nutri-e/
├── .github/
│ └── workflows/
│ └── deploy-website.yml # GitHub Action workflow
│
├── website/ # Source files (deployed)
│ ├── index.html
│ ├── privacy.html
│ ├── terms.html
│ ├── 404.html
│ ├── styles.css
│ ├── script.js
│ └── images/
│
├── azure-setup.sh # Setup automation
├── AZURE_QUICKSTART.md # Quick start guide
├── AZURE_DEPLOYMENT.md # Full documentation
└── DEPLOYMENT_FLOW.md # This file
Monitoring¶
GitHub Actions¶
- View workflow runs:
https://github.com/Stig-Johnny/nutri-e/actions - Check logs for each step
- See deployment history
Azure Portal¶
- Storage metrics: Transactions, bandwidth, errors
- Access logs: Who accessed what and when
- Cost analysis: Daily/monthly spending
Azure CLI¶
# View recent deployments
az storage blob list \
--account-name nutriewebsite \
--container-name '$web' \
--query '[].{Name:name, LastModified:properties.lastModified}'
# Check storage metrics
az monitor metrics list \
--resource <storage-resource-id> \
--metric Transactions
Rollback Procedure¶
If something goes wrong:
Method 1: Revert GitHub Commit¶
Method 2: Redeploy Previous Version¶
git checkout <previous-commit>
git checkout -b hotfix
git push origin hotfix
# Manually trigger workflow for hotfix branch
Method 3: Manual Upload¶
az storage blob upload-batch \
--account-name nutriewebsite \
--source ./backup/website \
--destination '$web' \
--overwrite true
Disaster Recovery¶
Backup¶
- Azure Storage has built-in versioning (can be enabled)
- Git repository serves as source of truth
- All versions are in Git history
Recovery¶
- Delete corrupted storage account
- Run
./azure-setup.shto recreate - Push to main branch to redeploy
- Update GitHub secrets if needed
Performance Optimization¶
Without CDN¶
- Latency: 50-200ms (depending on user location)
- Throughput: Limited by Azure Storage limits
- Caching: Browser cache only
With CDN¶
- Latency: 10-50ms (served from edge locations)
- Throughput: Unlimited (CDN handles traffic)
- Caching: Edge + browser cache
- Cost: Lower (reduced bandwidth from storage)
Cost Breakdown¶
Basic Setup (Storage Only)¶
Storage: 0.5 GB × $0.02/GB = $0.01/month
Bandwidth: 10 GB × $0.08/GB = $0.80/month
Operations: 50K × $0.005/10K = $0.03/month
Total = $0.84/month
With CDN¶
Storage: 0.5 GB × $0.02/GB = $0.01/month
CDN Data: 10 GB × $0.06/GB = $0.60/month
Operations: 50K × $0.005/10K = $0.03/month
Total = $0.64/month
(Cheaper due to reduced storage egress!)
Troubleshooting Quick Reference¶
| Issue | Solution |
|---|---|
| GitHub Action fails authentication | Regenerate AZURE_CREDENTIALS |
| Website not updating | Check workflow ran, clear cache |
| 404 on all pages | Check files uploaded to $web |
| Slow performance | Enable CDN |
| High costs | Review bandwidth usage, enable CDN |
| Storage name taken | Use unique name in setup script |