Deploying Containers on Azure Container Apps
In this article, we’ll explore Azure Container Apps and walk through example ARM templates to demonstrate how to deploy them within an Azure environment.
Join the DZone community and get the full member experience.
Join For FreeAzure Container Apps are a popular offering that helps you deploy Docker containers. It provides a sweet spot between effortless container scaling and avoiding the operational complexity of managing a full Kubernetes environment. Some of its best use cases include deploying API endpoints, building event-driven services, and running cloud workflows.
In addition, it also provides all the observability features required for cloud-scale monitoring and alerting. As part of this tutorial, we will explore how to deploy a container to Azure Container Apps.
- The first thing you need to create an app is a container environment. A container app environment can be thought of as a boundary that hosts related services. For example, if you are hosting services for an e-commerce website, there will be one environment with multiple apps for each microservice.
- Each environment app has a workload profile, which can be either a container app or a dedicated app. The main difference between the Container and Dedicated profiles is that the Container profile charges per app hosted in the environment, while the Dedicated profile incurs a fixed cost regardless of the number of apps.
- In terms of the limits, there can be at most 200 container apps per environment and up to 20 environments in a region per subscription.
Setting Up Container Apps
Now, let's look at how to deploy the container apps using ARM templates. The first step is to create the managed environment.
{
"type": "Microsoft.App/managedEnvironments",
"apiVersion": "2025-01-01",
"name": "[parameters('containerAppEnvironmentName')]",
"location": "[resourceGroup().location]",
"identity": {
"type": "UserAssigned",
"userAssignedIdentities": {
"[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.ManagedIdentity/userAssignedIdentities/', parameters('userMsiName'))]":{}
}
},
"properties": {
"workloadProfiles": [
{
"workloadProfileType": "Consumption",
"name": "Consumption"
}
]
}
}
What you see above is a template to create the managed environment. The mandatory parameters here are
- name: Name of the managed environment.
- location: Azure region where the environment needs to be deployed.
- identity: The kind of identity associated with the environment. User Assigned or System Managed identity. Both fall under the managed identity umbrella, where all the secrets required for communication are managed by Azure.
- workloadProfiles: In this case, we are using Consumption. The other option as discussed earlier is the Dedicated profile.
Next, we need to create the container app itself.
{
"type": "Microsoft.App/containerApps",
"apiVersion": "2025-01-01",
"name": "[parameters('containerAppName')]",
"identity": {
"type": "UserAssigned",
"userAssignedIdentities": {
"[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.ManagedIdentity/userAssignedIdentities/', parameters('userMsiName'))]":{}
}
},
"kind": "workflowapp",
"location": "[resourceGroup().location]",
"properties": {
"configuration": {
"ingress": {
"transport": "auto"
},
"runtime": {
"dotnet": {
"autoConfigureDataProtection": "true"
}
},
"registries": [{
"identity": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.ManagedIdentity/userAssignedIdentities/', parameters('userMsiName'))]",
"server": "[variables('registry')]"
}]
},
"environmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvironmentName'))]",
"template": {
"scale": {
"minReplicas": 2
},
"containers": [
{
"image": "[concat(variables('registry'), '/official/', variables('repository'), '/api-service-host-image:', parameters('imageVersion'))]",
"name": "api-host-container",
"resources": {
"cpu": "1",
"memory": "2Gi"
}
}
]
}
},
"dependsOn": [
"[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvironmentName'))]"
]
}
Like the managed environment, we have parameters like:
- name: Which is simply the name of the container app.
- location: The region where the container app needs to be created.
- registries: Because an image is being deployed, we also need to specify the registry from which the image must be pulled.
- When specifying the registry properties, it is also important to mention the identity that needs to be used to pull the image from the registry.
- template: Specifies the properties to spin up the container.
- containers:
- image: The Docker image that needs to be deployed.
- resources: The CPU and memory requirements to run the containers.
- name: Name for the container inside the container app.
- containers:
- ingress: This comes into picture when an HTTP API is being hosted. If the API needs to take traffic, it needs to be behind an endpoint, and specifying an ingress creates one.
- dependsOn: Make sure there is a property that waits for the managed environment to be created and then only creates the container app.
Command to deploy the ARM template:
az deployment group create \
--resource-group foo-bar-rg \
--template-file <path-to-template.json> \
--parameters <path-to-parameters.json>
Securing Your Container Apps
- First and foremost, attach a VNet to the app. VNets are the fundamental building blocks to provide private networks in Azure. By attaching a Network Security Group (NSG) to the VNet, one can control both the ingress and egress traffic flowing through the container app. This is the best strategy to block malicious traffic.
- Second, the images being deployed must be signed. This is to ensure supply chain security. There are open-source tools like Trivy that can help sign Docker images.
- If possible, use private container registries. The benefit is that no one other than your organization/team can push images to the registry.
- When there are public-facing API's, it is recommended to put them behind an application gateway or a traffic manager to protect against denial-of-service attacks.
Scaling Infrastructure
Just like traditional VM infrastructure, we want our containerized workloads to scale seamlessly based on demand. Azure Container Apps makes this possible through KEDA (Kubernetes Event-Driven Autoscaler), enabling event-driven, automatic scaling down to zero and up to meet peak traffic.
The scaling rules can depend on HTTP traffic, CPU, memory, or any other resource utilization. For example, to create a container app with HTTP scale settings, use the following command.
az containerapp create \
--name foo-bar-container-app \
--resource-group foo-bar-resource-group \
--environment bar-environment \
--image foo.acr.io/bar-image
--min-replicas 0 \
--max-replicas 5 \
--scale-rule-name azure-http-rule \
--scale-rule-type http \
--scale-rule-http-concurrency 300
Conclusion
Overall, Azure Container Apps are the way to go if you need to run microservices and event-driven workloads in containers without managing Kubernetes directly. With support for autoscaling and built-in observability, secure networking, it strikes the perfect balance between simplicity and flexibility.
Opinions expressed by DZone contributors are their own.
Comments