Learning Azure Container Apps
-
📂 This repo contains code to deploy the Dapr pub-sub application on Azure Container Apps using Azure Service Bus for handling publisher and subscriber (Pub-Sub) functionality.
-
💻 The application consists of a React web frontend, a Python subscriber component and a Node subscriber component.
-
🎩 Since we use Dapr we can replace Redis (as used in the application quickstart) with Azure Service Bus without any changes to the application code. In fact, we change both the environment the apps run in from local containers or Kubernetes to Azure Container Apps and the message broker without modifying the applications themselves! Portable microservices FTW!
- 🔨 Azure PowerShell for deployment
- 💪 Bicep for Infrastructure as Code
(modified diagram, original by Dapr - Apache 2.0)
- Contributor (or higher) permissions on an Azure subscription
- Install/update Azure PowerShell module (Tested with Az 6.15.0)
- Install/update Bicep CLI (Tested with 0.25.53)
- Connect to Azure:
Connect-AzAccount
- Set Context:
Set-AzContext -SubscriptionName <subscription name>
- Register resource provider:
Register-AzResourceProvider -ProviderNamespace Microsoft.App
Open PowerShell and run deploy.ps1 to deploy the resources:
./deploy.ps1
# Example output
🚀 Deploying container apps...(this will take a few minutes)
✔️ Deploy succeeded! React frontend url:
https://react-form.<unique name>.canadacentral.azurecontainerapps.io/order
- Open the React frontend url from the deployment output:
- Choose a message type and enter your custom message, and press Submit
Example publisher messages:
A: Hello world to all my subscribers! # this topic will be received by all
B: Hello world to some of my subscribers! # this topic will only be received by node-subscriber
C: Hello to my C-topic fans! # this topic will only be received by python-subscriber
💡 Note that it might take 5-10 minutes after initial deployment before logs from the container apps is available in the workspace.
- View logs from both subscriber services:
$LOG_ANALYTICS_WORKSPACE_CLIENT_ID = (Get-AzOperationalInsightsWorkspace -ResourceGroupName dapr-pubsub-containerapps-demo).CustomerId.Guid
$query = "
ContainerAppConsoleLogs_CL
| where ContainerAppName_s hassuffix_cs '-subscriber'
| where ContainerName_s hassuffix_cs '-subscriber'
| where Stream_s == 'stdout'
| project ContainerName_s, Log_s, TimeGenerated
| sort by TimeGenerated desc
| take 6
"
$queryResults = Invoke-AzOperationalInsightsQuery -WorkspaceId $LOG_ANALYTICS_WORKSPACE_CLIENT_ID -Query $query -TimeSpan (New-Timespan -Hours 1)
Write-Host "$($queryResults.Results | ConvertTo-Json)"
Example output:
[
{
"ContainerName_s": "python-subscriber",
"Log_s": "Received message \"Hello to my C-topic fans!\" on topic \"C\"",
"TimeGenerated": "2022-05-24T21:39:31.287Z"
},
{
"ContainerName_s": "python-subscriber",
"Log_s": "C: {'datacontenttype': 'application/json', 'source': 'react-form', 'type': 'com.dapr.event.sent', 'traceid': '00-eafc38d79174ab05699909ef2ef43051-e2ccdc2b04447cd5-01', 'tracestate': '', 'data': {'messageType': 'C', 'message': 'Hello to my C-topic fans!'}, 'id': '59d412c7-2bce-416b-972d-8d3237e02cd5', 'specversion': '1.0', 'topic': 'C', 'pubsubname': 'pubsub', 'traceparent': '00-eafc38d79174ab05699909ef2ef43051-e2ccdc2b04447cd5-01'}",
"TimeGenerated": "2022-05-24T21:39:31.287Z"
},
{
"ContainerName_s": "node-subscriber",
"Log_s": "B: Hello world to some of my subscribers!",
"TimeGenerated": "2022-05-24T21:39:01.737Z"
},
{
"ContainerName_s": "python-subscriber",
"Log_s": "Received message \"Hello world to all my subscribers!\" on topic \"A\"",
"TimeGenerated": "2022-05-24T21:38:41.18Z"
},
{
"ContainerName_s": "python-subscriber",
"Log_s": "A: {'traceparent': '00-1d2dd1e66c9f1c9491cf2ef316b6d15c-14c080f90a881d07-01', 'tracestate': '', 'datacontenttype': 'application/json', 'topic': 'A', 'pubsubname': 'pubsub', 'traceid': '00-1d2dd1e66c9f1c9491cf2ef316b6d15c-14c080f90a881d07-01', 'data': {'messageType': 'A', 'message': 'Hello world to all my subscribers!'}, 'id': '231c85e2-565e-4d93-b96f-7dbab41f6ab1', 'specversion': '1.0', 'source': 'react-form', 'type': 'com.dapr.event.sent'}",
"TimeGenerated": "2022-05-24T21:38:41.18Z"
},
{
"ContainerName_s": "node-subscriber",
"Log_s": "A: Hello world to all my subscribers!",
"TimeGenerated": "2022-05-24T21:38:41.12Z"
}
]
✔️ As noted the messages on topic A is received by both the node and python-subscriber, while B is only received by the node-subscriber and C is only received by the python-subscriber. All without any direct coupling between the publisher service and the subscriber services! Loosely coupled? ;)
This is a very simple explanation of how Service Bus topics works, but feel free to read more about architectural considerations around Pub-Sub architecture!
The deployed resources will not incur large costs, but to clean everything up run the following command:
Remove-AzResourceGroup -Name dapr-pubsub-containerapps-demo -Force