With Azure Container Apps (ACA), Microsofts latest serverless runtime for containerized workloads in Azure, we can customize certain aspects of traffic routing on the ingress level. This article will look at traffic split and configure a relatively simple API to leverage a tailored traffic split configuration in Azure Container Apps. If you are new to Azure Container Apps, consider reading my article “Introduction to Azure Container Apps” first.
What is traffic split
First, let’s quickly revisit what traffic split is. Traffic split is a mechanism that routes configurable percentages of incoming requests (traffic) to various downstream services. See the following picture, which shows the relation between ACA ingress controller, traffic split configuration, and downstream services (or revisions in the case of ACA):
The sample application
To demonstrate traffic split, we will take a small API written in Go. The API responds to HTTP calls being issued to the root route (/
). It returns a simple JSON structure so we can see which version of the API handled the request. The sample repository contains two dedicated versions of that API. See the following snippet demonstrating requests to both versions of the API while running locally:
# query version 1
curl --silent http://127.0.0.1:8080/ | jq
{
"version": "v1.0.0"
}
# query version 2
curl --silent http://127.0.0.1:8081/ | jq
{
"version": "v2.0.0"
}
Create the core infrastructure
First, let’s create the necessary core infrastructure in Azure. Although I use Azure CLI, you can also create the resource group and the Azure Container Registry (ACR) using the Azure Portal.
rgName="rg-blog-sample"
acrName="blogsample"
location="northeurope"
# create a resource group
az group create -n $rgName -l $location
# create an Azure Container Registry
az acr create -n $acrName -g $rgName -l $location --sku Basic --admin-enabled true
In the snippet above, an ACR instance was created with admin-enabled
set to true
. In a real-world scenario, you should definitely use ACR tokens instead of enabling the admin account. However, this requires an ACR Premium SKU.
Publishing Containers to Azure Container Registry
Now that we have an ACR instance in place, we can build and push our API container images.
# build container images
docker build ./v1 -t $acrName.azurecr.io/sample-api:1.0.0
docker build ./v2 -t $acrName.azurecr.io/sample-api:2.0.0
# login to ACR
az acr login -n $acrName
# push container images
docker push $acrName.azurecr.io/sample-api:1.0.0
docker push $acrName.azurecr.io/sample-api:2.0.0
You should now have both container images stored securely in ACR. Next, we have to grab the password for the administrative account:
# Grab Admin Password from ACR
acrPassword=$(az acr credential show -n $acrName --query "passwords[0].value" -o tsv)
We will hand over this password later to Azure Container Apps (ACA), because the service must authenticate against ACR.
Deploy revisions to Azure Container Apps
To configure traffic split in ACA, we need at least two active revisions of our API. The first revision will use the container image sample-api:1.0.0
. For the second revision, we will deploy the container image sample-api:2.0.0
.
By default, ACA will only have one active revision per container app. However, we can customize this behavior by setting activeRevisionsMode
to multiple
on our container app. This is done in ./bicep/container-app.bicep:26
:
// omitted
properties: {
kubeEnvironmentId: containerAppEnvironmentId
configuration: {
activeRevisionsMode: 'multiple'
// omitted
The repository contains all necessary Bicep manifests to deploy a container app to ACA (including downstream Log Analytics Workspace) and the required ACA environment. If you want to learn more about the deployment, read my post on “Deploying Azure Container Apps with Bicep”.
Deploy the initial revision
We can issue a deployment using Azure CLI on the scope of the previously created resource group:
# Deploy initial revision
az deployment group create -n golang-api -g $rgName \
--template-file ./bicep/main.bicep \
-p containerImage=$acrName.azurecr.io/sample-api:1.0.0 \
containerPort=5000 \
registry=$acrName.azurecr.io \
registryUsername=$acrName \
registryPassword=$acrPassword
Deploy the preview revision
Deploying the second revision is pretty much identical to deploying the initial revision; the only difference is that we provide a different container image tag as a parameter:
# Deploy initial revision
az deployment group create -n golang-api -g $rgName \
--template-file ./bicep/main.bicep \
-p containerImage=$acrName.azurecr.io/sample-api:2.0.0 \
containerPort=5000 \
registry=$acrName.azurecr.io \
registryUsername=$acrName \
registryPassword=$acrPassword
Verify revisions in Azure Container Apps
We can use Azure CLI to grab a list of all revisions for a particular container app in ACA, as shown here:
az containerapp revision list -n golang-api \
-g $rgName -o table
Name CreatedTime Active Replicas
------------------- ------------------------- -------- ----------
golang-api--s25y6d3 2021-11-04T17:16:02+00:00 True 1
golang-api--hjctnnw 2021-11-04T17:18:25+00:00 True 1
Note down the revision names from the table. We need those to configure traffic split.
Traffic split in Azure Container Apps
Modifying traffic split configuration and re-deploying a container app to ACA does not create a new revision. So we can easily update the Bicep manifest and provide a tailored traffic split configuration to route 80% of all requests to version 1.0.0
and 20% to version 2.0.0
of our API.
Please keep in mind that the sum of all splits must be 100. Otherwise, Azure will reject the deployment.
Traffic split is configured on the ingress controller used to route requests to our containers. That said, update ./bicep/container-app.bicep
and provide the following traffic split configuration. (Note: Remember to replace the revision names with yours)
ingress: {
external: useExternalIngress
targetPort: containerPort
traffic: [
{
revisionName: 'golang-api--s25y6d3' // sample-api:1.0.0
weight: 80
}
{
revisionName: 'golang-api--hjctnnw' // sample-api:2.0.0
weight: 20
}
]
}
Apply traffic split in Azure Container Apps
With your custom traffic split configuration in place, we can issue another deployment using Azure CLI. Using the same values containerImage
and containerPort
as we used to deploy the second revision is important. Updating just the configuration
of a container app (speaking of ingress
, registries
, secrets
, and activeRevisionsMode
) is considered as an application-scope change. Application-scope changes won’t result in ACA creating a new revision.
# Redeploy bicep manifest
az deployment group create -n golang-api -g $rgName \
--template-file ./bicep/main.bicep \
-p containerImage=$acrName.azurecr.io/sample-api:2.0.0 \
containerPort=5000 \
registry=$acrName \
registryUsername=$acrName \
registryPassword=$acrPassword
Test traffic split behavior
To test traffic split behavior, we have to get the FQDN associated with our container app:
# query FQDN
fqdn=$(az deployment group show -g $rgName --query properties.outputs.fqdn.value \
-n golang-api -o tsv)
You can test traffic split in the browser by just hitting the FQDN a couple of times. However, you can also use curl
. The repository contains a simple script that will issue 10 requests to the URL specified as arg. Let’s do that to verify traffic split works as expected:
# Let's issue 10 requests to see traffic split in action
./scripts/test.sh $fqdn
{"version":"v1.0.0"}
{"version":"v2.0.0"}
{"version":"v2.0.0"}
{"version":"v1.0.0"}
{"version":"v1.0.0"}
{"version":"v1.0.0"}
{"version":"v1.0.0"}
{"version":"v1.0.0"}
{"version":"v1.0.0"}
{"version":"v1.0.0"}
Excellent. As you can see from the output of test.sh
, our traffic split configuration works as expected, and ACA routes requests different revisions according to our specification.
Conclusion
Being able to configure traffic split in Azure Container Apps is great. Multi-Revision mode is something you can active also after the container app has been deployed to ACA, so there is no need to re-create an already running instance of your container app. Once that’s the case, we can route requests randomly to different revisions of an application running in ACA.
The current implementation of traffic split in ACA is straightforward. We can weight traffic between multiple downstream revisions. However, what I need is the possibility to create HTTPRouteGroup
instances according to the SMI Spec.