Azure Container Apps are relatively new in Azure, still in Preview as of writing this blog post. In this blog, I will be deploying to Azure Container App from Azure Container Registry using a CI/CD Azure DevOps Pipeline and Azure CLI.
You may be thinking, what is the difference between a Container App and Container Instance?
- Container instance is a single isolated pod/container on demand
- Microsoft reference a container instance as a lower-level “building block” option compared to Container Apps
- Scaling and load balancing is not provided with Container Instances
- Container Apps provide the ability to scale & use of certificates on top of your container(s)
The Deployment Overview
Once we commit a change to the source code – we want the Container Application to have been deployed with the latest commit automatically.
Lets look at this further with the below diagram:
- Developers commits code change to Azure Repo
- Azure Pipeline trigger, triggers a build with merge to main branch happens
- Azure Pipeline flows throw the stages
- Azure Pipeline Builds and pushes latest change in code to a new image within the Azure Container Registry
- Stage
deploy_container_app
will pull the latest image from Azure Container Registry and deploy this to Azure Container Instance - Log Analytics can be reviewed regarding logs from the container environment
- Back to the developer to commit next change and this process happens again
Azure DevOps Pipeline Overview
The pipeline will consist of 4 stages:
create_acr
will create resource group , container registry to where the sample application will be uploaded to and also log analytics workspace that the container environment will connect tobuild
will build the sample application and push to the Container Registry created increate_acr
stagecreate_container_environment
Will create the container environment to host the container app that will be deployed in the final stagedeploy_container_app
will deploy the container app from image created inbuild
stage
Please note:- As the date of this blog post; there is no set tasks in Azure DevOps for deploying container apps – I will be using provided Az CLI
Azure DevOps Pipeline Setup
For this setup, I will go through each of the 4 stages of the pipeline
Prior to setup, a variable group is required, this will store the Azure Container Registry Login Password. Check out this blog regarding Referencing Variable Groups in Azure DevOps Pipeline Templates
Variable group azure-container-app
has been created
create_acr stage
The first stage & base of the pipeline; this stage will be the initial base of the Azure DevOps Pipeline along with creating:
- Azure Resource Group
azure-container-rg
that will be used to deploy all additional resources into - Azure Container Registry
tamopsacrcontainers
that will be used to store the sample application deployed in stagebuild
- Azure Log Analytics
tamopsacrcontainersla
to send container environment data
Notce the trigger reference(highlighted)? This will run the pipeline each time there is a merge to the main branch
name: $(BuildDefinitionName)_$(date:yyyyMMdd)$(rev:.r)
trigger:
batch: true
branches:
include:
- main
variables:
- group: azure-container-app
stages :
- stage: create_acr
jobs:
- job: "create_acr"
steps:
- task: AzureCLI@2
displayName: 'Deploy RG and ACR'
inputs:
azureSubscription: 'thomasthorntoncloud'
scriptType: bash
scriptLocation: inlineScript
addSpnToEnvironment: true
inlineScript: |
#!/bin/bash
RESOURCE_GROUP="azure-container-rg"
LOCATION="westeurope"
LOG_ANALYTICS_WORKSPACE="tamopsacrcontainersla"
ACR="tamopsacrcontainers"
az group create -l $LOCATION -n $RESOURCE_GROUP
az acr create -g $RESOURCE_GROUP -n $ACR --sku basic --admin-enabled true
az monitor log-analytics workspace create -g $RESOURCE_GROUP --workspace-name $LOG_ANALYTICS_WORKSPACE
Run this initial stage, you will have deployed successfully the three resources
As the Azure Container Registry has been deployed with admin enabled, grab the password from the registry in Access Keys settings and store it within variable group as secret name acrpassword
Docker Registry Service Connection creation
With the ACR deployed, prior to adding the stage build
we will now create a Docker Registry service connection within Azure DevOps
Inside Azure DevOps -> Project settings -> Service Connections -> Docker Service Connection
Select relevant subscription & newly created Azure container registry
I will create the service connection with name: tamopsacrcontainers
– this will be referenced within the pipeline that will be created
build stage
Moving onto the build stage – this is where the sample app will be built and image pushed to container registry:
- Using the tag reference
$(Build.BuildId)
will tag the image with the latest BuildId – each time the pipeline runs, a new image is created – the beginning of CI/CD 🙂 containerRegistry
is referencing the service connection created above
- stage: Build
dependsOn: [create_acr]
displayName: Build sample app
jobs:
- job: Build
displayName: Build job
steps:
- task: Docker@2
displayName: Build and push sample app to container registry
inputs:
command: buildAndPush
repository: 'sampleapp'
dockerfile: '$(Build.SourcesDirectory)/aspnet-core-dotnet-core/Dockerfile'
containerRegistry: 'tamopsacrcontainers'
tags: '$(Build.BuildId)'
A successful run of this stage will upload the latest Image to Azure Container Registry as below
create_container_environment stage
Prior to deploying a Container App – a container environment is required
- stage: create_container_environment
dependsOn: [Build]
jobs:
- job: "deploy_app"
steps:
- task: AzureCLI@2
displayName: 'Create container app environment'
inputs:
azureSubscription: 'thomasthorntoncloud'
scriptType: bash
scriptLocation: inlineScript
addSpnToEnvironment: true
inlineScript: |
#!/bin/bash
az config set extension.use_dynamic_install=yes_without_prompt
az provider register --namespace Microsoft.App
RESOURCE_GROUP="azure-container-rg"
LOCATION="westeurope"
LOG_ANALYTICS_WORKSPACE="tamopsacrcontainersla"
CONTAINERAPPS_ENVIRONMENT="tamops-environment"
LOG_ANALYTICS_WORKSPACE_CLIENT_ID=`az monitor log-analytics workspace show --query customerId -g $RESOURCE_GROUP -n $LOG_ANALYTICS_WORKSPACE -o tsv | tr -d '[:space:]'`
LOG_ANALYTICS_WORKSPACE_CLIENT_SECRET=`az monitor log-analytics workspace get-shared-keys --query primarySharedKey -g $RESOURCE_GROUP -n $LOG_ANALYTICS_WORKSPACE -o tsv | tr -d '[:space:]'`
az containerapp env create \
-n $CONTAINERAPPS_ENVIRONMENT \
-g $RESOURCE_GROUP \
--logs-workspace-id $LOG_ANALYTICS_WORKSPACE_CLIENT_ID \
--logs-workspace-key $LOG_ANALYTICS_WORKSPACE_CLIENT_SECRET \
-l $LOCATION
Single environment or multiple? Have a look at some reasons to deploy container environments and reasons possibly for different environments (taken from docs.microsoft.com)
Reasons to deploy container apps to the same environment include situations when you need to:
- Manage related services
- Deploy different applications to the same virtual network
- Have applications communicate with each other using Dapr
- Have applications to share the same Dapr configuration
- Have applications share the same log analytics workspace
Reasons to deploy container apps to different environments include situations when you want to ensure:
- Two applications never share the same compute resources
- Two applications can’t communicate with each other via Dapr
deploy_container_app stage
The final stage 🙂 – where the container app will be deployed
- stage: deploy_container_app
dependsOn: [create_container_environment]
jobs:
- job: "deploy_app"
steps:
- task: AzureCLI@2
displayName: 'Deploy app to Container App'
inputs:
azureSubscription: 'thomasthorntoncloud'
scriptType: bash
scriptLocation: inlineScript
addSpnToEnvironment: true
inlineScript: |
#!/bin/bash
az config set extension.use_dynamic_install=yes_without_prompt
az provider register --namespace Microsoft.App
RESOURCE_GROUP="azure-container-rg"
LOG_ANALYTICS_WORKSPACE="tamopsacrcontainersla"
CONTAINERAPPS_ENVIRONMENT="tamops-environment"
az containerapp create \
--name tamopsapp \
--resource-group $RESOURCE_GROUP \
--environment $CONTAINERAPPS_ENVIRONMENT \
--image tamopsacrcontainers.azurecr.io/sampleapp:'$(Build.BuildId)' \
--min-replicas 2 \
--max-replicas 2 \
--registry-server tamopsacrcontainers.azurecr.io \
--registry-username tamopsacrcontainers \
--registry-password $(acrpassword) \
--target-port 80 \
--ingress 'external'
Please note, the above pipeline requires a change once ran initially – it has been highlighted, after first initial deployment az containerapp create
needs swapped to az containerapp update
with the following syntax
az containerapp update \
--name tamopsapp \
--resource-group $RESOURCE_GROUP \
--image scottishsummitcontainers.azurecr.io/sampleapp:'$(Build.BuildId)'
The final stage of the pipeline now complete!
A successful Azure Container App tamopsapp
has been deployed successfully!
Accessing the Application URL, will show the sample application
A simple change applied and merged, re-running the pipeline will display the latest change within your container app using az container app update
I really do think Azure Container Apps are going to awesome and used quite alot once they make their way out of Preview! Thank you for reading this blog post 🙂
What do you think of using GitHub Enterprise Server instead of Azure Repos?
Hi Robert,
It’s another recommended Git repository, I use github alot also for source control with Azure DevOps Pipelines.
That’s a huge bonus of Azure Devops, very flexible!
Thanks
Thomas
Interesting and awesome .. great job 👍
Thanks for the comment – glad you enjoyed it!
Those stages are not idempotent right? They will fail the next time you execute them.
Which stage?
Have tested this previously before releasing blog and it works fine.
Thanks
Thomas
I am new to Docker and Azure DevOps. Do you need to create a new container registry (step create_acr) every time you run the pipeline or can you use an existing ACR?
Missed this comment initially.. You can reuse an already created ACR
Thanks
Thomas