Endpoint: <registry>.azurecr.io/<repository>/<image-or-artifact>:<tag>
<repository>
is also known as <namepsace>
. It allows sharing a single registry across multiple groups within your organization. Can be multiple levels deep. Optional.
# Login to manage resources
az login
# Create a resource group
az group create --name $resourceGroup --location eastus
# Create Azure Container Registry
## https://learn.microsoft.com/en-us/azure/container-registry/container-registry-skus
## --sku {Basic,Standard,Premium} # 10, 100, 500GB; 💎: Concurrent operations, High volumes (⚡), Customer-Managed Key, Content trust for image tag signing, Private link
## Throttling: May happen if you exceed the registry's limits, causing temporary `HTTP 429` errors and requiring retry logic or reducing the request rate.
##
## [--default-action {Allow, Deny}] # 💎: Default action when no rules apply
##
## https://learn.microsoft.com/en-us/azure/container-registry/zone-redundancy
## [--zone-redundancy {Disabled, Enabled}] # 💎: Min 3 separate zones in each enabled region. The environment must include a virtual network (VNET) with an available subnet.
az acr create --resource-group $resourceGroup --name $registryName --sku Standard # ⭐: Production
# NOTE: High numbers of repositories and tags can impact the performance. Periodically delete unused.
# ACR Login: https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication
## - Interactive: Individual Entra ID login, Admin Account
## - Unatended / Headless: Entra ID Service Principal, Managed Identity for Azure Resources
## Roles: https://learn.microsoft.com/en-us/azure/container-registry/container-registry-roles?tabs=azure-cli
##
## 1) Individual login with Entra ID: Interactive push/pull by developers, testers.
## az login - provides the token. It has to be renewed every 3 hours
az acr login --name "$registryName" # Token must be renewed every 3 hours.
##
## 2) Entra ID Service Principal: Unattended push/pull in CI/CD pipelines
### Create service principal
#### Method 1: Short version that will setup and return appId and password in JSON format
az ad sp create-for-rbac --name $ServicePrincipalName --role AcrPush,AcrPull,AcrDelete --scopes /subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.ContainerRegistry/registries/$registryName
#### Method 2: Create a service principal and configure roles separately
az ad sp create --id $ServicePrincipalName
az role assignment create --assignee $appId --role AcrPush,AcrPull,AcrDelete --scope /subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.ContainerRegistry/registries/$registryName
az ad sp credential reset --name $appId # for method 2 password is not explicitly created, so we need to create (reset) it
#### Note: Password expires in 1 year.
az acr login --name $registryName --username $appId --password $password
##
## 3) Managed identities
az role assignment create --assignee $managedIdentityId --scope $registryName --role AcrPush,AcrPull,AcrDelete
## Now container instances / apps must use that managed identity to access this ACR (pull or push images)
##
## 4) Admin User: ❌. Interactive push/pull by individual developers.
### The admin account is provided with two passwords, both of which can be regenerated
az acr update -n $registryName --admin-enabled true # this is disabled by default
docker login $registryName.azurecr.io
# Tasks: https://learn.microsoft.com/en-us/azure/container-registry/container-registry-tasks-overview
## [--platform Linux {Linux, Windows}] # Linux supports all architectures (ex: Linux/arm), Windows: only amd64 (ex: Windows/amd64) - arch is optional
##
## - Quick task
az acr build --registry $registryName --image $imageName:$tag . # docker build, docker push
az acr run --registry $registryName --cmd '$registryName/$repository/$imageName:$tag' /dev/null # Run image (last param is source location, optional for non-image building tasks)
##
## - Automatically Triggered Task
### [--<operation>-trigger-enabled true] # CI on commit or pull-request
### [--schedule] # CRON schedule (⭐: OS/framework patching): https://learn.microsoft.com/en-us/azure/container-registry/container-registry-tasks-scheduled
az acr task create --name ciTask --registry $registryName --image $imageName:{{.Run.ID}} --context https://github.com/myuser/myrepo.git --file Dockerfile --git-access-token $GIT_ACCESS_TOKEN
az acr task create --name cmdTask --registry $registryName --cmd mcr.microsoft.com/hello-world --context /dev/null
### az acr task run --name mytask --registry $registryName # manually run task
##
## - Multi-step Task: granular control (build, push, when, cmd defined as steps) - https://learn.microsoft.com/en-us/azure/container-registry/container-registry-tasks-reference-yaml
### NOTE: --file is used for both multi-step task and Dockerfile
az acr run --file multi-step.yaml https://github.com/Azure-Samples/acr-tasks.git
az acr task create --file multi-step.yaml --name ciTask --registry $registryName --image $imageName:{{.Run.ID}} --context https://github.com/myuser/myrepo.git --git-access-token $GIT_ACCESS_TOKEN
Push and run a Docker image:
# Push image to registry
## Pull 'hello-world' image from Microsoft's Registry
docker pull mcr.microsoft.com/hello-world
## Tag the image
docker tag mcr.microsoft.com/hello-world $registryName.azurecr.io/$repository/$imageName:$tag
## Push image
docker push $registryName.azurecr.io/$repository/$imageName:$tag
# Run image from registry
docker run $registryName.azurecr.io/$repository/$imageName:$tag
# Alt: az acr run --registry $registryName --cmd '$registryName/$repository/$imageName:$tag' /dev/null
List container images and tags:
az acr repository list --name $registryName --output table
az acr repository show-tags --name $registryName --repository $repository --output table
Enables the deployment of Docker containers (up to 15 GB) without provisioning virtual machines.
Does not support scaling! Use Container Apps for that!
NB: If a container group restarts, its IP might change. Avoid using hardcoded IP addresses. For a stable public IP, consider using Application Gateway.
# Login to manage resources
az login
# Create a resource group
az group create --name $resourceGroup --location eastus
# (Optional)
# Deployment
##
## NOTE: If using managed identities with ACR, you'll also need --asign-identity param
## or az container identity assign --identities $identityName --resource-group $resourceGroup --name $containerName
##
## From image - simple scenarios
###
### Azure File share: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-volume-azure-files
### Can only be mounted to Linux containers running as root!
### --os-type Linux
### --azure-file-volume-account-name # Azure File Share requires existing storage account and account key
### --azure-file-volume-account-key
### --azure-file-volume-mount-path
### --azure-file-volume-share-name
### NOTE: No direct integration Blob Storage because it lacks SMB support
###
### Public DNS name (must be unique) - accessible from $dnsLabel.<region>.azurecontainer.io
### --dns-name-label $dnsLabel
### --ip-address public
###
### [--restart-policy {Always, Never, OnFailure}] # Default: Always. Never if you only want to run once. Status when stopped: Terminated
###
### Environment variables: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-environment-variables
#### NOTE: Format can be 'key'='value', key=value, 'key=value'
### --environment-variables # ex: 'PUBLIC_ENV_VAR'='my-exposed-value'
### --secure-environment-variables # ex: 'SECRET_ENV_VAR'='my-secret-value' - not visible in your container's properties
###
### Mount secret volumes: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-volume-secret
### --secrets mysecret1="My first secret FOO" mysecret2="My second secret BAR"
### --secrets-mount-path /mnt/secrets
### NB: Restricted to Linux containers
### NOTE: This creates mysecret1 and mysecret2 files in /mnt/secrets with value the content of the secret
###
az container create --name $containerName --image $imageName:$tag --resource-group $resourceGroup
##
## From YAML file - deployment includes only container instances
### Same options as from simple deployment, but in a YAML file. Includes container groups.
az container create --name $containerName --file deploy.yml --resource-group $resourceGroup
##
## ARM template - deploy additional Azure service resources (for example, an Azure Files share)
### No example, but it's good to know this fact
# Verify container is running
az container show --name $containerName --resource-group $resourceGroup --query "{FQDN:ipAddress.fqdn,ProvisioningState:provisioningState}" --out table
YAML deployment (deploy.yml
) (see CLI example above for reference):
apiVersion: "2019-12-01"
location: eastus
name: containerName
properties:
# Container groups: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-container-groups
# Containers use a single host machine, sharing lifecycle, resources, network (share an external IP, ports. DNS), and storage volumes
# For Windows containers, only single-instance deployment are allowed (NOTE: Here we use two!)
# The resources allocated for the host are sum of all resources requested (In this case: 2 CPUs and 2.5 GB RAM)
containers:
- name: helloworld
properties:
environmentVariables:
- name: "PUBLIC_ENV_VAR"
value: "my-exposed-value"
- name: "SECRET_ENV_VAR"
secureValue: "my-secret-value"
image: mcr.microsoft.com/hello-world
ports:
- port: 443
resources:
requests:
cpu: 1.0
memoryInGB: 1
volumeMounts:
- mountPath: /mnt/secrets
name: secretvolume
- name: hellofiles
properties:
environmentVariables: []
image: mcr.microsoft.com/azuredocs/aci-hellofiles
ports:
- port: 80
resources:
requests:
cpu: 1.0
memoryInGB: 1.5
volumeMounts:
- mountPath: /aci/logs/
name: filesharevolume
osType: Linux # or Windows (for single containers)
restartPolicy: Always
ipAddress:
type: Public
ports:
- port: 443
- port: 80
dnsNameLabel: containerName
volumes:
- name: filesharevolume
azureFile:
sharename: acishare
storageAccountName: <Storage account name>
storageAccountKey: <Storage account key>
- name: secretvolume
secret:
# NB: The secret values must be Base64-encoded!
mysecret1: TXkgZmlyc3Qgc2VjcmV0IEZPTwo= # "My first secret FOO"
mysecret2: TXkgc2Vjb25kIHNlY3JldCBCQVIK # "My second secret BAR"
tags: {}
type: Microsoft.ContainerInstance/containerGroups
Azure Container Instances does not support direct integration Blob Storage because it lacks SMB support.
To use Azure File Share, you need to:
- Create the storage account
- Create a file share
- From storage account, you need Storage account name, Share name, and Storage account key.
az container attach
Connects your local console to a container's output and error streams in real time (example: to debug startup issue).
az container logs
Displays logs (when no real time monitoring is needed)
Fully managed (no need to manage other Azure infrastructure) environment. Common use cases:
- Deploying API endpoints
- Hosting background processing applications
- Handling event-driven processing
- Running microservices
Limitations:
- Cannot run privileged containers (no root).
- linux/amd64 container images are required.
- State doesn't persist inside a container due to regular restarts. Use external caches for in-memory cache requirements.
A webhook can be used to notify Azure Container Apps when a new image has been pushed to the ACR, triggering automatic deployment of the updated container.
The platform's authentication and authorization middleware component runs as a sidecar container on each application replica, screening all incoming HTTPS (ensure allowInsecure
is disabled in ingress config) requests before they reach your application. See more. It requires any identity provider and specified provider within app settings.
Main topic: Managed Identities
Not supported in scaling rules.
Scaling is driven by three different categories of triggers:
- HTTP: Based on the number of concurrent HTTP requests to your revision.
- TCP: Based on the number of concurrent TCP connections to your revision.
- Custom: Based on CPU, memory (cannot scale to 0), or supported event-driven data sources such as:
- Azure Service Bus
- Azure Event Hubs
- Apache Kafka
- Redis
As a container app revision scales out, new instances of the revision are created on-demand. These instances are known as replicas (default: 0-10). Adding or editing scaling rules creates a new revision of the container app. In "multiple revisions" mode, adding a new scale trigger creates a new revision of your application but your old revision remains available with the old scale rules.
Example:
az containerapp create \
# ...
--min-replicas 0 \
--max-replicas 5 \
# HTTP Scaling Rule
--scale-rule-name http-rule-name \
--scale-rule-type http \
--scale-rule-http-concurrency 100
# TCP Scaling Rule
--scale-rule-name tcp-rule-name \
--scale-rule-type tcp \
--scale-rule-tcp-concurrency 100
# Custom Scaling rule
## Note we use --secrets to define the connection string and reuse it by secret name in --scale-rule-auth
--secrets "connection-string-secret=<SERVICE_BUS_CONNECTION_STRING>" \
--scale-rule-auth "connection=connection-string-secret"
--scale-rule-name servicebus-rule-name \
--scale-rule-type azure-servicebus \
--scale-rule-metadata "queueName=my-queue" "namespace=service-bus-namespace" "messageCount=5"
Without a scale rule, the default (HTTP, 0-10 replicas) applies to your app. Create a rule or set minReplicas to 1+ if ingress is disabled. Without minReplicas or a custom rule, your app can scale to zero and won't start.
Immutable snapshots of a container app version. The first revision is auto-created upon deployment, new are created on revision scope changes. Up to 100 revisions can be stored for history. Multiple revisions can run at once, with HTTP traffic split among them.
- Single revision mode: keeps the old revision active until the new one is ready.
- Multiple revision mode: you control revision lifecycle and traffic distribution (via ingress), with traffic only switching to the latest revision when it's ready.
Revision Labels: direct traffic to specific revisions. A label provides a unique URL that you can use to route traffic to the revision that the label is assigned.
Scopes:
- Revision-scope changes via
az containerapp update
trigger a new revision when you deploy your app. Trigger: changing properties.template. Example: version suffix, container config, scaling rules. The changes don't affect other revisions. - Application-scope changes are globally applied to all revisions. A new revision isn't created Trigger: changing properties.configuration. Example: secrets, revision mode, ingress, credentials, DARP settings.
Secrets are scoped to an application (az containerapp create
), outside of any specific revision of an application. Once secrets are defined at the application level, secured values are available to container apps. Adding, removing, or changing secrets doesn't generate new revisions. Apps need to be restarted to reflect updates.
Defining secrets: --secrets "queue-connection-string=<CONNECTION_STRING>"
Secrets from Key Vault: --secrets "kv-connection-string=keyvaultref:<KEY_VAULT_SECRET_URI>,identityref:<USER_ASSIGNED_IDENTITY_ID>"
Mounting Secrets in a Volume (secret name is filename, secret value is content): --secret-volume-mount "/mnt/secrets"
Referencing Secrets in Environment Variables (secretref:
): --env-vars "QueueName=myqueue" "ConnectionString=secretref:queue-connection-string"
- System Logs (at the container app level)
- Console Logs (from the
stderr
andstdout
messages inside container app)
# ContainerAppConsoleLogs_CL or ContainerAppSystemLogs_CL
az monitor log-analytics query --workspace $WORKSPACE_CUSTOMER_ID --analytics-query "ContainerAppConsoleLogs_CL | where ContainerAppName_s == 'album-api' | project Time=TimeGenerated, AppName=ContainerAppName_s, Revision=RevisionName_s, Container=ContainerName_s, Message=Log_s, LogLevel_s | take 5" --out table
az containerapp create
- Creates a new container app in Azure with specific configurations (CPU, memory, environment variables, etc).az containerapp up
- Quicker and more automated deployment process, ideal for development or testing.
If you anticipate needing more control or specific configurations in the future, az containerapp create
might be the more suitable choice. If simplicity and speed are the primary concerns, az containerapp up
might be preferred.
# Upgrade Azure CLI version on the workstation
az upgrade
# Add and upgrade the containerapp extension for managing containerized services
az extension add --name containerapp --upgrade
# Login to Azure
az login
# Register providers for Azure App Services (for hosting APIs) and Azure Operational Insights (for telemetry)
az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsights
# Create an environment 'prod' in Azure Container Apps
az containerapp env create --resource-group $resourceGroup --name prod
# Deploy the API service to the 'prod' environment, using the source code from a repository
# https://learn.microsoft.com/en-us/azure/container-apps/quickstart-code-to-cloud
function deploy_repo() {
az containerapp up \
--name MyAPI \
--resource-group $resourceGroup \
--location eastus \
--environment prod \
--context-path ./src \
--repo myuser/myrepo \
--ingress 'external'
# Display the Fully Qualified Domain Name (FQDN) of the app after it's deployed. This is the URL you would use to access your application.
az containerapp show --name MyAPI --resource-group $resourceGroup --query properties.configuration.ingress.fqdn
}
# Deploy a containerized application in Azure Container Apps, using an existing public Docker image
# https://learn.microsoft.com/en-us/azure/container-apps/get-started
function deploy_image() {
az containerapp up \
--name MyContainerApp \
--resource-group $resourceGroup \
--environment prod \
--image mcr.microsoft.com/azuredocs/containerapps-helloworld:latest \
--target-port 80 \
--ingress 'external' \ # allows the application to be accessible from the internet.
# Display the Fully Qualified Domain Name (FQDN) of the app after it's deployed. This is the URL you would use to access your application.
--query properties.configuration.ingress.fqdn
# Alt: Deploy from a Docker Image in Azure Container Registry (ACR)
# --image myAcr.azurecr.io/myimage:latest \
# --registry-username myAcrUsername \
# --registry-password myAcrPassword \
}
In the event of a full region outage, you have two strategies:
- Manual recovery:
- Manually deploy to a new region
- Wait for the region to recover, and then manually redeploy all environments and apps.
- Resilient recovery: Deploy your container apps in advance to multiple regions. Use a traffic management service (ex: Azure Front Door) to direct requests to your main region. In case of an outage, reroute traffic from the affected region.
Dapr is activated per container app. Its APIs are accessible via a Dapr sidecar using HTTP or gRPC. Dapr's modular design allows shared or app-specific components, which can connect to external services and securely access configuration metadata. By default Dapr-enabled container apps load the full set of deployed components. To load components only for the right apps, application scopes are used.
Enable Dapr: az containerapp create --dapr-enabled ...
Main APIs provided by Dapr:
- Service-to-service invocation: Enables secure, direct service calls.
- State management: Manages transactions and CRUD operations.
- Pub/sub: Facilitates communication between container apps via message broker. For event-driven architecture.
- Bindings: Communicate with external systems.
- Actors: Supports scalable, message-driven units of work.
- Observability: Sends tracing information to an Application Insights backend.
- Secrets: Accesses secrets or references secure values in Dapr components.
dotnet/core/sdk
- build an ASP.NET appdotnet/core/aspnet
- run an ASP.NET app
# Sets the working directory to `/app`, which is where app files will be copied.
WORKDIR /app
# Copies the contents of the published app to the container's working directory (`/app` in this case).
COPY bin/Release/net6.0/publish/ .
- Compile Stage:
- Choose a base image suitable for compiling the code.
- Set the working directory.
- Copy the source code.
- Compile the code.
- Runtime Stage:
- Choose a base image suitable for running the application.
- Copy compiled binaries or artifacts from the compile stage.
- Set the command to run the application.
FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS build
WORKDIR /app
# copy csproj and restore as distinct layers
COPY *.sln .
COPY aspnetapp/*.csproj ./aspnetapp/
RUN dotnet restore
# copy everything else and build app
COPY aspnetapp/. ./aspnetapp/
WORKDIR /app/aspnetapp
RUN dotnet publish -c Release -o out
FROM mcr.microsoft.com/dotnet/core/aspnet:3.0 AS runtime
WORKDIR /app
COPY --from=build /app/aspnetapp/out ./
ENTRYPOINT ["dotnet", "aspnetapp.dll"]
Serving both secure and non-secure web traffic:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
# copy csproj and restore as distinct layers
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"]
RUN dotnet restore "WebApplication1/WebApplication1.csproj"
# copy everything else and build app
COPY . .
WORKDIR "/src/WebApplication1"
RUN dotnet build "WebApplication1.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "WebApplication1.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApplication1.dll"]
Command | Brief Explanation | Example |
---|---|---|
az acr login | Authenticate with an ACR. | az acr login --name MyRegistry |
az acr create | Create a new ACR. | az acr create --resource-group $resourceGroup --name MyRegistry --sku Basic |
az acr update | Update properties of an ACR. | az acr update --name MyRegistry --tags key=value |
az acr build | Build a container image in ACR. | az acr build --image MyImage:tag --registry MyRegistry . |
az acr task create | Create a task for an ACR. | az acr task create --registry MyRegistry --name MyTask --image MyImage:tag --context /path/to/source |
az acr repository | Manage repositories (image storage) in ACR. | az acr repository show-tags --name MyRegistry --repository MyImage |
az acr repository list |
List repositories / Verify an image has been pushed to ACR | az acr repository list --name MyRegistry |
az acr run | Queue a run to stream logs for an ACR. | az acr run --registry MyRegistry --cmd '$Registry/myimage' /dev/null |
az acr show | Get the details of an ACR. | az acr show --name MyRegistry --query "loginServer" |
az container create | Create a container group in ACI (deploy an image). | az container create --name MyContainer --image myimage:latest |
az container attach | Attach local standard output and error streams to a container in a container group. | az container attach --name MyContainer --resource-group $resourceGroup |
az container show | Get the details of a container group. | az container show --name MyContainer --resource-group $resourceGroup |
az container logs | Fetch the logs for a container in a container group. | az container logs --name MyContainer --resource-group $resourceGroup |
az containerapp create | Create a Container App. | az containerapp create --name MyContainerApp --resource-group $resourceGroup --image myimage:latest |
az containerapp up | Create or update a Container App and associated resources. | az containerapp up --name MyContainerApp |
az containerapp env create | Create an environment for a Container App. | az containerapp env create --name MyEnvironment --resource-group $resourceGroup |
az containerapp show | Show details of a Container App. | az containerapp show --name MyContainerApp --resource-group $resourceGroup |
az containerapp identity assign | Assign managed identities to a Container App. | az containerapp identity assign --name MyContainerApp --identities [system] |
az upgrade | Upgrade Azure CLI and extensions. | az upgrade |
az identity create | Create a managed identity. | az identity create --name MyManagedIdentity --resource-group $resourceGroup |
az role assignment create | Create a new role assignment for a user, group, or service principal. | az role assignment create --assignee [email protected] --role Reader |
az ad sp | Manage Microsoft Entra ID service principals. | az ad sp create-for-rbac --name MyServicePrincipal |
az monitor log-analytics query | Query a Log Analytics workspace. | az monitor log-analytics query --workspace MyWorkspace --query 'MyQuery' |
az acr import | Import an image to an ACR from another registry. | az acr import --name MyRegistry --source myregistry.azurecr.io/myimage:tag |
az containerapp revision list | List the revisions of a Container App. | az containerapp revision list --name MyContainerApp --resource-group $resourceGroup |