Appearance
Deployment Architecture Guide
This document provides comprehensive guidance on deploying the Azure AI Travel Agents system across different environments, from local development to production Azure Container Apps deployments.
Table of Contents
- Deployment Overview
- Local Development Deployment
- Docker Compose Deployment
- Azure Container Apps Deployment
- Infrastructure as Code
- Monitoring and Observability
- Security Configuration
- Scaling and Performance
- Disaster Recovery
- Cost Optimization
Deployment Overview
Deployment Environments
| Environment | Purpose | Hosting | Complexity | Cost |
|---|---|---|---|---|
| Local Development | Individual development and testing | Local machine | Low | Free |
| Docker Compose | Team development and integration testing | Local/VM | Medium | Low |
| Azure Container Apps | Production and staging | Azure Cloud | High | Variable |
Architecture Components
Local Development Deployment
Prerequisites
bash
# Required software
node --version # v22.16.0+
npm --version # 10.0.0+
docker --version # 24.0.0+
git --version # 2.40.0+
# Windows users only
pwsh --version # 7.0.0+
# Azure tools
az --version # 2.60.0+
azd version # 1.9.0+Setup Process
1. Repository Setup
bash
# Clone repository
git clone https://github.com/Azure-Samples/azure-ai-travel-agents.git
cd azure-ai-travel-agents
# Verify repository structure
ls -la
# Expected: README.md, azure.yaml, src/, docs/, etc.2. Azure Authentication
bash
# Login to Azure
azd auth login
# Alternative for GitHub Codespaces
azd auth login --use-device-code
# Verify authentication
az account show3. Azure Resource Provisioning
bash
# Provision Azure resources
azd provision
# This creates:
# - Resource Group
# - Azure OpenAI Service
# - Azure Container Registry
# - Azure Container Apps Environment
# - Azure Monitor workspace4. Environment Configuration
bash
# Create local environment files
cp packages/api-*/.env.sample packages/api-*/.env
cp packages/ui-{framework}.env.sample packages/ui-{framework}.env
# Edit configuration (auto-populated by azd provision)
cat packages/api-*/.envExample .env configuration:
bash
# Azure Services
AZURE_OPENAI_ENDPOINT=https://your-openai.openai.azure.com/openai/v1/
AZURE_OPENAI_API_KEY=your-api-key
AZURE_OPENAI_DEPLOYMENT_NAME=gpt-5
# MCP Server URLs (local development)
MCP_CUSTOMER_QUERY_URL=http://localhost:5001
MCP_DESTINATION_RECOMMENDATION_URL=http://localhost:5002
MCP_ITINERARY_PLANNING_URL=http://localhost:5003
MCP_ECHO_PING_URL=http://localhost:5004
# Monitoring
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:18889
OTEL_SERVICE_NAME=api-langchain-js5. Local Service Startup
bash
# Terminal 1: Start Langchain.js API server
cd packages/api-langchain-js && npm install
npm start
# API available at http://localhost:4000
# Terminal 1: Start LlamaIndex.ts API server
cd packages/api-llamaindex-ts && npm install
npm start
# API available at http://localhost:4001
# Terminal 1: Start MAF Python API server
cd packages/api-{orchestrator}-{language} && pip install -e .
uvicorn src.main:app --reload --port 4010 --log-level=debug
# API available at http://localhost:4010
# Terminal 2: Start UI
cd packages/ui-{framework} && npm install
npm start
# UI available at http://localhost:4200
# Terminal 3: Start monitoring (optional)
docker run -d \
--name aspire-dashboard \
-p 18888:18888 \
-p 18889:18889 \
-e DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true \
mcr.microsoft.com/dotnet/aspire-dashboard:9.1
# Dashboard available at http://localhost:18888Development Workflow
Testing and Validation
bash
# Health check
curl http://localhost:4000/api/health
# Tool discovery
curl http://localhost:4000/api/tools
# Test chat (basic)
curl -X POST http://localhost:4000/api/chat \
-H "Content-Type: application/json" \
-d '{"message":"Hello","tools":[]}'Docker Compose Deployment
Architecture Overview
Docker Compose provides a complete multi-container environment that closely mirrors production:
yaml
# docker-compose.yml structure
services:
aspire-dashboard:
mcp-echo-ping:
mcp-customer-query:
mcp-destination-recommendation:
mcp-itinerary-planning:
api-langchain-js:
api-llamaindex-ts:
api-maf-python:
ui-angular:Deployment Process
1. Environment Preparation
bash
cd src
# Create Docker environment files
cp api-*/.env.sample api-*/.env.docker
cp ui-*/.env.sample ui-*/.env.docker
# Configure for Docker networking
cat > api-*/.env.docker << EOF
# MCP Server URLs (Docker internal)
MCP_ECHO_PING_URL=http://mcp-echo-ping:8080
MCP_CUSTOMER_QUERY_URL=http://mcp-customer-query:8080
MCP_DESTINATION_RECOMMENDATION_URL=http://mcp-destination-recommendation:8080
MCP_ITINERARY_PLANNING_URL=http://mcp-itinerary-planning:8000
# External services (from azd provision)
AZURE_OPENAI_ENDPOINT=https://your-openai.openai.azure.com/openai/v1/
AZURE_OPENAI_API_KEY=your-api-key
# Docker-specific settings
IS_LOCAL_DOCKER_ENV=true
OTEL_EXPORTER_OTLP_ENDPOINT=http://aspire-dashboard:18889
EOF
cat > ui/.env.docker << EOF
NG_API_URL=http://api-langchain-js:4000
EOF2. Service Deployment
bash
# Build and start all services
docker-compose up --build
# Background deployment
docker-compose up -d --build
# Check service status
docker-compose ps3. Service Access
bash
# Service endpoints
echo "Angular UI: http://localhost:4200"
echo "Langchain.js API: http://localhost:4000"
echo "LlamaIndex.ts API: http://localhost:4001"
echo "MAF Python API: http://localhost:4010"
echo "Monitoring: http://localhost:18888"
# MCP server endpoints
echo "Customer Query: http://localhost:5001"
echo "Destination Rec: http://localhost:5002"
echo "Itinerary Planning: http://localhost:5003"
echo "Echo Ping: http://localhost:5004"
# ... etcDocker Compose Management
Service Operations
bash
# View logs (examples)
docker-compose logs -f api-langchain-js
docker -compose logs -f ui-angular
docker-compose logs -f mcp-echo-ping
# Restart specific service
docker-compose restart api-langchain-js
# Scale services (if stateless)
docker-compose up -d --scale mcp-echo-ping=2
# Rebuild single service
docker-compose build api-langchain-js
docker-compose up -d api-langchain-jsResource Monitoring
bash
# Resource usage
docker stats
# Service health
docker-compose exec api-langchain-js curl http://localhost:4000/api/health
# Network inspection
docker network ls
docker network inspect src_defaultTroubleshooting
bash
# Debug container issues
docker-compose exec api-langchain-js /bin/sh
docker-compose exec mcp-echo-ping /bin/bash
# Check container logs
docker logs $(docker-compose ps -q api-langchain-js)
# Restart with fresh build
docker-compose down
docker-compose build --no-cache
docker-compose upAzure Container Apps Deployment
Infrastructure Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Resource Group │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Container Apps Environment ││
│ │ ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ ││
│ │ │ UI │ │ API │ │ MCP Services │ ││
│ │ │ (nginx) │ │ │ │ │ ││
│ │ │ │ │ │ │ ┌─────┬─────┬─────┐ │ ││
│ │ │ Replicas: │ │ Replicas: │ │ │Echo │CustQ│ ... │ │ ││
│ │ │ 1-3 │ │ 1-5 │ │ │Ping │ │ │ │ ││
│ │ │ │ │ │ │ └─────┴─────┴─────┘ │ ││
│ │ │ CPU: 0.25 │ │ CPU: 1.0 │ │ CPU: 0.5 each │ ││
│ │ │ RAM: 0.5Gi │ │ RAM: 2Gi │ │ RAM: 1Gi each │ ││
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Shared Services ││
│ │ ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ ││
│ │ │ Azure │ │ Container │ │ Monitor │ ││
│ │ │ OpenAI │ │ Registry │ │ (Logs, Metrics, │ ││
│ │ │ │ │ │ │ Traces) │ ││
│ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ ││
│ │ ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ ││
│ │ │ Key Vault │ │ Managed │ │ Storage Account │ ││
│ │ │ (Secrets) │ │ Identity │ │ (Logs, Config) │ ││
│ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘Deployment Process
1. Prerequisites Verification
bash
# Verify Azure CLI login
az account show
# Verify subscription access
az account list-locations -o table
# Check resource providers
az provider show --namespace Microsoft.App --query "registrationState"
az provider show --namespace Microsoft.ContainerRegistry --query "registrationState"
az provider show --namespace Microsoft.CognitiveServices --query "registrationState"2. Automated Deployment
bash
# Deploy using Azure Developer CLI
azd up
# This process:
# 1. Provisions infrastructure (Bicep templates)
# 2. Builds container images
# 3. Pushes to Azure Container Registry
# 4. Deploys to Container Apps
# 5. Configures networking and secrets3. Manual Deployment Steps (Alternative)
If you need to deploy manually or understand the process:
bash
# Step 1: Create resource group
az group create \
--name rg-travel-agents-prod \
--location swedencentral
# Step 2: Create Container Apps environment
az containerapp env create \
--name cae-travel-agents \
--resource-group rg-travel-agents-prod \
--location swedencentral \
--enable-workload-profiles
# Step 3: Create Azure Container Registry
az acr create \
--name acrTravelAgents \
--resource-group rg-travel-agents-prod \
--sku Standard \
--admin-enabled true
# Step 4: Build and push images
az acr build \
--registry acrTravelAgents \
--image travel-agents-ui-angular:latest \
--file packages/ui-{framework}Dockerfile \
packages/ui-{framework}
az acr build \
--registry acrTravelAgents \
--image travel-agents-api-langchain-js:latest \
--file packages/api-langchain-js/Dockerfile \
packages/api-langchain-js
az acr build \
--registry acrTravelAgents \
--image travel-agents-api-llamaindex-ts:latest \
--file packages/api-llamaindex-ts/Dockerfile \
packages/api-llamaindex-ts
az acr build \
--registry acrTravelAgents \
--image travel-agents-api-maf-python:latest \
--file packages/api-{orchestrator}-{language}/Dockerfile \
packages/api-{orchestrator}-{language}
az acr build \
--registry acrTravelAgents \
--image travel-agents-mcp-echo-ping:latest \
--file packages/mcp-servers/echo-ping/Dockerfile \
packages/mcp-servers/echo-ping
az acr build \
--registry acrTravelAgents \
--image travel-agents-mcp-customer-query:latest \
--file packages/mcp-servers/customer-query/Dockerfile \
packages/mcp-servers/customer-query
az acr build \
--registry acrTravelAgents \
--image travel-agents-mcp-destination-recommendation:latest \
--file packages/mcp-servers/destination-recommendation/Dockerfile \
packages/mcp-servers/destination-recommendation
# Step 5: Deploy container apps
az containerapp create \
--name ca-travel-ui \
--resource-group rg-travel-agents-prod \
--environment cae-travel-agents \
--image acrTravelAgents.azurecr.io/travel-agents-ui-angular:latest \
--target-port 4200 \
--ingress external \
--min-replicas 1 \
--max-replicas 3 \
--cpu 0.25 \
--memory 0.5GiContainer App Configuration
UI Container App
yaml
# Bicep template excerpt
resource uiApp 'Microsoft.App/containerApps@2023-05-01' = {
name: 'ca-travel-ui-angular'
location: location
properties: {
environmentId: containerAppsEnvironment.id
configuration: {
ingress: {
external: true
targetPort: 4200
allowInsecure: false
traffic: [
{
weight: 100
latestRevision: true
}
]
}
secrets: []
registries: [
{
server: containerRegistry.properties.loginServer
identity: managedIdentity.id
}
]
}
template: {
containers: [
{
name: 'travel-ui'
image: '${containerRegistry.properties.loginServer}/travel-agents-ui:latest'
resources: {
cpu: json('0.25')
memory: '0.5Gi'
}
env: [
{
name: 'NG_API_URL'
value: 'https://${apiLangchainJsApp.properties.configuration.ingress.fqdn}'
}
]
}
]
scale: {
minReplicas: 1
maxReplicas: 3
rules: [
{
name: 'http-scaling'
http: {
metadata: {
concurrentRequests: '100'
}
}
}
]
}
}
}
}API Container App
yaml
resource apiLangchainJsApp 'Microsoft.App/containerApps@2023-05-01' = {
name: 'ca-travel-api-langchain-js'
location: location
properties: {
environmentId: containerAppsEnvironment.id
configuration: {
ingress: {
external: true
targetPort: 4000
allowInsecure: false
corsPolicy: {
allowedOrigins: ['*']
allowedMethods: ['GET', 'POST', 'OPTIONS']
allowedHeaders: ['*']
}
}
secrets: [
{
name: 'azure-openai-key'
keyVaultUrl: '${keyVault.properties.vaultUri}secrets/azure-openai-key'
identity: managedIdentity.id
}
]
}
template: {
containers: [
{
name: 'travel-api-langchain-js'
image: '${containerRegistry.properties.loginServer}/travel-agents-api-langchain-js:latest'
resources: {
cpu: json('1.0')
memory: '2Gi'
}
env: [
{
name: 'AZURE_OPENAI_ENDPOINT'
value: openAIService.properties.endpoint
}
{
name: 'AZURE_OPENAI_API_KEY'
secretRef: 'azure-openai-key'
}
// MCP server URLs
{
name: 'MCP_ECHO_PING_URL'
value: 'https://${echoMcpApp.properties.configuration.ingress.fqdn}'
}
// Additional environment variables...
]
}
]
scale: {
minReplicas: 1
maxReplicas: 10
rules: [
{
name: 'http-scaling'
http: {
metadata: {
concurrentRequests: '50'
}
}
}
{
name: 'cpu-scaling'
custom: {
type: 'cpu'
metadata: {
type: 'Utilization'
value: '70'
}
}
}
]
}
}
}
}Deployment Monitoring
Deployment Status
bash
# Check deployment status
azd show
# Monitor container app status
az containerapp show \
--name ca-travel-api-langchain-js \
--resource-group rg-travel-agents-prod \
--query "properties.provisioningState"
# View recent revisions
az containerapp revision list \
--name ca-travel-api-langchain-js \
--resource-group rg-travel-agents-prod \
--query "[].{Name:name,Active:properties.active,CreatedTime:properties.createdTime}"Application Logs
bash
# Stream API logs
az containerapp logs show \
--name ca-travel-api-langchain-js \
--resource-group rg-travel-agents-prod \
--follow
# View specific revision logs
az containerapp logs show \
--name ca-travel-api-langchain-js \
--resource-group rg-travel-agents-prod \
--revision-name "ca-travel-api-langchain-js--xyz123"Infrastructure as Code
Bicep Templates Structure
infra/
├── main.bicep # Main template
├── modules/
│ ├── container-apps.bicep # Container Apps configuration
│ ├── monitoring.bicep # Azure Monitor setup
│ ├── security.bicep # Key Vault, Managed Identity
│ └── ai-services.bicep # OpenAI, Cognitive Services
├── parameters/
│ ├── dev.bicepparam # Development parameters
│ ├── staging.bicepparam # Staging parameters
│ └── prod.bicepparam # Production parameters
└── scripts/
├── deploy.sh # Deployment scripts
└── cleanup.sh # Cleanup scriptsMain Bicep Template
bicep
// infra/main.bicep
@description('Environment name (dev, staging, prod)')
param environment string = 'dev'
@description('Primary location for resources')
param location string = resourceGroup().location
@description('Application name prefix')
param appName string = 'travel-agents'
// Variables
var containerAppName = '${appName}-${environment}'
var resourcePrefix = 'rg-${appName}-${environment}'
// Modules
module containerApps 'modules/container-apps.bicep' = {
name: 'containerApps'
params: {
location: location
environment: environment
appName: appName
containerRegistryName: containerRegistry.outputs.name
keyVaultName: security.outputs.keyVaultName
managedIdentityId: security.outputs.managedIdentityId
}
}
module monitoring 'modules/monitoring.bicep' = {
name: 'monitoring'
params: {
location: location
environment: environment
appName: appName
}
}
module security 'modules/security.bicep' = {
name: 'security'
params: {
location: location
environment: environment
appName: appName
}
}
module aiServices 'modules/ai-services.bicep' = {
name: 'aiServices'
params: {
location: location
environment: environment
appName: appName
keyVaultName: security.outputs.keyVaultName
}
}
// Container Registry
resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-07-01' = {
name: 'acr${appName}${environment}${uniqueString(resourceGroup().id)}'
location: location
sku: {
name: environment == 'prod' ? 'Premium' : 'Standard'
}
properties: {
adminUserEnabled: false
policies: {
trustPolicy: {
type: 'Notary'
status: 'enabled'
}
retentionPolicy: {
status: 'enabled'
days: environment == 'prod' ? 30 : 7
}
}
}
}
// Outputs
output containerAppsEnvironmentFqdn string = containerApps.outputs.environmentFqdn
output uiAppUrl string = containerApps.outputs.uiAppUrl
output apiAppUrl string = containerApps.outputs.apiAppUrlContainer Apps Module
bicep
// infra/modules/container-apps.bicep
@description('Location for resources')
param location string
@description('Environment name')
param environment string
@description('Application name')
param appName string
@description('Container registry name')
param containerRegistryName string
@description('Key Vault name')
param keyVaultName string
@description('Managed identity resource ID')
param managedIdentityId string
// Container Apps Environment
resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
name: 'cae-${appName}-${environment}'
location: location
properties: {
appLogsConfiguration: {
destination: 'log-analytics'
logAnalyticsConfiguration: {
customerId: logAnalyticsWorkspace.properties.customerId
sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
}
}
zoneRedundant: environment == 'prod'
workloadProfiles: [
{
name: 'Consumption'
workloadProfileType: 'Consumption'
}
{
name: 'Dedicated-D4'
workloadProfileType: 'D4'
minimumCount: environment == 'prod' ? 1 : 0
maximumCount: 3
}
]
}
}
// UI Container App
resource uiApp 'Microsoft.App/containerApps@2023-05-01' = {
name: 'ca-${appName}-ui-${environment}'
location: location
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${managedIdentityId}': {}
}
}
properties: {
environmentId: containerAppsEnvironment.id
workloadProfileName: 'Consumption'
configuration: {
ingress: {
external: true
targetPort: 4200
allowInsecure: false
traffic: [
{
weight: 100
latestRevision: true
}
]
}
registries: [
{
server: '${containerRegistryName}.azurecr.io'
identity: managedIdentityId
}
]
}
template: {
containers: [
{
name: 'ui'
image: '${containerRegistryName}.azurecr.io/${appName}-ui:latest'
resources: {
cpu: json('0.25')
memory: '0.5Gi'
}
env: [
{
name: 'NG_API_URL'
value: 'https://${apiApp.properties.configuration.ingress.fqdn}'
}
]
}
]
scale: {
minReplicas: environment == 'prod' ? 2 : 1
maxReplicas: environment == 'prod' ? 10 : 3
rules: [
{
name: 'http-scaling'
http: {
metadata: {
concurrentRequests: '100'
}
}
}
]
}
}
}
}
// API Container App (similar structure)
resource apiApp 'Microsoft.App/containerApps@2023-05-01' = {
// ... detailed configuration
}
// MCP Server Container Apps
resource mcpServers 'Microsoft.App/containerApps@2023-05-01' = [for server in mcpServerConfigs: {
name: 'ca-${appName}-${server.name}-${environment}'
location: location
// ... configuration for each MCP server
}]
// Outputs
output environmentFqdn string = containerAppsEnvironment.properties.defaultDomain
output uiAppUrl string = 'https://${uiApp.properties.configuration.ingress.fqdn}'
output apiAppUrl string = 'https://${apiApp.properties.configuration.ingress.fqdn}'Deployment Scripts
bash
#!/bin/bash
# infra/scripts/deploy.sh
set -e
# Configuration
ENVIRONMENT=${1:-dev}
LOCATION=${2:-swedencentral}
RESOURCE_GROUP="rg-travel-agents-${ENVIRONMENT}"
echo "Deploying Azure AI Travel Agents to ${ENVIRONMENT} environment..."
# Create resource group if it doesn't exist
az group create \
--name $RESOURCE_GROUP \
--location $LOCATION
# Deploy infrastructure
echo "Deploying infrastructure..."
az deployment group create \
--resource-group $RESOURCE_GROUP \
--template-file infra/main.bicep \
--parameters infra/parameters/${ENVIRONMENT}.bicepparam \
--verbose
# Get Container Registry name
ACR_NAME=$(az deployment group show \
--resource-group $RESOURCE_GROUP \
--name main \
--query "properties.outputs.containerRegistryName.value" \
--output tsv)
# Build and push container images
echo "Building and pushing container images..."
# UI Image
az acr build \
--registry $ACR_NAME \
--image travel-agents-ui:latest \
--file packages/ui-{framework}Dockerfile \
packages/ui-{framework}
# API Image
az acr build \
--registry $ACR_NAME \
--image travel-agents-api:latest \
--file packages/api-{orchestrator}-{language}/Dockerfile or packages/api-{orchestrator}-{language}/Dockerfile \
packages/api-{orchestrator}-{language} or packages/api-{orchestrator}-{language}
# MCP Server Images
for server in echo-ping customer-query destination-recommendation itinerary-planning; do
echo "Building MCP server: $server"
az acr build \
--registry $ACR_NAME \
--image travel-agents-mcp-${server}:latest \
--file packages/mcp-servers/${server}/Dockerfile \
packages/mcp-servers/${server}
done
# Update container app revisions
echo "Updating container app revisions..."
az containerapp update \
--name ca-travel-agents-ui-${ENVIRONMENT} \
--resource-group $RESOURCE_GROUP \
--image ${ACR_NAME}.azurecr.io/travel-agents-ui:latest
az containerapp update \
--name ca-travel-agents-api-${ENVIRONMENT} \
--resource-group $RESOURCE_GROUP \
--image ${ACR_NAME}.azurecr.io/travel-agents-api:latest
echo "Deployment completed successfully!"
# Display application URLs
UI_URL=$(az containerapp show \
--name ca-travel-agents-ui-${ENVIRONMENT} \
--resource-group $RESOURCE_GROUP \
--query "properties.configuration.ingress.fqdn" \
--output tsv)
API_URL=$(az containerapp show \
--name ca-travel-agents-api-${ENVIRONMENT} \
--resource-group $RESOURCE_GROUP \
--query "properties.configuration.ingress.fqdn" \
--output tsv)
echo ""
echo "Application URLs:"
echo "UI: https://${UI_URL}"
echo "API: https://${API_URL}"
echo ""Monitoring and Observability
Azure Monitor Integration
bicep
// infra/modules/monitoring.bicep
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: 'law-${appName}-${environment}'
location: location
properties: {
sku: {
name: 'PerGB2018'
}
retentionInDays: environment == 'prod' ? 90 : 30
features: {
enableLogAccessUsingOnlyResourcePermissions: true
}
}
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
name: 'ai-${appName}-${environment}'
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalyticsWorkspace.id
IngestionMode: 'LogAnalytics'
}
}
// Alerts
resource responseTimeAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = {
name: 'High Response Time Alert'
location: 'global'
properties: {
description: 'Alert when average response time exceeds 5 seconds'
severity: 2
enabled: true
scopes: [
apiContainerApp.id
]
evaluationFrequency: 'PT1M'
windowSize: 'PT5M'
criteria: {
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
allOf: [
{
name: 'HighResponseTime'
metricName: 'RequestDuration'
operator: 'GreaterThan'
threshold: 5000
timeAggregation: 'Average'
}
]
}
}
}Custom Dashboards
json
{
"dashboard": {
"title": "Azure AI Travel Agents - Production",
"tiles": [
{
"position": { "x": 0, "y": 0, "colSpan": 6, "rowSpan": 4 },
"metadata": {
"type": "Blade",
"inputs": [
{
"name": "query",
"value": "ContainerAppConsoleLogs_CL | where ContainerAppName_s contains 'travel-agents-api' | where TimeGenerated > ago(1h) | summarize count() by bin(TimeGenerated, 5m)"
}
]
}
},
{
"position": { "x": 6, "y": 0, "colSpan": 6, "rowSpan": 4 },
"metadata": {
"type": "Blade",
"inputs": [
{
"name": "query",
"value": "requests | where name contains 'chat' | summarize avg(duration) by bin(timestamp, 5m)"
}
]
}
}
]
}
}Security Configuration
Managed Identity Setup
bicep
// Managed identity for container apps
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: 'mi-${appName}-${environment}'
location: location
}
// Key Vault access policy
resource keyVaultAccessPolicy 'Microsoft.KeyVault/vaults/accessPolicies@2023-07-01' = {
name: 'add'
parent: keyVault
properties: {
accessPolicies: [
{
tenantId: tenant().tenantId
objectId: managedIdentity.properties.principalId
permissions: {
secrets: ['get', 'list']
}
}
]
}
}
// Container Registry access
resource containerRegistryAccess 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(containerRegistry.id, managedIdentity.id, 'AcrPull')
scope: containerRegistry
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') // AcrPull
principalId: managedIdentity.properties.principalId
principalType: 'ServicePrincipal'
}
}Network Security
bicep
// Virtual network for secure communication
resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-05-01' = if (environment == 'prod') {
name: 'vnet-${appName}-${environment}'
location: location
properties: {
addressSpace: {
addressPrefixes: ['10.0.0.0/16']
}
subnets: [
{
name: 'container-apps-subnet'
properties: {
addressPrefix: '10.0.1.0/24'
delegations: [
{
name: 'Microsoft.App.environments'
properties: {
serviceName: 'Microsoft.App/environments'
}
}
]
}
}
]
}
}
// Container Apps Environment with VNet integration
resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
name: 'cae-${appName}-${environment}'
location: location
properties: {
vnetConfiguration: environment == 'prod' ? {
infrastructureSubnetId: virtualNetwork.properties.subnets[0].id
internal: false
} : null
// ... other properties
}
}Scaling and Performance
Horizontal Pod Autoscaling
bicep
// API scaling configuration
template: {
scale: {
minReplicas: environment == 'prod' ? 2 : 1
maxReplicas: environment == 'prod' ? 20 : 5
rules: [
{
name: 'http-scaling-rule'
http: {
metadata: {
concurrentRequests: '50'
}
}
}
{
name: 'cpu-scaling-rule'
custom: {
type: 'cpu'
metadata: {
type: 'Utilization'
value: '70'
}
}
}
{
name: 'memory-scaling-rule'
custom: {
type: 'memory'
metadata: {
type: 'Utilization'
value: '80'
}
}
}
]
}
}Performance Optimization
bash
# Resource allocation per environment
ENVIRONMENT_CONFIGS = {
"dev": {
"ui": {"cpu": "0.25", "memory": "0.5Gi", "replicas": {"min": 1, "max": 2}},
"api": {"cpu": "0.5", "memory": "1Gi", "replicas": {"min": 1, "max": 3}},
"mcp": {"cpu": "0.25", "memory": "0.5Gi", "replicas": {"min": 1, "max": 2}}
},
"staging": {
"ui": {"cpu": "0.5", "memory": "1Gi", "replicas": {"min": 1, "max": 5}},
"api": {"cpu": "1.0", "memory": "2Gi", "replicas": {"min": 2, "max": 8}},
"mcp": {"cpu": "0.5", "memory": "1Gi", "replicas": {"min": 1, "max": 3}}
},
"prod": {
"ui": {"cpu": "0.5", "memory": "1Gi", "replicas": {"min": 2, "max": 10}},
"api": {"cpu": "2.0", "memory": "4Gi", "replicas": {"min": 3, "max": 20}},
"mcp": {"cpu": "1.0", "memory": "2Gi", "replicas": {"min": 2, "max": 5}}
}
}Disaster Recovery
Backup Strategy
bash
#!/bin/bash
# Backup script for critical configurations
# Backup Container Registry images
az acr import \
--name $BACKUP_ACR \
--source $PRIMARY_ACR.azurecr.io/travel-agents-api:latest \
--image travel-agents-api:backup-$(date +%Y%m%d)
# Backup Key Vault secrets
az keyvault secret backup \
--vault-name $KEY_VAULT_NAME \
--name azure-openai-key \
--file backup-secrets-$(date +%Y%m%d).blob
# Backup Application Insights data (export)
az monitor app-insights component export create \
--app $APP_INSIGHTS_NAME \
--resource-group $RESOURCE_GROUP \
--destination-type Blob \
--destination-address $BACKUP_STORAGE_URLMulti-Region Deployment
bicep
// Primary region deployment
module primaryRegion 'main.bicep' = {
name: 'primaryRegion'
params: {
location: 'swedencentral'
environment: environment
isPrimary: true
}
}
// Secondary region deployment (disaster recovery)
module secondaryRegion 'main.bicep' = if (environment == 'prod') {
name: 'secondaryRegion'
params: {
location: 'northeurope'
environment: environment
isPrimary: false
primaryResourceGroup: primaryRegion.outputs.resourceGroupName
}
}
// Traffic Manager for failover
resource trafficManager 'Microsoft.Network/trafficManagerProfiles@2022-04-01' = if (environment == 'prod') {
name: 'tm-${appName}-${environment}'
location: 'global'
properties: {
profileStatus: 'Enabled'
trafficRoutingMethod: 'Priority'
endpoints: [
{
name: 'primary'
type: 'externalEndpoints'
properties: {
target: primaryRegion.outputs.uiAppFqdn
priority: 1
endpointStatus: 'Enabled'
}
}
{
name: 'secondary'
type: 'externalEndpoints'
properties: {
target: secondaryRegion.outputs.uiAppFqdn
priority: 2
endpointStatus: 'Enabled'
}
}
]
}
}Cost Optimization
Cost Analysis
bash
# Get current month costs
az consumption usage list \
--start-date $(date -d "$(date +%Y-%m-01)" +%Y-%m-%d) \
--end-date $(date +%Y-%m-%d) \
--query "[?contains(instanceName, 'travel-agents')].{Service:meterName,Cost:pretaxCost,Usage:quantity}" \
--output table
# Set up budget alerts
az consumption budget create \
--budget-name "travel-agents-monthly-budget" \
--amount 500 \
--time-grain Monthly \
--time-period start-date=$(date +%Y-%m-01) \
--notifications \
'[{
"enabled": true,
"operator": "GreaterThan",
"threshold": 80,
"contactEmails": ["admin@example.com"],
"contactRoles": ["Owner"]
}]'Cost-Saving Configurations
bicep
// Environment-specific cost optimizations
var costOptimizations = {
dev: {
// Use consumption pricing for all services
containerAppsWorkloadProfile: 'Consumption'
logRetentionDays: 7
autoShutdown: true
}
staging: {
containerAppsWorkloadProfile: 'Consumption'
logRetentionDays: 30
autoShutdown: false
}
prod: {
containerAppsWorkloadProfile: 'Dedicated-D4'
logRetentionDays: 90
autoShutdown: false
}
}
// Auto-shutdown for dev environment
resource autoShutdownScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = if (environment == 'dev') {
name: 'auto-shutdown-${environment}'
location: location
kind: 'AzurePowerShell'
properties: {
azPowerShellVersion: '9.0'
scriptContent: '''
# Scale down to 0 replicas during off-hours (evenings and weekends)
$resourceGroup = $env:AZURE_RESOURCE_GROUP
$containerApps = az containerapp list --resource-group $resourceGroup --query "[].name" -o tsv
foreach ($app in $containerApps) {
az containerapp update --name $app --resource-group $resourceGroup --min-replicas 0
}
'''
retentionInterval: 'P1D'
}
}This comprehensive deployment architecture guide provides the complete framework for deploying the Azure AI Travel Agents system across different environments, from local development to production-ready Azure Container Apps deployments.
