/dapr-microservices

Sample outlining how Dapr can be used to build and run microservices on Azure.

Primary LanguageC#MIT LicenseMIT

Dapr Microservices

Deployment

Introduction

Sample outlining how Dapr can be used to build and run microservices on Azure. For educational purposes and the sake of simplicity, some shortcuts were taken. Nevertheless, the sample could act as a baseline and further evolved to become production-grade e.g.,

High-level Architecture

Tenants and users are managed within separated microservices, which itself leverage integration events for asynchronous communication and state synchronization (See: Implementing event-based communication between microservices (integration events))

sequenceDiagram
    participant HTTPClient
    participant TenantService
    participant UserService

    HTTPClient->>TenantService: Create Tenant
    TenantService->>TenantService: Store Tenant
    TenantService->>UserService: TenantCreated
    UserService->>UserService: Store Tenant

    HTTPClient->>UserService: Create User associated to Tenant
    UserService->>UserService: Store User

    HTTPClient->>TenantService: Delete Tenant
    TenantService->>TenantService: Delete Tenant
    TenantService->>UserService: TenantDeleted
    UserService->>UserService: Delete Users associated to Tenant
Loading

Remarks:

  • Dapr building blocks address common challenges in building resilient, microservices applications and codify best practices and patterns e.g., state management, service-to-service invocation, publish and subscribe, observability and secrets (See: Dapr Building Blocks).
  • Dapr allows to switch underlying technologies per configuration e.g., the sample leverages MongoDB locally and Azure Storage in the cloud for state management.
  • Querying state is still in alpha state and only supported for certain state stores e.g., MongoDB and CosmosDB. Production scenarios most-likely require falling back to store-specific implementations to also optimize query performance.
  • Currently Dapr has limited support for managed identities in Azure, which adds additional complexity when implementing key rotation. For instance, while managed identities are supported to access Azure Key Vault, they are currently not supported for accessing Azure Storage and Azure Service Bus (See: Authenticating to Azure).

Setup

Prerequisites

  1. Open Bash on your local machine e.g., by using Windows Subsystem for Linux.

  2. Verify whether Docker is installed, if not follow this guide for installation:

docker version # Version should be '20.10.12' or newer.
  1. Verify whether Azure CLI is installed, if not follow this guide for installation:
az version # Version should be '2.33.0' or newer.
  1. Verify whether kubectl is installed, if not follow this guide for installation:
kubectl version # Version should be '1.22.5' or newer.
  1. Verify whether Git is installed, if not follow this guide for installation:
git version # Version should be '2.25.1' or newer.
  1. Verify whether GitHub CLI is installed, if not follow this guide for installation:
gh version # Version should be '2.5.1' or newer.
  1. Verify whether jq is installed, if not follow this guide for installation:
jq --version # Version should be '1.6' or newer.

Local

  1. Login to GitHub
gh auth login
  1. Clone the repository:
gh repo clone fawohlsc/dapr-microservices
  1. Navigate into the source code:
cd dapr-microservices/src/
  1. Build the microservices:
make build
  1. Run the microservices:
make up
  1. Open a separate Bash session on your local machine.

  2. Interact with the microservices:

# Set HTTP endpoints.
tenant_service="http://localhost:5001/Tenant"
user_service="http://localhost:5002/User"

# Create tenant.
curl -v -X POST -H "Content-Type: application/json" -d '{ "id": "f9a358dd-dc88-4466-b42e-6c3b368f1877", "name": "tenant01", "sku": "free" }' $tenant_service; echo # Adds newline to response.

# Read tenant.
curl -v -X GET "${tenant_service}/f9a358dd-dc88-4466-b42e-6c3b368f1877"; echo # Adds newline to response.

# Update tenant.
curl -v -X PUT -H "Content-Type: application/json" -d '{ "id": "f9a358dd-dc88-4466-b42e-6c3b368f1877", "name": "tenant01", "sku": "premium" }' "${tenant_service}/f9a358dd-dc88-4466-b42e-6c3b368f1877"; echo # Adds newline to response.

# Create user associated to tenant.
curl -v -X POST -H "Content-Type: application/json" -d '{ "id": "8df29090-d719-4521-98e2-cebc7b0deb16", "firstName": "John", "lastName": "Doe", "emailAddress": "john.doe@outlook.com", "tenantId": "f9a358dd-dc88-4466-b42e-6c3b368f1877" }' $user_service; echo # Adds newline to response.

# Read user.
curl -v -X GET "${user_service}/8df29090-d719-4521-98e2-cebc7b0deb16"; echo # Adds newline to response.

# Update user.
curl -v -X PUT -H "Content-Type: application/json" -d '{ "id": "8df29090-d719-4521-98e2-cebc7b0deb16", "firstName": "Jane", "lastName": "Doe", "emailAddress": "jane.doe@outlook.com", "tenantId": "f9a358dd-dc88-4466-b42e-6c3b368f1877" }' "${user_service}/8df29090-d719-4521-98e2-cebc7b0deb16"; echo # Adds newline to response.

# Delete user.
curl -v -X DELETE "${user_service}/8df29090-d719-4521-98e2-cebc7b0deb16"; echo # Adds newline to response.

# Delete tenant.
curl -v -X DELETE "${tenant_service}/f9a358dd-dc88-4466-b42e-6c3b368f1877"; echo # Adds newline to response.
  1. View the logs using Seq i.e., verify the processing of the integration events (TenantCreated, TenantDeleted):
sensible-browser http://localhost:5341/
  1. View the traces using Zipkin i.e., verify the processing of the integration events (TenantCreated, TenantDeleted):
sensible-browser http://localhost:9412/
  1. You can use Visual Studio Code to debug the microservices.

  2. Read about the other options to locally host microservices powered by Dapr.

Azure

  1. Determine the Azure tenant:
azure_tenant_id=$(az account show --query "tenantId" -o tsv)
  1. Determine the Azure suscription:
azure_subscription_id=$(az account show --query "id" -o tsv)
  1. Create the Azure credentials:
azure_credentials=$(az ad sp create-for-rbac --name "dapr-microservices" --role "Owner" --scopes "/subscriptions/${azure_subscription_id}" --sdk-auth)
  1. Login to GitHub:
gh auth login
  1. Fork the GitHub repository fawohlsc/dapr-microservices:
gh repo fork "fawohlsc/dapr-microservices" --clone=false
  1. Determine the forked GitHub repository e.g., janedoe/dapr-microservices:
repo=$(gh repo list --fork --json nameWithOwner --jq '.[] | select(.nameWithOwner | match("/dapr-microservices")) | .nameWithOwner')
  1. Create the GitHub secrets:
gh secret set AZURE_TENANT_ID --body $azure_tenant_id} --repo $repo
gh secret set AZURE_CREDENTIALS --body "${azure_credentials}" --repo $repo
  1. Enable the GitHub workflows:
gh workflow enable --repo $repo
  1. Trigger the GitHub workflow:
gh workflow run "deployment.yml"
  1. Verify whether the GitHub workflow was triggered:
gh run list --workflow deployment.yml
  1. Open the forked GitHub repository in your browser:
gh browse --repo $repo
  1. Navigate to Actions tab and wait for the GitHub workflow to successfully complete.

  2. Within the existing Bash session, login to Azure Kubernetes Service (AKS):

readonly RESOURCE_GROUP="dapr-microservices"
aks_name=$(az aks list --resource-group $RESOURCE_GROUP --query "[0].name" -o tsv)
az aks get-credentials --resource-group $RESOURCE_GROUP --name $aks_name --overwrite-existing
  1. Verify whether the microservices are running:
readonly NAMESPACE="dapr-microservices"
kubectl get pods --namespace $NAMESPACE
  1. View the logs of the tenant service:
tenant_service_pod=$(kubectl get pods --namespace $NAMESPACE --output=name | grep "tenantservice")

# Microservice
kubectl logs $tenant_service_pod --container "tenantservice"  --namespace $NAMESPACE

# Dapr sidecar
kubectl logs $tenant_service_pod --container "daprd"  --namespace $NAMESPACE
  1. View the logs of the user service:
user_service_pod=$(kubectl get pods --namespace $NAMESPACE --output=name | grep "userservice")

# Microservice
kubectl logs $user_service_pod --container "userservice" --namespace $NAMESPACE

# Dapr sidecar
kubectl logs $user_service_pod --container "daprd"  --namespace $NAMESPACE
  1. Interact with the microservices:
# Determine HTTP endpoints.
tenant_service="$(kubectl get ingress "tenantservice" --namespace $NAMESPACE --output=json | jq -r ".spec.rules[0].host")/Tenant"
user_service="$(kubectl get ingress "userservice" --namespace $NAMESPACE --output=json | jq -r ".spec.rules[0].host")/User"

# Create tenant.
curl -v -X POST -H "Content-Type: application/json" -d '{ "id": "f9a358dd-dc88-4466-b42e-6c3b368f1877", "name": "tenant01", "sku": "free" }' $tenant_service; echo # Adds newline to response.

# Read tenant.
curl -v -X GET "${tenant_service}/f9a358dd-dc88-4466-b42e-6c3b368f1877"; echo # Adds newline to response.

# Update tenant.
curl -v -X PUT -H "Content-Type: application/json" -d '{ "id": "f9a358dd-dc88-4466-b42e-6c3b368f1877", "name": "tenant01", "sku": "premium" }' "${tenant_service}/f9a358dd-dc88-4466-b42e-6c3b368f1877"; echo # Adds newline to response.

# Create user associated to tenant.
curl -v -X POST -H "Content-Type: application/json" -d '{ "id": "8df29090-d719-4521-98e2-cebc7b0deb16", "firstName": "John", "lastName": "Doe", "emailAddress": "john.doe@outlook.com", "tenantId": "f9a358dd-dc88-4466-b42e-6c3b368f1877" }' $user_service; echo # Adds newline to response.

# Read user.
curl -v -X GET "${user_service}/8df29090-d719-4521-98e2-cebc7b0deb16"; echo # Adds newline to response.

# Update user.
curl -v -X PUT -H "Content-Type: application/json" -d '{ "id": "8df29090-d719-4521-98e2-cebc7b0deb16", "firstName": "Jane", "lastName": "Doe", "emailAddress": "jane.doe@outlook.com", "tenantId": "f9a358dd-dc88-4466-b42e-6c3b368f1877" }' "${user_service}/8df29090-d719-4521-98e2-cebc7b0deb16"; echo # Adds newline to response.

# Delete user.
curl -v -X DELETE "${user_service}/8df29090-d719-4521-98e2-cebc7b0deb16"; echo # Adds newline to response.

# Delete tenant.
curl -v -X DELETE "${tenant_service}/f9a358dd-dc88-4466-b42e-6c3b368f1877"; echo # Adds newline to response.
  1. Congratulations! You have completed the setup. Your feedback and contributions are very much appreciated i.e., starring this repository, raising issues or opening pull requests. Many thanks upfront!

Further Reading