Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Mats Estensen committed May 20, 2022
0 parents commit dbe3f68
Show file tree
Hide file tree
Showing 7 changed files with 710 additions and 0 deletions.
395 changes: 395 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# DAPR Container Apps in Azure

> Learning Azure Container Apps based on [this guide](https://docs.microsoft.com/en-us/azure/container-apps/microservices-dapr-azure-resource-manager?tabs=powershell&pivots=container-apps-bicep) from Microsoft
This repo contains code to deploy the [DAPR hello-world application](https://github.com/dapr/quickstarts/tree/master/tutorials/hello-world) on [Azure Container Apps](https://docs.microsoft.com/en-us/azure/container-apps/overview) with [DAPR](https://dapr.io/) using Azure Storage Account for storing state.

### Technologies

- :hammer: Azure PowerShell and AZ CLI for interaction with Azure
- :gear: PowerShell for deployment script
- :muscle: Bicep for Infrastructure as Code

### Overview

![diagram](static/azure-container-apps-microservices-dapr.png)
([diagram by Microsoft](https://docs.microsoft.com/en-us/azure/container-apps/microservices-dapr-azure-resource-manager?tabs=powershell&pivots=container-apps-bicep#prerequisites) - [CC BY 4.0](https://github.com/MicrosoftDocs/azure-docs/blob/main/LICENSE))


## Usage

### Prerequisites

1. [Install/update Azure PowerShell](https://docs.microsoft.com/en-us/powershell/azure/install-az-ps?view=latest)
2. [Install/update Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)
- containerapp extension: `az extension add --name containerapp --upgrade`
- bicep: `az bicep install`
3. Connect to Azure: `Connect-AzAccount`
4. Set Context: `Set-AzContext -SubscriptionName <subscription name>`
4. Register resource provider: `Register-AzResourceProvider -ProviderNamespace Microsoft.App`

### Deploy

Open PowerShell and run [deploy.ps1](./deploy.ps1) to deploy the resources:

```powershell
./deploy.ps1
# Example output
🚀 Deploying container apps...(this will take a few minutes)
✔️ Deploy succeeded! API url:
https://nodeapp.<unique name>.canadacentral.azurecontainerapps.io/order
⌛ Waiting for a minute before querying api and logs...
🔎 Querying /order API...
{
"orderId": 99
}
🗒️ Querying logs from Log Analytics Workspace. Listing 5 latest entries...
[
{
"ContainerAppName_s": "nodeapp",
"Log_s": "Got a new order! Order ID: 83",
"TimeGenerated": "2022-05-20T21:36:37.839Z"
},
{
"ContainerAppName_s": "nodeapp",
"Log_s": "Got a new order! Order ID: 84",
"TimeGenerated": "2022-05-20T21:36:37.839Z"
},
{
"ContainerAppName_s": "nodeapp",
"Log_s": "Got a new order! Order ID: 85",
"TimeGenerated": "2022-05-20T21:36:39.674Z"
},
{
"ContainerAppName_s": "nodeapp",
"Log_s": "Got a new order! Order ID: 60",
"TimeGenerated": "2022-05-20T21:36:13.533Z"
},
{
"ContainerAppName_s": "nodeapp",
"Log_s": "Got a new order! Order ID: 64",
"TimeGenerated": "2022-05-20T21:36:17.643Z"
}
]
```
35 changes: 35 additions & 0 deletions deploy.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
$ErrorActionPreference = 'Stop'

$params = @{
Name = "dapr-containerapp$(Get-Date -Format "yyMMdd-HHmm")"
Location = "canadacentral"
TemplateFile = "main.bicep"
rgName = "dapr-containerapps-demo"
LocationFromTemplate = "canadacentral"
containerEnvironmentName = "dapr-containerapps-env"
storageContainerName = "dapr-containerapps-state"
}

Write-Host "`n🚀 Deploying container apps...(this will take a few minutes)"
$deploy = New-AzSubscriptionDeployment @params

if ($deploy.ProvisioningState -ne "Succeeded"){
Write-Host "$($deploy | Out-String)"
Write-Error "Something went wrong in deploy. Please revise"
}

Write-Host "`n✔️ Deploy succeeded! API url:"
$orderApi = "https://$($deploy.Outputs.uri.Value)/order"
Write-Host $orderApi

# Verify that orders are running
Write-Host "`n⌛ Waiting for a minute before querying api and logs..."
Start-Sleep -Seconds 60

Write-Host "`n🔎 Querying /order API..."
Write-Host $(Invoke-RestMethod -Uri $orderApi | ConvertTo-Json)

Write-Host "`n🗒️ Querying logs from Log Analytics Workspace. Listing 5 latest entries..."
$LOG_ANALYTICS_WORKSPACE_CLIENT_ID = (az containerapp env show --name $params.containerEnvironmentName --resource-group $params.rgName --query properties.appLogsConfiguration.logAnalyticsConfiguration.customerId --out tsv)
$queryResults = Invoke-AzOperationalInsightsQuery -WorkspaceId $LOG_ANALYTICS_WORKSPACE_CLIENT_ID -Query "ContainerAppConsoleLogs_CL | where ContainerAppName_s == 'nodeapp' and (Log_s contains 'persisted' or Log_s contains 'order') | project ContainerAppName_s, Log_s, TimeGenerated | take 5"
Write-Host "$($queryResults.Results | ConvertTo-Json)"
33 changes: 33 additions & 0 deletions main.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
targetScope = 'subscription'

param rgName string
param location string
param containerEnvironmentName string
param storageContainerName string

resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: rgName
location: location
}

module storageAccount 'modules/storage.bicep' = {
scope: resourceGroup
name: '${deployment().name}-stg'
params: {
storageAccountName: 'stg${uniqueString(resourceGroup.id)}'
location: location
}
}

module containerApps 'modules/containerapp.bicep' = {
scope: resourceGroup
name: '${deployment().name}-apps'
params: {
containerEnvironmentName: containerEnvironmentName
storageAccountName: storageAccount.outputs.name
storageContainerName: storageContainerName
location: location
}
}

output uri string = containerApps.outputs.nodeAppUri
153 changes: 153 additions & 0 deletions modules/containerapp.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
param containerEnvironmentName string
param location string = resourceGroup().location
param storageAccountName string
param storageContainerName string

var logAnalyticsWorkspaceName = '${containerEnvironmentName}-logs'
var appInsightsName = '${containerEnvironmentName}-appins'

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = {
name: storageAccountName
}

resource logAnalyticsWorkspace'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = {
name: logAnalyticsWorkspaceName
location: location
properties: any({
retentionInDays: 30
features: {
searchVersion: 1
}
sku: {
name: 'PerGB2018'
}
})
}

resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: appInsightsName
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalyticsWorkspace.id
}
}

resource environment 'Microsoft.App/managedEnvironments@2022-03-01' = {
name: containerEnvironmentName
location: location
properties: {
daprAIInstrumentationKey:appInsights.properties.InstrumentationKey
appLogsConfiguration: {
destination: 'log-analytics'
logAnalyticsConfiguration: {
customerId: reference(logAnalyticsWorkspace.id, '2020-03-01-preview').customerId
sharedKey: listKeys(logAnalyticsWorkspace.id, '2020-03-01-preview').primarySharedKey
}
}
}
resource daprComponent 'daprComponents@2022-03-01' = {
name: 'statestore'
properties: {
componentType: 'state.azure.blobstorage'
version: 'v1'
ignoreErrors: false
initTimeout: '5s'
secrets: [
{
name: 'storageaccountkey'
value: storageAccount.listKeys().keys[0].value
}
]
metadata: [
{
name: 'accountName'
value: storageAccountName
}
{
name: 'containerName'
value: storageContainerName
}
{
name: 'accountKey'
secretRef: 'storageaccountkey'
}
]
scopes: [
'nodeapp'
]
}
}
}

resource nodeapp 'Microsoft.App/containerApps@2022-03-01' = {
name: 'nodeapp'
location: location
properties: {
managedEnvironmentId: environment.id
configuration: {
ingress: {
external: true
targetPort: 3000
}
dapr: {
enabled: true
appId: 'nodeapp'
appProtocol: 'http'
appPort: 3000
}
}
template: {
containers: [
{
image: 'dapriosamples/hello-k8s-node:latest'
name: 'hello-k8s-node'
resources: {
cpu: '0.5'
memory: '1.0Gi'
}
}
]
scale: {
minReplicas: 1
maxReplicas: 1
}
}
}
}

resource pythonapp 'Microsoft.App/containerApps@2022-03-01' = {
name: 'pythonapp'
location: location
properties: {
managedEnvironmentId: environment.id
configuration: {
dapr: {
enabled: true
appId: 'pythonapp'
}
}
template: {
containers: [
{
image: 'dapriosamples/hello-k8s-python:latest'
name: 'hello-k8s-python'
resources: {
cpu: '0.5'
memory: '1.0Gi'
}
}
]
scale: {
minReplicas: 1
maxReplicas: 1
}
}
}
dependsOn: [
nodeapp
]
}

output nodeAppUri string = nodeapp.properties.configuration.ingress.fqdn
15 changes: 15 additions & 0 deletions modules/storage.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
param location string = resourceGroup().location
param storageAccountName string

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' = {
name: storageAccountName
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_RAGRS'
}
}

output name string = storageAccount.name
output id string = storageAccount.id
output blobEndpoint string = storageAccount.properties.primaryEndpoints.blob
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit dbe3f68

Please sign in to comment.