Azure Container Apps
Lately I’ve been playing around with both Azure App Service and Azure Kubernetes Service along with Terraform - hopefully I’ll throw together a blog post or two about my experiences with that later. To sum up some of my experiences working with this: There has to be an easier way.
Azure Container Apps announced
Now Microsoft has announced a new general-purpose serverless container platform: Azure Container Apps. Container Apps is what seems like a fully managed platform for microservice applications that runs on top of Kubernetes and open-source technologies like KEDA and Dapr.
The new service supports a broad range of usage scenarios, including
- Microservices over HTTP or gRPC
- HTTP APIs and websites
- Event processing workers
- Long-running background jobs
Having played around with AKS, which in itself is a managed platform, there is still quite a lot infrastructure-related things to take care of. Now, I haven’t worked long enough with AKS or other Kubernetes-offerings to be able now exactly what we loose when it comes to flexibility. But the threshold of running containers with the ability to autoscale, run different revisions of containers, enable HTTPS ingress without worrying about other Azure infrastructure or splitting traffic across different versions of your application for Blue/Green deployment or A/B-testing is a lot lower.
Create a simple Container App
Checking out Azure Container apps seemed like a good time to try out Bicep. My reasoning was that was that we actually just need a resource group, a log analytics workspace, container app environment and the container app itself - so not that many resources.
The resources need to be put into a resource group, so you need to create that:
az group create -n containerAppRg -l northeurope
At the time of writing the Azure Container App service is only available in canadacentral and northeurope.
Log analytics workspace: create la-workspace.bicep
param location string
param name string
resource la-workspace 'Microsoft.OperationalInsights/workspaces@2020-03-01-preview' = {
name: name
location: location
properties: any({
retentionInDays: 30
features: {
searchVersion: 1
}
sku: {
name: 'PerGB2018'
}
})
}
output clientId string = la-workspace.properties.customerId
output clientSecret string = la-workspace.listKeys().primarySharedKey
Container App Environment: create containerenv.bicep
param name string
param location string
param logAnalyticsClientId string
param logAnalyticsClientSecret string
resource containerappenv 'Microsoft.Web/kubeEnvironments@2021-02-01' = {
name: name
location: location
properties: {
type: 'managed'
internalLoadBalancerEnabled: false
appLogsConfiguration: {
destination: 'log-analytics'
logAnalyticsConfiguration: {
customerId: logAnalyticsClientId
sharedKey: logAnalyticsClientSecret
}
}
}
}
output id string = containerappenv.id
Container App Environment: create containerapp.bicep
param location string
param name string
param containerEnvId string
param containerImage string
param useExternalIngress bool = false
param containerPort int
resource containerApp 'Microsoft.Web/containerApps@2021-03-01' = {
name: name
kind: 'containerapp'
location: location
properties: {
kubeEnvironmentId: containerEnvId
configuration: {
ingress: {
external: useExternalIngress
targetPort: containerPort
}
}
template: {
containers: [
{
image: containerImage
name: name
env: envVars
}
]
scale: {
minReplicas: 0
}
}
}
}
Wrap it up with main.bicep
param location string = resourceGroup().location
param envName string = 'containerApp-test'
module law 'la-workspace.bicep' = {
name: 'log-analytics-workspace'
params: {
location: location
name: 'law-${envName}'
}
}
module containerAppEnv 'containerenv.bicep' = {
name: 'containerAppEnv'
params: {
name: envName
location: location
lawClientId: law.outputs.clientId
lawClientSecret: law.outputs.clientSecret
}
}
module containerApp 'containerapp.bicep' = {
name: 'containerapptest'
params: {
name: 'containerapptest'
location: location
containerEnvId: containerAppEnv.outputs.id
containerImage: 'mcr.microsoft.com/azuredocs/aks-helloworld:v1'
containerPort: 80
envVars: [
{
name: 'TITLE'
value: 'Welcome to your new Container App!'
}
]
useExternalIngress: true
}
}
Now deploy the solution
az deployment group create -n container-app -g containerAppRg --template-file ./main.bicep
When this has gone through go check your resources in the portal. From the Container App that was created find the “Application Url” in the overview blade and test the url. If everything went well you should see at simple web page with the text “Welcome to your new Container App!”
Finishing thoughts
Having done a bit of work with Terraform lately I was surprised how easy using Bicep felt. I will definitively look into using Bicep more as I am not doing anything else than work on Azure anyways. Having done pretty much the same drill getting the container in the example above with Terraform and AKS it is a lot simpler with Container Apps. The ability to enable the HTTPS Ingress in particular is a real time saver - this cost me a lot of time when trying to set this up correctly in AKS with cert-manager, NGINX and Let’s Encrypt.
I’ll hopefully be back with more on my experiences trying out AKS and Terraform at a later date!