Module 3 60 minutes

Module 3: Application Deployment

Deploy the Flask application to Azure Container Apps

0%

Module 3: Application Deployment

πŸš€ Look at you go! Two modules down, and you've already got Azure infrastructure running. Time to put some actual application code on those fancy resources you just created! πŸ’ͺ

Duration: 60 minutes

Objective: Understand the Flask application code, containerization process, and deployment to Azure Container Apps.


🎯 Learning Objectives

By the end of this module, you will:

  • Understand the Flask application structure and key files
  • Explore the frontend HTML/CSS/JavaScript implementation
  • Learn how Docker containerization works (without needing Docker Desktop!)
  • Deploy the application using azd cloud builds
  • Access and test the deployed web application

οΏ½ Prerequisites Check

Before starting, ensure Module 2 is complete:

  • βœ… Azure infrastructure deployed via azd up
  • βœ… Resource group created with Container App, Registry, Storage
  • βœ… Application endpoint URL available
  • βœ… No build or deployment errors

Quick verification:

# Check deployment status
azd show

# Verify your app URL is accessible
# Example: https://ca-cora-dev.happyocean-a1b2c3d4.eastus.azurecontainerapps.io

πŸ—οΈ Understanding the Application Architecture

CORA is a Flask-based web application with these key components:

Application Files

File Purpose Lines of Code
app.py Flask web server, routes, authentication ~500
agent.py AI agent integration with Azure OpenAI ~200
storage_service.py Azure Table Storage operations ~150
config.py Environment configuration management ~50
auth.py Azure AD authentication (optional) ~100

Frontend Files

File Purpose
templates/index.html Main UI structure, conversation interface
templates/landing.html Landing page with auth
static/css/style.css Styling, themes, animations
static/js/app.js Real-time chat, speech recognition, analytics

Infrastructure Files (Already Deployed!)

File Purpose
infra/main.bicep Main infrastructure template
infra/core/* Reusable Bicep modules
azure.yaml azd configuration
Dockerfile Container build instructions

πŸ” Step 1: Explore the Application Code

Let’s understand what you just deployed!

The Flask Web Server (app.py)

Open app.py in your code editor. Here are the key routes:

1. Home Route - Serves the Main UI

@app.route('/')
def index():
    """Main conversation interface"""
    return render_template('index.html')

What it does: Loads the chat interface where users interact with CORA.

2. Chat Route - Processes Voice Agent Requests

@app.route('/chat', methods=['POST'])
def chat():
    """Process chat message and return AI response"""
    data = request.json
    user_message = data.get('message', '')
    conversation_id = data.get('conversation_id')
    
    # Call AI agent
    response = voice_agent.process_message(user_message, conversation_id)
    
    # Store interaction in Azure Table Storage
    storage_service.save_conversation(conversation_id, user_message, response)
    
    return jsonify(response)

What it does:

  1. Receives user message from frontend
  2. Sends message to Azure OpenAI via agent
  3. Saves conversation score to Table Storage
  4. Returns AI response to user

3. Analytics Route - Fetches Conversation Data

@app.route('/api/analytics')
def get_analytics():
    """Return conversation metrics for charts"""
    conversations = storage_service.get_recent_conversations(days=30)
    return jsonify(conversations)

What it does: Powers the analytics dashboard with historical data.


The AI Agent (agent.py)

This file handles communication with Azure OpenAI:

Key Functions:

class VoiceAgent:
    def __init__(self):
        # Initialize Azure OpenAI client
        self.client = AzureOpenAI(
            azure_endpoint=os.getenv('AZURE_OPENAI_ENDPOINT'),
            api_key=managed_identity_credential,  # Uses Managed Identity!
            api_version='2024-08-01-preview'
        )
    
    def process_message(self, user_message, conversation_id):
        # Send to GPT-4o model
        response = self.client.chat.completions.create(
            model=os.getenv('AZURE_OPENAI_DEPLOYMENT_NAME'),
            messages=[
                {"role": "system", "content": "You are CORA, a helpful voice agent..."},
                {"role": "user", "content": user_message}
            ]
        )
        
        # Score the conversation quality (1-5)
        score = self.evaluate_response(response)
        
        return {
            'response': response.choices[0].message.content,
            'score': score,
            'conversation_id': conversation_id
        }

πŸ”‘ Key Insight: Notice there’s no API key in the code! The application uses Managed Identity to authenticate with Azure OpenAI. This is a security best practice.


Storage Service (storage_service.py)

Handles saving and retrieving conversation data from Azure Table Storage:

class StorageService:
    def __init__(self):
        # Connect to Azure Table Storage using Managed Identity
        self.table_client = TableClient.from_table_url(
            table_url=f"{storage_account_url}/conversations",
            credential=DefaultAzureCredential()
        )
    
    def save_conversation(self, conversation_id, user_message, ai_response, score):
        """Save conversation to Table Storage"""
        entity = {
            'PartitionKey': conversation_id,
            'RowKey': str(uuid.uuid4()),
            'Timestamp': datetime.utcnow(),
            'UserMessage': user_message,
            'AIResponse': ai_response,
            'Score': score
        }
        self.table_client.create_entity(entity)

Why Table Storage?

  • Cost-effective (~$0.045 per 10,000 transactions)
  • NoSQL flexibility
  • Perfect for conversation logs
  • Native Azure integration

🎨 Step 2: Explore the Frontend Implementation

The Main UI (templates/index.html)

Open templates/index.html and find these key sections:

Conversation Interface

<div class="chat-container">
    <div id="messages" class="messages-list">
        <!-- Messages appear here -->
    </div>
    
    <div class="input-area">
        <button id="voiceBtn" class="voice-button">
            🎀 Start Voice
        </button>
        <input type="text" id="messageInput" placeholder="Type a message...">
        <button id="sendBtn">Send</button>
    </div>
</div>

Analytics Dashboard

<div class="analytics-section">
    <canvas id="scoreChart"></canvas>
    <canvas id="volumeChart"></canvas>
    <div id="recentConversations"></div>
</div>

What makes it work: Chart.js for visualizations, AJAX for real-time updates.


Voice Recognition (static/js/app.js)

🎀 Important: This app uses the Web Speech API (browser native), NOT Azure Cognitive Services.

Speech-to-Text Implementation

const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
recognition.continuous = false;
recognition.interimResults = false;
recognition.lang = 'en-US';

recognition.onresult = (event) => {
    const transcript = event.results[0][0].transcript;
    sendMessage(transcript);
};

voiceButton.addEventListener('click', () => {
    recognition.start();
});

Text-to-Speech Implementation

const synth = window.speechSynthesis;

function speakResponse(text) {
    const utterance = new SpeechSynthesisUtterance(text);
    utterance.voice = synth.getVoices()[0];
    utterance.rate = 1.0;
    utterance.pitch = 1.0;
    synth.speak(utterance);
}

Platform Support:

Platform Speech Recognition Speech Synthesis
Desktop Chrome/Edge βœ… Full support βœ… Full support
Desktop Safari βœ… Full support βœ… Full support
Mobile browsers ❌ Not available βœ… Works

Why not Azure Speech Services?

  • Web Speech API is free and built into browsers
  • No additional Azure costs
  • Perfect for training and prototyping
  • Great for desktop use cases

When to use Azure Speech:

  • Mobile app requirements
  • Advanced features (custom models, speaker recognition)
  • Batch transcription
  • Multi-language support beyond browser capabilities

🐳 Step 3: Understanding Containerization

The Dockerfile

Your application was containerized using this Dockerfile:

FROM python:3.11-slim

WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# Copy requirements and install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Expose port
EXPOSE 5000

# Set environment variables
ENV PYTHONUNBUFFERED=1
ENV FLASK_APP=app.py

# Run the application
CMD ["python", "app.py"]

What Each Line Does

Line Purpose
FROM python:3.11-slim Start with lightweight Python base image
WORKDIR /app Set working directory in container
RUN apt-get... Install system-level dependencies (gcc for some Python packages)
COPY requirements.txt . Copy dependency list
RUN pip install... Install Python packages
COPY . . Copy all application code
EXPOSE 5000 Document that app listens on port 5000
CMD ["python", "app.py"] Command to run when container starts

azd Cloud Builds: No Docker Desktop Needed!

Here’s the magic: You don’t need Docker installed locally!

When you ran azd up, here’s what happened:

  1. πŸ“¦ azd packages your code β†’ Creates a build archive
  2. ☁️ Uploads to Azure β†’ Sends code to Azure Container Registry
  3. πŸ—οΈ Cloud build β†’ ACR builds the Docker image in Azure
  4. πŸš€ Deploy β†’ Container App pulls and runs the image

Benefits:

  • βœ… No Docker Desktop installation
  • βœ… Consistent builds (same environment every time)
  • βœ… Faster on slow machines (cloud does the work)
  • βœ… Built-in registry integration

πŸš€ Step 4: Deploy Application Updates

Your app is already deployed from Module 2! But let’s understand how to update it.

Scenario: You Made Code Changes

Let’s say you edited app.py to change CORA’s greeting. Here’s how to deploy:

# Navigate to project directory
cd c:\Local Dev\Cora-Voice-Agent-Training

# Deploy only the application (skip infrastructure)
azd deploy

What Happens During azd deploy

Deploying services (azd deploy)

  (βœ“) Done: Packaging service 'web'
  (βœ“) Done: Building container image in Azure
  (βœ“) Done: Pushing to Azure Container Registry
  (βœ“) Done: Deploying to Container App

SUCCESS: Service 'web' deployed successfully!

Endpoint: https://ca-cora-dev.happyocean-a1b2c3d4.eastus.azurecontainerapps.io

Time: 2-3 minutes for code-only deployments

Deploy vs Provision vs Up

Command What It Does When to Use
azd up Provision + Package + Deploy (everything) First deployment or infrastructure changes
azd provision Only create/update Azure resources After editing Bicep files
azd deploy Only build and deploy application code After editing Python/HTML/JS

πŸ’‘ Pro Tip: Use azd deploy for fast iterations during development!


βœ… Step 5: Access and Test Your Application

1. Get Your Application URL

azd show

Look for output like:

Service: web
  Endpoint: https://ca-cora-dev.happyocean-a1b2c3d4.eastus.azurecontainerapps.io
  Status: Running
  Replicas: 1

2. Open in Browser

Copy the endpoint URL and open it in your browser.

Expected: CORA landing page with:

  • CORA Customer Omnichannel Response Agent title
  • About Cora button
  • Getting Started button
  • New Conversation button
  • Clean, modern interface

3. Test the Voice Agent

Before You Start: Configure CORA’s personality and voice!

Mood Selection:

  • Choose from: Neutral, Happy, Frustrated, Confused, Angry, Helpful
  • Each mood changes how CORA responds as a customer
  • Try different moods to see varied conversation styles

Voice Selection:

  • Default: Random (CORA picks a different voice each time)
  • Have fun! Try other voices from the dropdown
  • Each voice has unique characteristics

Voice Quality Settings:

  • Adjust speech rate, pitch, and volume
  • Experiment to find your preferred settings

Test Scenario 1: Text Chat

  1. Select a Mood (try β€œFrustrated” for interesting responses!)
  2. Click in the text input box
  3. Type: β€œHello CORA, how are you today?”
  4. Click Send
  5. Expected: CORA responds as a customer with the selected mood

Test Scenario 2: Voice Input (Desktop only)

  1. Select a Mood and Voice (or leave Random selected)
  2. Ensure β€œAuto-Play Cora’s responses” checkbox is checked (bottom right)
  3. Click β€œNew Conversation” button
  4. Click β€œEnable Voice Chat” button
  5. Allow microphone access when browser prompts
  6. Speak: β€œHow can I help you today?”
  7. Expected:
    • Your speech is transcribed to text
    • CORA responds as a customer with the selected mood
    • Response is spoken aloud automatically in the selected voice

Test Scenario 3: Analytics Dashboard

  1. Have a few conversations (3-5 messages)
  2. Click β€œEnd Conversation” button
  3. Click the β€œAnalytics” tab
  4. Expected:
    • Score chart showing conversation ratings (1-5)
    • Volume chart showing message count over time
    • Recent conversations list

πŸ› Troubleshooting Deployment Issues

Issue 1: Container App Shows β€œApplication Error”

Symptoms: URL loads but shows error page

Diagnosis:

# View application logs
az containerapp logs show \
  --name ca-cora-dev \
  --resource-group rg-cora-dev \
  --follow

Common Causes:

  • Missing environment variables (check AZURE_OPENAI_ENDPOINT)
  • AI Foundry model not deployed
  • Managed Identity permissions not set

Fix:

# Verify environment variables
azd env get-values

# Redeploy with correct values
azd env set AZURE_OPENAI_ENDPOINT "https://your-endpoint.openai.azure.com/"
azd deploy

Issue 2: Voice Recognition Not Working

Symptoms: β€œEnable Voice Chat” button does nothing or microphone not activating

Diagnosis: Check browser console (F12 β†’ Console tab)

Common Causes:

Cause Solution
Mobile browser Use text input (voice not supported on mobile)
HTTP (not HTTPS) Web Speech API requires HTTPS (Container Apps provides this)
Microphone permission denied Reload page and allow microphone access
Browser not supported Use Chrome, Edge, or Safari

Fix: Use desktop browser with microphone permissions enabled.


Issue 3: Analytics Shows No Data

Symptoms: Charts are empty even after conversations

Diagnosis:

# Check Table Storage connection
az storage table list \
  --account-name <your-storage-account> \
  --auth-mode login

Common Causes:

  • Storage Account connection string not set
  • Managed Identity doesn’t have Storage permissions
  • Conversations not being saved due to API errors

Fix:

# Verify Managed Identity has Storage permissions
az role assignment list \
  --assignee <managed-identity-principal-id> \
  --resource-group rg-cora-dev

The identity should have β€œStorage Table Data Contributor” role.


🎯 Understanding the Deployment Architecture

What’s Running Where?

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Azure Container Apps Environment      β”‚
β”‚                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Container App: ca-cora-dev      β”‚  β”‚
β”‚  β”‚                                   β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚  β”‚
β”‚  β”‚  β”‚  Your Docker Container    β”‚   β”‚  β”‚
β”‚  β”‚  β”‚  - Flask app on port 5000 β”‚   β”‚  β”‚
β”‚  β”‚  β”‚  - Python 3.11            β”‚   β”‚  β”‚
β”‚  β”‚  β”‚  - All dependencies       β”‚   β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚  β”‚
β”‚  β”‚                                   β”‚  β”‚
β”‚  β”‚  Managed Identity ────────────►  β”‚  β”‚
β”‚  β”‚  (No API keys needed!)           β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚               β”‚
           β”‚               β”‚
           β–Ό               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ Azure    β”‚    β”‚ Azure Table  β”‚
    β”‚ OpenAI   β”‚    β”‚ Storage      β”‚
    β”‚ (GPT-4o) β”‚    β”‚ (Analytics)  β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Concepts

Container Apps Environment:

  • Kubernetes-based (but you don’t manage Kubernetes!)
  • Automatic HTTPS
  • Built-in load balancing
  • Scale-to-zero capability

Your Container:

  • Pulled from Azure Container Registry
  • Runs your Flask app
  • Auto-restarts if it crashes
  • Logs to Log Analytics

Managed Identity:

  • Automatically created by azd
  • Grants permissions to your app
  • No API keys to manage
  • More secure than connection strings

πŸ“Š Monitoring Your Deployment

View Real-Time Logs

# Stream logs from your container
az containerapp logs show \
  --name ca-cora-dev \
  --resource-group rg-cora-dev \
  --follow

What to look for:

  • βœ“ Azure Monitor OpenTelemetry configured - Monitoring working
  • βœ“ Connected to Azure OpenAI - AI integration working
  • βœ“ Table Storage initialized - Analytics storage ready

Check Application Metrics

  1. Go to Azure Portal
  2. Navigate to your Container App (ca-cora-dev)
  3. Click β€œMetrics” in the left menu

Useful Metrics:

  • Replica Count: How many instances are running
  • CPU Usage: Performance monitoring
  • Memory Usage: Resource consumption
  • HTTP Request Count: Traffic volume

πŸŽ“ What You’ve Learned

By completing Module 3, you now understand:

  • βœ… Flask application structure - Routes, templates, static files
  • βœ… AI agent integration - Azure OpenAI communication via Managed Identity
  • βœ… Frontend development - HTML, CSS, JavaScript with Web Speech API
  • βœ… Containerization - Dockerfile basics and cloud builds
  • βœ… Azure Container Apps - Serverless container hosting
  • βœ… Deployment workflow - azd deploy for rapid iterations
  • βœ… Monitoring and troubleshooting - Logs, metrics, and debugging

πŸš€ Next Steps

Ready to dive deeper into AI? Module 4 covers:

  • Azure AI Foundry project configuration
  • Model deployment and management
  • Conversation scoring and evaluation
  • Cost optimization strategies

πŸ”— Additional Resources