Skip to content

clarenceb/tutorial-dapr-cli

Repository files navigation

Microservices with Dapr using the CLI

Prerequisites

Setup base resources

RESOURCE_GROUP="containerapps"
LOCATION="canadacentral"
CONTAINERAPPS_ENVIRONMENT="containerapps"

az login

az extension add --name containerapp
az provider register --namespace Microsoft.App
az provider show -n Microsoft.App --query registrationState

az group create \
  --name $RESOURCE_GROUP \
  --location "$LOCATION"

az deployment group create \
  --name env-create \
  -g $RESOURCE_GROUP \
  --template-file ./environment.bicep \
  --parameters environmentName=$CONTAINERAPPS_ENVIRONMENT

az containerapp env list -o table
az containerapp env show -n containerapps -g containerapps
az containerapp env dapr-component list -n containerapps -g containerapps -o table
az containerapp env dapr-component show -n containerapps -g containerapps --dapr-component-name statestore

DEPLOY_OUTPUTS=$(az deployment group show --name env-create -g $RESOURCE_GROUP --query properties.outputs)

ACR_USERNAME=$(echo $DEPLOY_OUTPUTS | jq -r .acrUserName.value)
ACR_PASSWORD=$(echo $DEPLOY_OUTPUTS | jq -r .acrPassword.value)
ACR_LOGIN_SERVER=$(echo $DEPLOY_OUTPUTS | jq -r .acrloginServer.value)
ACR_NAME=$(echo $ACR_LOGIN_SERVER | cut -f1,1 -d .)
WORKSPACE_CLIENT_ID=$(echo $DEPLOY_OUTPUTS | jq -r .workspaceId.value)

Create new contaimer images (for displaying a custom message with the the app version)

pushd ~

git clone https://github.com/clarenceb/quickstarts dapr-quickstarts
cd dapr-quickstarts/hello-kubernetes/node

az acr build --image hello-k8s-node:v1 \
  --registry $ACR_NAME \
  --file Dockerfile .

cd ../python

az acr build --image hello-k8s-python:v1 \
  --registry $ACR_NAME \
  --file Dockerfile .

popd

Deploy the service application (HTTP web server)

# Deploy via Bicep template
az deployment group create \
  --name nodeapp-v1 \
  -g $RESOURCE_GROUP \
  --template-file ./nodeapp-containerapp.bicep \
  --parameters environment_name=$CONTAINERAPPS_ENVIRONMENT \
    custom_message="v1" \
    image_name="$ACR_LOGIN_SERVER/hello-k8s-node:v1" \
    registry_login_server=$ACR_LOGIN_SERVER \
    registry_username=$ACR_USERNAME \
    registry_password=$ACR_PASSWORD

# Or via CLI
az containerapp create \
  --name nodeapp \
  --container-name nodeapp \
  --revisions-mode multiple \
  --resource-group $RESOURCE_GROUP \
  --environment $CONTAINERAPPS_ENVIRONMENT \
  --image $ACR_LOGIN_SERVER/hello-k8s-node:v1 \
  --registry-server $ACR_LOGIN_SERVER \
  --registry-username $ACR_USERNAME \
  --registry-password $ACR_PASSWORD \
  --env-vars MESSAGE=v1 \
  --cpu 0.5 \
  --memory 1.0Gi \
  --target-port 3000 \
  --ingress external \
  --min-replicas 1 \
  --max-replicas 1 \
  --enable-dapr \
  --dapr-app-port 3000 \
  --dapr-app-id nodeapp \
  --dapr-app-protocol http

az containerapp list -o table
az containerapp revision list -n nodeapp -g $RESOURCE_GROUP -o table

Deploy the client application (headless client)

The Python App will invoke the NodeApp every second via it's Dapr sidecar via the URI: http://localhost:{DAPR_PORT}/v1.0/invoke/nodeapp/method/neworder

NODEAPP_INGRESS_URL="https://$(az containerapp show -n nodeapp -g $RESOURCE_GROUP --query properties.configuration.ingress.fqdn -o tsv)"

# Deploy via Bicep template
az deployment group create \
  --name pythonapp \
  -g $RESOURCE_GROUP \
  --template-file ./pythonapp-containerapp.bicep \
  --parameters environment_name=$CONTAINERAPPS_ENVIRONMENT \
    image_name="$ACR_LOGIN_SERVER/hello-k8s-python:v1" \
    registry_login_server=$ACR_LOGIN_SERVER \
    registry_username=$ACR_USERNAME \
    registry_password=$ACR_PASSWORD

# Or via CLI
az containerapp create \
  --name pythonapp \
  --container-name pythonapp \
  --resource-group $RESOURCE_GROUP \
  --environment $CONTAINERAPPS_ENVIRONMENT \
  --image $ACR_LOGIN_SERVER/hello-k8s-node:v1 \
  --registry-server $ACR_LOGIN_SERVER \
  --registry-username $ACR_USERNAME \
  --registry-password $ACR_PASSWORD \
  --cpu 0.5 \
  --memory 1.0Gi \
  --min-replicas 1 \
  --max-replicas 1 \
  --enable-dapr \
  --dapr-app-id pythonapp \
  --dapr-app-protocol http

# Append paraemter `nodeapp_url` if not using Dapr for service discovery
# nodeapp_url=$NODEAPP_INGRESS_URL/neworder

az containerapp list -o table
az containerapp revision list -n pythonapp -g $RESOURCE_GROUP -o table

Create some orders

curl -i --request POST --data "@sample.json" --header Content-Type:application/json $NODEAPP_INGRESS_URL/neworder

curl -s $NODEAPP_INGRESS_URL/order | jq

az monitor log-analytics query \
  --workspace $WORKSPACE_CLIENT_ID \
  --analytics-query "ContainerAppConsoleLogs_CL | where ContainerAppName_s == 'nodeapp' and (Log_s contains 'persisted' or Log_s contains 'order') | where TimeGenerated >= ago(30m) | project ContainerAppName_s, Log_s, TimeGenerated | order by TimeGenerated desc | take 20" \
  --out table

watch -n 5 az monitor log-analytics query --workspace $WORKSPACE_CLIENT_ID --analytics-query "\"ContainerAppConsoleLogs_CL | where ContainerAppName_s == 'nodeapp' and (Log_s contains 'persisted' or Log_s contains 'order') | where TimeGenerated >= ago(30m) | project ContainerAppName_s, Log_s, TimeGenerated | order by TimeGenerated desc | take 20\"" --out table

# Or in the Azure Portal, enter this KQL query:
ContainerAppConsoleLogs_CL
| where ContainerAppName_s == 'nodeapp' and (Log_s contains 'persisted' or Log_s contains 'order')
| where TimeGenerated >= ago(30m)
| project ContainerAppName_s, Log_s, TimeGenerated
| order by TimeGenerated desc
| take 20

# Note: There is some latency when streaming logs via the CLI. Use the Azure Portal if you want to query logs with less latency.

URL=$NODEAPP_INGRESS_URL/neworder k6 run k6-script.js

Deploy v2 of nodeapp

# Deploy via Bicep template
az deployment group create \
  --name nodeapp-v1 \
  -g $RESOURCE_GROUP \
  --template-file ./nodeapp-containerapp.bicep \
  --parameters environment_name=$CONTAINERAPPS_ENVIRONMENT \
    custom_message="v2" \
    image_name="$ACR_LOGIN_SERVER/hello-k8s-node:v1" \
    registry_login_server=$ACR_LOGIN_SERVER \
    registry_username=$ACR_USERNAME \
    registry_password=$ACR_PASSWORD

# Or via CLI
az containerapp update \
  --name nodeapp \
  --container-name nodeapp \
  --resource-group $RESOURCE_GROUP \
  --image $ACR_LOGIN_SERVER/hello-k8s-node:v1 \
  --replace-env-vars MESSAGE=v2 \
  --min-replicas 1 \
  --max-replicas 1

az containerapp list -o table
az containerapp revision list -n nodeapp -g $RESOURCE_GROUP -o table

In the Azure Portal, split traffic 50% to v1 and v2 and send some orders.

URL=$NODEAPP_INGRESS_URL/neworder k6 run k6-script.js

Inspect the logs via CLI or in the Azure Portal to see round-robin between the two revisions.

watch -n 5 az monitor log-analytics query --workspace $WORKSPACE_CLIENT_ID --analytics-query "\"ContainerAppConsoleLogs_CL | where ContainerAppName_s == 'nodeapp' and (Log_s contains 'persisted' or Log_s contains 'order') | where TimeGenerated >= ago(30m) | project ContainerAppName_s, Log_s, TimeGenerated | order by TimeGenerated desc | take 20\"" --out table

# Or in the Azure Portal, enter this KQL query:
ContainerAppConsoleLogs_CL
| where ContainerAppName_s == 'nodeapp' and (Log_s contains 'persisted' or Log_s contains 'order')
| where TimeGenerated >= ago(30m)
| project ContainerAppName_s, Log_s, TimeGenerated
| order by TimeGenerated desc
| take 20

Application Insights

After creating some orders, check the App Insights resource.

  • Application Map
  • Performance

Cleanup

Cleanup container apps to restart the demo (retaining enviornment and other resources):

az deployment group delete --name nodeapp-v1 -g $RESOURCE_GROUP
az deployment group delete --name pythonapp -g $RESOURCE_GROUP

az containerapp delete --name nodeapp --resource-group $RESOURCE_GROUP --yes
az containerapp delete --name pythonapp --resource-group $RESOURCE_GROUP --yes

or full cleanup:

az group delete \
    --resource-group $RESOURCE_GROUP \
    --yes

Resources