Daniel Wagner – .NET Software Entwicklung

Azure Containers Tutorial

Azure Containers Tutorial

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

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert