Dapr integration with Azure Container Apps

The Distributed Application Runtime (Dapr) is a set of incrementally adoptable features that simplify the authoring of distributed, microservice-based applications. For example, Dapr provides capabilities for enabling application intercommunication, whether through messaging via pub/sub or reliable and secure service-to-service calls. Once Dapr is enabled for a container app, a secondary process will be created alongside your application code that will enable communication with Dapr via HTTP or gRPC.

Dapr's APIs are built on best practice industry standards, that:

  • Seamlessly fit with your preferred language or framework
  • Are incrementally adoptable; you can use one, several, or all dapr capabilities depending on your application's needs

Dapr is an open source, Cloud Native Computing Foundation (CNCF) project. The CNCF is part of the Linux Foundation and provides support, oversight, and direction for fast-growing, cloud native projects. As an alternative to deploying and managing the Dapr OSS project yourself, the Container Apps platform:

  • Provides a managed and supported Dapr integration
  • Handles Dapr version upgrades seamlessly
  • Exposes a simplified Dapr interaction model to increase developer productivity

This guide provides insight into core Dapr concepts and details regarding the Dapr interaction model in Container Apps.

Dapr APIs

Diagram that shows Dapr APIs.

Dapr API Description
Service-to-service invocation Discover services and perform reliable, direct service-to-service calls with automatic mTLS authentication and encryption.
State management Provides state management capabilities for transactions and CRUD operations.
Pub/sub Allows publisher and subscriber container apps to intercommunicate via an intermediary message broker.
Bindings Trigger your applications based on events
Actors Dapr actors are message-driven, single-threaded, units of work designed to quickly scale. For example, in burst-heavy workload situations.
Observability Send tracing information to an Application Insights backend.
Secrets Access secrets from your application code or reference secure values in your Dapr components.

Dapr concepts overview

The following example based on the Pub/sub API is used to illustrate core concepts related to Dapr in Azure Container Apps.

Diagram demonstrating Dapr pub/sub and how it works in Container Apps.

Label Dapr settings Description
1 Container Apps with Dapr enabled Dapr is enabled at the container app level by configuring a set of Dapr arguments. These values apply to all revisions of a given container app when running in multiple revisions mode.
2 Dapr The fully managed Dapr APIs are exposed to each container app through a Dapr sidecar. The Dapr APIs can be invoked from your container app via HTTP or gRPC. The Dapr sidecar runs on HTTP port 3500 and gRPC port 50001.
3 Dapr component configuration Dapr uses a modular design where functionality is delivered as a component. Dapr components can be shared across multiple container apps. The Dapr app identifiers provided in the scopes array dictate which dapr-enabled container apps will load a given component at runtime.

Dapr enablement

You can configure Dapr using various arguments and annotations based on the runtime context. Azure Container Apps provides three channels through which you can configure Dapr:

  • Container Apps CLI
  • Infrastructure as Code (IaC) templates, as in Bicep or Azure Resource Manager (ARM) templates
  • The Azure portal

The table below outlines the currently supported list of Dapr sidecar configurations in Container Apps:

Container Apps CLI Template field Description
--enable-dapr dapr.enabled Enables Dapr on the container app.
--dapr-app-port dapr.appPort The port your application is listening on which will be used by Dapr for communicating to your application
--dapr-app-protocol dapr.appProtocol Tells Dapr which protocol your application is using. Valid options are http or grpc. Default is http.
--dapr-app-id dapr.appId A unique Dapr identifier for your container app used for service discovery, state encapsulation and the pub/sub consumer ID.
--dapr-max-request-size dapr.httpMaxRequestSize Set the max size of request body http and grpc servers to handle uploading of large files. Default is 4 MB.
--dapr-read-buffer-size dapr.httpReadBufferSize Set the max size of http header read buffer in to handle when sending multi-KB headers. The default 4 KB.
--dapr-api-logging dapr.enableApiLogging Enables viewing the API calls from your application to the Dapr sidecar.
--dapr-log-level dapr.logLevel Set the log level for the Dapr sidecar. Allowed values: debug, error, info, warn. Default is info.

When using an IaC template, specify the following arguments in the properties.configuration section of the container app resource definition.

 dapr: {
   enabled: true
   appId: 'nodeapp'
   appProtocol: 'http'
   appPort: 3000
 }

The above Dapr configuration values are considered application-scope changes. When you run a container app in multiple revision mode, changes to these settings won't create a new revision. Instead, all existing revisions will be restarted to ensure they're configured with the most up-to-date values.

Dapr components

Dapr uses a modular design where functionality is delivered as a component. The use of Dapr components is optional and dictated exclusively by the needs of your application.

Dapr components in container apps are environment-level resources that:

  • Can provide a pluggable abstraction model for connecting to supporting external services.
  • Can be shared across container apps or scoped to specific container apps.
  • Can use Dapr secrets to securely retrieve configuration metadata.

Component schema

All Dapr OSS components conform to the following basic schema.

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: [COMPONENT-NAME]
  namespace: [COMPONENT-NAMESPACE]
spec:
  type: [COMPONENT-TYPE]
  version: v1
  initTimeout: [TIMEOUT-DURATION]
  ignoreErrors: [BOOLEAN]
  metadata:
    - name: [METADATA-NAME]
      value: [METADATA-VALUE]

In Container Apps, the above schema has been slightly simplified to support Dapr components and remove unnecessary fields, including apiVersion, kind, and redundant metadata and spec properties.

componentType: [COMPONENT-TYPE]
version: v1
initTimeout: [TIMEOUT-DURATION]
ignoreErrors: [BOOLEAN]
metadata:
  - name: [METADATA-NAME]
    value: [METADATA-VALUE]

Component scopes

By default, all Dapr-enabled container apps within the same environment will load the full set of deployed components. To ensure components are loaded at runtime by only the appropriate container apps, application scopes should be used. In the example below, the component will only be loaded by the two Dapr-enabled container apps with Dapr application IDs APP-ID-1 and APP-ID-2:

Note

Dapr component scopes correspond to the Dapr application ID of a container app, not the container app name.

componentType: [COMPONENT-TYPE]
version: v1
initTimeout: [TIMEOUT-DURATION]
ignoreErrors: [BOOLEAN]
metadata:
  - name: [METADATA-NAME]
    value: [METADATA-VALUE]
scopes:
  - [APP-ID-1]
  - [APP-ID-2]

Connecting to external services via Dapr

There are a few approaches supported in container apps to securely establish connections to external services for Dapr components.

  1. Using Managed Identity
  2. Using a Dapr Secret Store component reference
  3. Using Platform-managed Kubernetes secrets

Using managed identity

For Azure-hosted services, Dapr can use the managed identity of the scoped container apps to authenticate to the backend service provider. When using managed identity, you don't need to include secret information in a component manifest. Using managed identity is preferred as it eliminates storage of sensitive input in components and doesn't require managing a secret store.

Using a Dapr secret store component reference

When you create Dapr components for non-AD enabled services, certain metadata fields require sensitive input values. The recommended approach for retrieving these secrets is to reference an existing Dapr secret store component that securely accesses secret information.

Here are the steps to set up a reference:

  1. Create a Dapr secret store component using the Container Apps schema. The component type for all supported Dapr secret stores begins with secretstores..
  2. Create extra components as needed which reference this Dapr secret store component to retrieve the sensitive metadata input.

When creating a secret store component in container apps, you can provide sensitive information in the metadata section in either of the following ways:

  • For an Azure Key Vault secret store, use managed identity to establish the connection.
  • For non-Azure secret stores, use platform-managed Kubernetes secrets that are defined directly as part of the component manifest.

The following component showcases the simplest possible secret store configuration. This example publisher and subscriber applications configured to both have a system or user-assigned managed identity with appropriate permissions on the Azure Key Vault instance.

componentType: secretstores.azure.keyvault
version: v1
metadata:
  - name: vaultName
    value: [your_keyvault_name]
  - name: azureEnvironment
    value: "AZUREPUBLICCLOUD"
  - name: azureClientId
    value: [your_managed_identity_client_id]
scopes:
  - publisher-app
  - subscriber-app

Note

Kubernetes secrets, Local environment variables and Local file Dapr secret stores are not supported in Container Apps. As an alternative for the upstream Dapr default Kubernetes secret store, container apps provides a platform-managed approach for creating and leveraging Kubernetes secrets.

Using Platform-managed Kubernetes secrets

This component configuration defines the sensitive value as a secret parameter that can be referenced from the metadata section. This approach can be used to connect to non-Azure services or in dev/test scenarios for quickly deploying components via the CLI without setting up a secret store or managed identity.

componentType: secretstores.azure.keyvault
version: v1
metadata:
  - name: vaultName
    value: [your_keyvault_name]
  - name: azureEnvironment
    value: "AZUREPUBLICCLOUD"
  - name: azureTenantId
    value: "[your_tenant_id]"
  - name: azureClientId
    value: "[your_client_id]"
  - name: azureClientSecret
    secretRef: azClientSecret
secrets:
  - name: azClientSecret
    value: "[your_client_secret]"
scopes:
  - publisher-app
  - subscriber-app

Referencing Dapr secret store components

Once you've created a Dapr secret store using one of the above approaches, you can reference that secret store from other Dapr components in the same environment. In the following example, the secretStoreComponent field is populated with the name of the secret store specified above, where the sb-root-connectionstring is stored.

componentType: pubsub.azure.servicebus
version: v1
secretStoreComponent: "my-secret-store"
metadata:
  - name: connectionString
    secretRef: sb-root-connectionstring
scopes:
  - publisher-app
  - subscriber-app

Component examples

To create a Dapr component via the Container Apps CLI, you can use a container apps YAML manifest. When configuring multiple components, you must create and apply a separate YAML file for each component.

az containerapp env dapr-component set --name ENVIRONMENT_NAME --resource-group RESOURCE_GROUP_NAME --dapr-component-name pubsub --yaml "./pubsub.yaml"
# pubsub.yaml for Azure Service Bus component
componentType: pubsub.azure.servicebus
version: v1
secretStoreComponent: "my-secret-store"
metadata:
  - name: connectionString
    secretRef: sb-root-connectionstring
scopes:
  - publisher-app
  - subscriber-app

Limitations

Unsupported Dapr capabilities

  • Custom configuration for Dapr Observability: Instrument your environment with Application Insights to visualize distributed tracing.
  • Dapr Configuration spec: Any capabilities that require use of the Dapr configuration spec.
  • Declarative pub/sub subscriptions
  • Any Dapr sidecar annotations not listed above

Known limitations

  • Actor reminders: Require a minReplicas of 1+ to ensure reminders will always be active and fire correctly.

Next Steps

Now that you've learned about Dapr and some of the challenges it solves: