Azure Containers Tutorial
You will use Azure Container Registry (ACR), Azure Container Instances (ACI) Azure Container Apps (ACA) while exploring key features such as Secrets, Authentication/Authorization, Revisions and Volumes.
You can use the tutorial for preparing for the AZ-204 exam, or just to get hands-on experience with Azure container services.
Note: There will be costs of a few cents associated with running the resources in this tutorial, if you don’t have a free Azure account. I’m not responsible for any costs incurred.
Make sure to clean up resources after completing the tutorial. Instructions for cleanup are provided at the end of the tutorial. Also, check in the Azure Portal for any leftover resources.
Prerequisites
- Azure CLI installed and logged in (
az login) - Azure subscription
- Local docker installation for one step
- Basic Docker knowledge
1. Azure Container Registry (ACR)
Open your Azure CLI. You can find it in the Azure Portal in the top right corner under „Cloud Shell“. We will set environment variables for resource names to make it easier to reuse them later. They will be lost when you close the Cloud Shell, so make sure to not close it until you finish the tutorial.
Example:
RESOURCE_GROUP="myResourceGroup" ACR_NAME="myacr$(date +%s)"
Create an ACR
We will create a resource group and an Azure Container Registry to store our Docker images. Then we will clone a sample project from github that contains a Dockerfile, build the Docker image, and push it to our ACR.
RESOURCE_GROUP="myResourceGroup" ACR_NAME="myacr$(date +%s)" az group create --name $RESOURCE_GROUP --location eastus az acr create \ --resource-group $RESOURCE_GROUP \ --name $ACR_NAME \ --sku Standard \ --admin-enabled true
Clone the repo and build the Docker image and push to the ACR using ACR Tasks.
git clone https://github.com/wagnersoftware/Azure.NetCore.Demos.git cd Azure.NetCore.Demos/Microservice1Api.Containers.Demo az acr build \ --registry $ACR_NAME\ --image microservice1api/containersdemo:v1 \ .
Verify the image in ACR
List the images in your ACR
az acr repository list \ --name $ACR_NAME \ --output table
2. Azure Container Instances (ACI)
Start a simple container instance from ACR image
We will create a simple container instance from the image we just pushed to our ACR.
Get the credentials for your ACR
ACR_USERNAME=$(az acr credential show \ --name $ACR_NAME \ --query "username" \ -o tsv) ACR_PASSWORD=$(az acr credential show \ --name $ACR_NAME \ --query "passwords[0].value" \ -o tsv)
Start the container instance from the ACR image
Note: the dns-name-label must be unique for the region. If you get an internal server error, you probably must assign another dns-name
CONTAINER_INSTANCE_NAME="myaci" az container create \ --resource-group $RESOURCE_GROUP \ --name $CONTAINER_INSTANCE_NAME \ --image $ACR_NAME.azurecr.io/microservice1api/containersdemo:v1 \ --cpu 1 \ --memory 1.5 \ --registry-login-server $ACR_NAME.azurecr.io \ --registry-username $ACR_USERNAME \ --registry-password $ACR_PASSWORD \ --restart-policy Never \ --dns-name-label aci-demo-$RANDOM \ --os-type Linux \ --ports 8080 \ --environment-variables ASPNETCORE_URLS=http://+:8080
Verify that the container is running
Print Full Qualified domain name (FQDN)
FQDN=$(az container show \ --name $CONTAINER_INSTANCE_NAME \ --resource-group $RESOURCE_GROUP \ --query "ipAddress.fqdn" \ -o tsv) CONTAINER_URI="http://$FQDN:8080/swagger/index.html" echo $CONTAINER_URI
Open the URL in your browser to see the Swagger UI of the running container instance.
Note: https will not work, because we don’t use a certificate
Create ACI with file Azure File Share mount
We will deploy a nginx container with an Azure File Share mount to serve static content. For this test, we must pull the nginx image locally from Docker Hub, because ACI has no access to Docker Hub.
We will push the Image to our ACR and then create the ACI from there.
First we need the credentials for our ACR. Enter the following commands in your Azure CLI and note the username and password.
Note the credentials for later use.
ACR_USERNAME=$(az acr credential show --name $ACR_NAME --query "username" -o tsv) ACR_PASSWORD=$(az acr credential show --name $ACR_NAME --query "passwords[0].value" -o tsv) echo $ACR_USERNAME echo $ACR_PASSWORD echo $ACR_NAME
Now we must run Docker on the local machine to pull the nginx image from Docker Hub, tag it and push it to our ACR.
Open a local terminal and pull the nginx image from Docker Hub, tag it and push it to your ACR. Replace <ACR_NAME>, <ACR_USERNAME>, and <ACR_PASSWORD> with your values.
docker login <ACR_NAME>.azurecr.io -u <ACR_USERNAME> -p <ACR_PASSWORD> docker pull nginx:latest docker tag nginx:latest <ACR_NAME>.azurecr.io/nginx:latest docker push <ACR_NAME>.azurecr.io/nginx:latest
Next, we need to create a Storage Account for the Azure File Share. Go back to your Azure CLI and enter the following command.
STORAGE_ACCOUNT_NAME="mystorageaccount$RANDOM" az storage account create \ --name $STORAGE_ACCOUNT_NAME \ --resource-group $RESOURCE_GROUP \ --location eastus \ --sku Standard_LRS
Get the storage account key and create a file share named myshare.
STORAGE_KEY=$(az storage account keys list \ --resource-group $RESOURCE_GROUP \ --account-name $STORAGE_ACCOUNT_NAME \ --query "[0].value" -o tsv) FILE_SHARE_NAME="myshare" az storage share create \ --name $FILE_SHARE_NAME \ --account-name $STORAGE_ACCOUNT_NAME \ --account-key $STORAGE_KEY
Create the ACI with the Azure File Share mount. When you are asked for Credentials, enter the ACR username and password you got earlier.
FILE_MOUNT_CONTAINER_NAME="filemount-demo" az container create \ --resource-group $RESOURCE_GROUP \ --name $FILE_MOUNT_CONTAINER_NAME \ --image $ACR_NAME.azurecr.io/nginx:latest \ --cpu 1 \ --memory 1.5 \ --ports 80 \ --dns-name-label $FILE_MOUNT_CONTAINER_NAME-$RANDOM \ --os-type Linux \ --azure-file-volume-account-name $STORAGE_ACCOUNT_NAME \ --azure-file-volume-account-key $STORAGE_KEY \ --azure-file-volume-share-name $FILE_SHARE_NAME \ --azure-file-volume-mount-path /usr/share/nginx/html
Verify the ACI with File Share mount
We will upload a simple test.html file to the Azure File Share and verify that nginx serves this file.
Create the file:
echo "Hello from Azure File Share!" > test.html
Upload the file to the Azure File Share:
az storage file upload \ --share-name $FILE_SHARE_NAME \ --source test.html \ --path test.html \ --account-name $STORAGE_ACCOUNT_NAME \ --account-key $STORAGE_KEY
Get the FQDN of the ACI:
FQDN=$(az container show \ --name $FILE_MOUNT_CONTAINER_NAME \ --resource-group $RESOURCE_GROUP \ --query "ipAddress.fqdn" \ -o tsv) FILE_SHARE_DEMO_URI="http://$FQDN/test.html" echo $FILE_SHARE_DEMO_URI
Open the URL in your browser. You should see the message „Hello from Azure File Share!“.
Use secrets as environment variables
We will create a Key Vault, store a secret, and reference it in an ACI environment variable.
Note: This is not best practice for production workloads. Consider using Managed Identities and Key Vault references instead.
With a managed identity, you can avoid storing secrets inside the container instance, the application will directly access the key vault instance.
Since you must modify the code to use managed identities, we will use this simple approach for demonstration purposes only.
Create Key Vault
KEYVAULT_NAME="myKeyVault$RANDOM" az keyvault create \ --name $KEYVAULT_NAME \ --resource-group $RESOURCE_GROUP \ --location eastus
Set access policy to allow the current user to read and write secrets
SUBSCRIPTION_ID=$(az account show --query id -o tsv) USER_OID=$(az ad signed-in-user show --query id -o tsv) az role assignment create \ --role "Key Vault Secrets Officer" \ --assignee-object-id $USER_OID \ --assignee-principal-type User \ --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/\ providers/Microsoft.KeyVault/vaults/$KEYVAULT_NAME"
Store a secret in Key Vault
SUBSCRIPTION_ID=$(az account show --query id -o tsv) USER_OID=$(az ad signed-in-user show --query id -o tsv) az role assignment create \ --role "Key Vault Secrets Officer" \ --assignee-object-id $USER_OID \ --assignee-principal-type User \ --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/\ providers/Microsoft.KeyVault/vaults/$KEYVAULT_NAME"
Read the secret value and store it in a variable
SECRET_VALUE=$(az keyvault secret show \
--vault-name $KEYVAULT_NAME \
--name mysecret \
--query value -o tsv)
Create the ACI with the secret as environment variable. The secret will be stored encrypted inside the container instance.
ACI_NAME="myaci-secure" az container create \ --resource-group $RESOURCE_GROUP \ --name $ACI_NAME \ --image $ACR_NAME.azurecr.io/microservice1api/containersdemo:v1 \ --cpu 1 \ --memory 1.5 \ --registry-login-server $ACR_NAME.azurecr.io \ --registry-username $ACR_USERNAME \ --registry-password $ACR_PASSWORD \ --os-type Linux \ --ports 8080 \ --restart-policy OnFailure \ --dns-name-label aci-demo-$RANDOM \ --environment-variables API_KEY=myapikey ASPNETCORE_URLS=http://+:8080 \ --secure-environment-variables CONNECTION_STRING=$SECRET_VALUE
Verify secret in ACI
We open the bash shell of the running container and check if the secret environment variable is set.
az container exec \ --resource-group $RESOURCE_GROUP \ --name $ACI_NAME \ --exec-command "/bin/sh"
Inside the container shell, run:
echo $CONNECTION_STRING
You should see the value „MY_SECRET“ printed. Type exit to leave the container shell.
3. Azure Container Apps (ACA)
Create a container app with multiple revisions and traffic splitting
We will create an Azure Container App and deploy a new revision, then we will split traffic between revisions.
First, make sure to install the Container Apps extension if you haven’t already. We must also register the Microsoft.App provider.
az extension add --name containerapp --upgrade az provider register --namespace Microsoft.App az provider register --namespace Microsoft.OperationalInsights
Create the container app environment. Apps in the same environment share:
- Virtual network
- Log Analytics workspace
- Dapr configuration
ENV_NAME="aca-demo-env" az containerapp env create \ --name $ENV_NAME \ --resource-group $RESOURCE_GROUP \ --location eastus
AUTH_APP_NAME="my-container-app" az containerapp create \ --name $AUTH_APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $ENV_NAME \ --image mcr.microsoft.com/azuredocs/containerapps-helloworld:latest \ --target-port 80 \ --ingress external \
Create the container app
AUTH_APP_NAME="my-container-app" az containerapp create \ --name $AUTH_APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $ENV_NAME \ --image mcr.microsoft.com/azuredocs/containerapps-helloworld:latest \ --target-port 80 \ --ingress external \
Set revision mode to multiple, the default is single. This will allow us to have multiple revisions and split traffic between them.
In single mode, only one active revision is allowed.
az containerapp revision set-mode --name $AUTH_APP_NAME \
--resource-group $RESOURCE_GROUP \
--mode multiple
Enable Authentication with Microsoft Entra ID
Now we must set up Microsoft Entra ID (formerly Active Directory) authentication for the container app.
Get the FQDN of the container app and set the redirect URI.
FQDN=$(az containerapp show \ --name $AUTH_APP_NAME \ --resource-group $RESOURCE_GROUP \ --query "properties.configuration.ingress.fqdn" \ -o tsv) REDIRECT_URI="https://$FQDN/.auth/login/aad/callback" echo $REDIRECT_URI
Get the client ID and tenant ID from your Microsoft Entra ID app registration and set up authentication for the container app.
CLIENT_ID=$(az ad app create \ --display-name "MyContainerApp" \ --sign-in-audience AzureADMyOrg \ --web-redirect-uris $REDIRECT_URI \ --query appId -o tsv) TENANT_ID=$(az account show --query tenantId -o tsv)
Get a client secret for the app registration
CLIENT_SECRET=$(az ad app credential reset \ --id $CLIENT_ID \ --display-name "containerapp-secret" \ --query password -o tsv)
Enable id_token in the authentication settings
az ad app update \ --id $CLIENT_ID \ --enable-id-token-issuance true
Add Authentication to Container App
az containerapp auth microsoft update \ --resource-group $RESOURCE_GROUP \ --name $AUTH_APP_NAME \ --client-id $CLIENT_ID \ --client-secret $CLIENT_SECRET \ --issuer https://sts.windows.net/$TENANT_ID/
Get Revision name
REVISION_NAME=$(az containerapp revision list \ --name $AUTH_APP_NAME \ --resource-group $RESOURCE_GROUP \ --query "[?properties.active==\`true\`].name" \ -o tsv)
Restart revision:
az containerapp revision restart \ --name $AUTH_APP_NAME \ --resource-group $RESOURCE_GROUP \ --revision $REVISION_NAME
Verify Authentication
Get FQDN
AAD_LOGIN_URL="https://$FQDN/.auth/login/aad" echo "Login URL: $AAD_LOGIN_URL"
Open the Login URL in your browser and authenticate with your Microsoft account. You can test the authentication by open a private/incognito browser window and accessing the AAD_LOGIN_URL. After successful authentication, you should be redirected to the container app and see the default welcome message.
Deploy a new revision and split traffic
Deploy a new revision by setting a revision-scope property. We set a new revision-suffix to automatically deploy a new revision.
az containerapp update \ --name $AUTH_APP_NAME \ --resource-group $RESOURCE_GROUP \ --revision-suffix "manualrev-$(date +%s)" \
Now we can list the revisions. This will show the revision names, that we can use to split traffic.
az containerapp revision list \ --name $AUTH_APP_NAME \ --resource-group $RESOURCE_GROUP \ --output table
Split traffic between the two revisions. Replace <REVISION_NAME_1> and <REVISION_NAME_2> with the actual revision names from the previous step.
az containerapp ingress traffic set \ --name $AUTH_APP_NAME \ --resource-group $RESOURCE_GROUP \ --revision-weight <REVISION_NAME_1>=50 <REVISION_NAME_2>=50
When you refresh the browser multiple times, you are routed to both revisions.
4. Cleanup
- delete resource group
az group delete --name $RESOURCE_GROUP --yes --no-wait
- list all folders and files in root directory
cd ~ ls -la
- delete demo app
rm -rf ~/Azure.NetCore.Demos
- confirm deletion
ls -la


Schreibe einen Kommentar