In a previous post we covered Azure Container Instances (ACI) across 3 regions in under 30 seconds with Azure Traffic Manager which we deployed using the Azure CLI. The Azure CLI is very approachable, often more efficient than the Azure Portal after a gentle learning curve, and a great tool for much of our day to day work in Azure. We can also re-use snippets and scripts, commit them to source control, etc.
The Azure CLI is an imperative approach to working with Azure resources. We can make an imperative approach easier to manage by making our infrastructure and deployments as close to immutable as possible.
It can sometimes be helpful to deploy into a single Resource Group as a logical unit and recreate it by deleting the group and re-creating it afresh rather than modifying it in place. A naming convention for Resource Groups, such as date prefix in the format ‘%y%m%d’, or 180301 for 1 April 2018, can also ensure Resource Groups are easy to find, “versioned”, sorted, and that we can easily delete old deployments used for test/dev, etc. I tend to mark “disposable” resources with a day prefix (180301) and “persistent” resources with a month prefix (180300).
In this post we will explore a declarative approach to working with Azure resources. Azure Resource Manager (ARM) templates, and the open source, cloud-agnostic, Terraform are just two available to us. Terraform will be covered in a future post, and we will use Azure Resource Manager (ARM) templates to replicate our Azure Container Instances (ACI) across 3 regions in under 30 seconds with Azure Traffic Manager deployment ARM instead of the CLI.
Azure Resource Manager (ARM)
I use Visual Studio Code and its Azure Resource Manager Tools extension to edit ARM templates. I extended the Azure Container Instances - Linux container with public IP (github) template from the github.com/Azure/azure-quickstart-templates repository to deploy an Azure Traffic Manager and Azure Container Instances across up to 3 regions. The complete ARM template, azuredeploy.json, is available in our GitHub repository github.com/aaronmsft/aaronmsft-com/.
This template uses some relatively advanced functionality. We begin with parameters element where we have set some sensible defaults for values are re-used throughout. We define an array of resources. The first resource is our Traffic Manager Profile (Microsoft.Network/trafficManagerProfiles) which would typically have an array of Endpoints (Microsoft.Network/trafficManagerProfiles/externalEndpoints) embedded in it. In many cases we would use basic Resource Iteration via the copy element to create them. The copyIndex function return the index of an iteration loop and enables us to generate a unique name for each resource with the help of the concat, parameters, and the deterministic uniqueString function.
However, because we are creating a configurable number of Container Groups (Microsoft.ContainerInstance/containerGroups) and their respective Endpoints across multiple regions, we can define the Endpoints resources outside of the Traffic Manager Profile so that we can iterate per region. We use dependsOn to indicate that the Endpoints depend on the Traffic Manager Profile, and a condition to control when Endpoints or Container Groups will be deployed for a particular region (in this case when the count for that region is greater than zero). Our ARM template had a significant performance advantage over our Azure CLI example because both the Endpoints and the Container Groups are created in parallel, whereas the CLI created them serially in a loop. This enables us to create a large number of Container Groups in mere seconds.
Finally, the outputs element returns the fully qualified domain name (fqdn) of our Traffic Manager Profile. Outputs are included the JSON response when we deploy our template via the Azure CLI. One we have an ARM template we can deploy it via the Portal, CLI, API, CI/CD platforms and more. Next we will cover deployment via the Azure CLI and Azure Portal.
Deploy ARM Template via Azure CLI
The az group deployment create command can be used to deploy a template from a local file or a remote URL as follows:
RESOURCE_GROUP='180400-test' LOCATION='eastus' # create a resource group az group create -l $LOCATION -n $RESOURCE_GROUP # deploy a local template with default parameters az group deployment create -g $RESOURCE_GROUP --mode Complete --template-file azuredeploy.json # deploy our template from our github repo with default parameters az group deployment create -g $RESOURCE_GROUP --mode Complete --template-uri https://raw.githubusercontent.com/aaronmsft/aaronmsft-com/master/azure-container-instances-arm/azuredeploy.json # deploy our template from our github repo with parameters az group deployment create -g $RESOURCE_GROUP --mode Complete --template-uri https://raw.githubusercontent.com/aaronmsft/aaronmsft-com/master/azure-container-instances-arm/azuredeploy.json --parameters region1Count=3 region2Count=3 region3Count=3 # deploy an empty template which will delete the resources in our resource group az group deployment create -g $RESOURCE_GROUP --mode Complete --template-uri https://raw.githubusercontent.com/aaronmsft/aaronmsft-com/master/azure-container-instances-arm/empty.json
Deploy ARM Template via Azure Portal
One of the most user friendly ways to deploy Custom Templates is via the Azure Portal. It requires no tooling and allows parameters to be provided or overridden at the time of deployment via a friendly UI.
Templates can be passed to the Azure Portal via a URL. You may recognize this from the “Deploy to Azure” links for templates in the github.com/Azure/azure-quickstart-templates repository. All we need to do is encode the URL and prefix it with
https://portal.azure.com/#create/Microsoft.Template/uri/. We can do this quickly and easily with bash and jq:
TEMPLATE_URL='https://raw.githubusercontent.com/aaronmsft/aaronmsft-com/master/azure-container-instances-arm/azuredeploy.json' OUTPUT_URL='https://portal.azure.com/#create/Microsoft.Template/uri/'$(echo $TEMPLATE_URL | jq -s -R -r @uri ) echo $OUTPUT_URL
# dig + curl DNS_NAME='tm-acqa4vg2wossw' # name server sourced via: dig +short trafficmanager.net NS while true; do CONTAINER_HOST=$(dig +short @tm1.msft.net $DNS_NAME'.trafficmanager.net' | sed -e 's/\.$//') curl --connect-timeout 1 'http://'$CONTAINER_HOST'/host' sleep 1 done
The output should look similar to the following:
Hostname: caas-1c1118cfee9f444393a288a71a7264b8-508147227-pzb8d Hostname: caas-92109c5896c54cd6912c2c6a72143f9f-768370293-w8mnk Hostname: caas-9ffd7f9d086b453792bcea617221e957-3945615887-kkq5s Hostname: caas-92109c5896c54cd6912c2c6a72143f9f-768370293-w8mnk Hostname: caas-1c1118cfee9f444393a288a71a7264b8-508147227-pzb8d
Any and all feedback and questions are welcome. Don’t hesitate to reach out via twitter, @as_w, and my DMs are always open!