|
| 1 | +--- |
| 2 | +title: "Deploy arc-enabled workloads in an Extended Zone: ContainerApps" |
| 3 | +description: Learn how to deploy arc-enabled ContainerApps in an Extended Zone. |
| 4 | +author: svaldes |
| 5 | +ms.author: svaldes |
| 6 | +ms.service: azure-extended-zones |
| 7 | +ms.topic: quickstart-arm |
| 8 | +ms.date: 30/04/2025 |
| 9 | +ms.custom: subject-armqs, devx-track-azurecli |
| 10 | + |
| 11 | +# Customer intent: As a cloud administrator and Azure Extended Zones user, I want a quick method to deploy PaaS services via Arc in an Azure Extended Zone. |
| 12 | +--- |
| 13 | + |
| 14 | +# Deploy arc-enabled workloads in an Extended Zone: ContainerApps |
| 15 | + |
| 16 | +In this article, you will learn how to deploy arc-enabled workloads in an Extended Zone. The current supported workloads are ContainerApps, ManagedSQL, and PostgreSQL. |
| 17 | +Feel free to explore Container Apps on Azure Arc Overview | Microsoft Learn to become more familiar with Container Apps on Azure Arc. |
| 18 | + |
| 19 | +## Prerequisites |
| 20 | + |
| 21 | +- [An Azure account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) with an active subscription. |
| 22 | +- Access to an Extended Zone. For more information, see [Request access to an Azure Extended Zone](request-access.md). |
| 23 | +- Azure Cloud Shell or Azure CLI. Install the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli). |
| 24 | +- Access to a public or private container registry, such as the [Azure Container Registry](https://learn.microsoft.com/en-us/azure/container-registry/). |
| 25 | +- Review the [requirements and limitations](https://learn.microsoft.com/en-us/azure/container-apps/azure-arc-overview0) of the public preview. Of particular importance are the cluster requirements. |
| 26 | +- Before you proceed to create a container app, you first need to set up an [Azure Arc-enabled Kubernetes cluster to run Azure Container Apps](https://learn.microsoft.com/en-us/azure/container-apps/azure-arc-enable-cluster). You will need to follow the steps in *Setup*, *Create a Connected Cluster*, and (optionally) *Create a Log Analytics Workspace* before addressing the following steps in this article. |
| 27 | +> [!NOTE] |
| 28 | +> Use the intended Extended Location as your location variable. |
| 29 | +
|
| 30 | +## Getting Started |
| 31 | +If you are already familiar with the topics below, you may skip this paragraph. There are important topics you may want read before you proceed with creation: |
| 32 | +• [Overview of Azure Arc-enabled data services](https://learn.microsoft.com/en-us/azure/azure-arc/data/overview) |
| 33 | +• [Connectivity modes and requirements](https://learn.microsoft.com/en-us/azure/azure-arc/data/connectivity) |
| 34 | +• [Storage configuration and Kubernetes storage concepts](https://learn.microsoft.com/en-us/azure/azure-arc/data/storage-configuration) |
| 35 | +• [Kubernetes resource model](https://github.com/kubernetes/design-proposals-archive/blob/main/scheduling/resources.md#resource-quantities) |
| 36 | + |
| 37 | +### Create Container Apps on Arc-enabled AKS in Extended Zones |
| 38 | + |
| 39 | +Now that the Arc-enabled AKS has been created, we can proceed to using the following PowerShell script to create our Container App on an AKS cluster in an Extended Zone and connect it to the Azure Arc-enabled Kubernetes. |
| 40 | + |
| 41 | +> [!NOTE] |
| 42 | +> Please make sure to transfer the parameters from the Arc-enabled AKS steps correctly into the script. |
| 43 | + |
| 44 | +```powershell |
| 45 | +# . "./CreateArcEnabledAksOnEZ.ps1" |
| 46 | +
|
| 47 | +# Create a container app on an AKS cluster in an edge zone and connect it to Azure Arc-enabled Kubernetes |
| 48 | +function CreateContainerAppOnArcEnabledAksEz { |
| 49 | + param( |
| 50 | + [string] $AKSClusterResourceGroupName, |
| 51 | + [string] $location = "westus", |
| 52 | + [string] $AKSName, |
| 53 | + [string] $edgeZone, |
| 54 | + [int] $nodeCount = 2, |
| 55 | + [string] $vmSize = "standard_nv12ads_a10_v5", |
| 56 | + [string] $ARCResourceGroupName, |
| 57 | + [string] $CONNECTED_ENVIRONMENT_NAME, |
| 58 | + [string] $CUSTOM_LOCATION_NAME, |
| 59 | + [string] $SubscriptionId, |
| 60 | + [string] $ACRName, |
| 61 | + [string] $imageName, |
| 62 | + [switch] $Debug |
| 63 | + ) |
| 64 | +
|
| 65 | + try { |
| 66 | + # Set the subscription |
| 67 | + az account set --subscription $SubscriptionId |
| 68 | +
|
| 69 | + # Create the ARC-enabled EZ AKS cluster |
| 70 | + createArcEnabledAksOnEz -SubscriptionId $SubscriptionId -AKSClusterResourceGroupName $AKSClusterResourceGroupName -location $location -AKSName $AKSName -edgeZone $edgeZone -nodeCount $nodeCount -vmSize $vmSize -ARCResourceGroupName $ARCResourceGroupName -Debug:$Debug |
| 71 | +
|
| 72 | + # Install container apps extension |
| 73 | + $CLUSTER_NAME = "$ARCResourceGroupName-cluster" # Name of the connected cluster resource |
| 74 | + $EXTENSION_NAME="appenv-ext" |
| 75 | + $NAMESPACE="app-ns" |
| 76 | + az k8s-extension create ` |
| 77 | + --resource-group $ARCResourceGroupName ` |
| 78 | + --name $EXTENSION_NAME ` |
| 79 | + --cluster-type connectedClusters ` |
| 80 | + --cluster-name $CLUSTER_NAME ` |
| 81 | + --extension-type 'Microsoft.App.Environment' ` |
| 82 | + --release-train stable ` |
| 83 | + --auto-upgrade-minor-version true ` |
| 84 | + --scope cluster ` |
| 85 | + --release-namespace $NAMESPACE ` |
| 86 | + --configuration-settings "Microsoft.CustomLocation.ServiceAccount=default" ` |
| 87 | + --configuration-settings "appsNamespace=${NAMESPACE}" ` |
| 88 | + --configuration-settings "clusterName=${CONNECTED_ENVIRONMENT_NAME}" ` |
| 89 | + --configuration-settings "envoy.annotations.service.beta.kubernetes.io/azure-load-balancer-resource-group=${AKSClusterResourceGroupName}" |
| 90 | + |
| 91 | + # Save id property of the Container Apps extension for later |
| 92 | + $EXTENSION_ID=$(az k8s-extension show ` |
| 93 | + --cluster-type connectedClusters ` |
| 94 | + --cluster-name $CLUSTER_NAME ` |
| 95 | + --resource-group $ARCResourceGroupName ` |
| 96 | + --name $EXTENSION_NAME ` |
| 97 | + --query id ` |
| 98 | + --output tsv) |
| 99 | +
|
| 100 | + # Wait for extension to fully install before proceeding |
| 101 | + az resource wait --ids $EXTENSION_ID --custom "properties.provisioningState!='Pending'" --api-version "2020-07-01-preview" |
| 102 | + |
| 103 | + $CONNECTED_CLUSTER_ID=$(az connectedk8s show --resource-group $ARCResourceGroupName --name $CLUSTER_NAME --query id --output tsv) |
| 104 | + az customlocation create ` |
| 105 | + --resource-group $ARCResourceGroupName ` |
| 106 | + --name $CUSTOM_LOCATION_NAME ` |
| 107 | + --host-resource-id $CONNECTED_CLUSTER_ID ` |
| 108 | + --namespace $NAMESPACE ` |
| 109 | + --cluster-extension-ids $EXTENSION_ID |
| 110 | +
|
| 111 | + # DEBUG: Test custom location creation |
| 112 | + if ($Debug) { |
| 113 | + Write-Debug az customlocation show --resource-group $ARCResourceGroupName --name $CUSTOM_LOCATION_NAME |
| 114 | + } |
| 115 | +
|
| 116 | + # Save id property of the custom location for later |
| 117 | + $CUSTOM_LOCATION_ID=$(az customlocation show ` |
| 118 | + --resource-group $ARCResourceGroupName ` |
| 119 | + --name $CUSTOM_LOCATION_NAME ` |
| 120 | + --query id ` |
| 121 | + --output tsv) |
| 122 | +
|
| 123 | + # Create container Apps connected environment |
| 124 | + az containerapp connected-env create ` |
| 125 | + --resource-group $ARCResourceGroupName ` |
| 126 | + --name $CONNECTED_ENVIRONMENT_NAME ` |
| 127 | + --custom-location $CUSTOM_LOCATION_ID ` |
| 128 | + --location eastus |
| 129 | +
|
| 130 | + # DEBUG: validate that the connected environment is successfully created |
| 131 | + if ($Debug) { |
| 132 | + Write-Debug az containerapp connected-env show --resource-group $ARCResourceGroupName --name $CONNECTED_ENVIRONMENT_NAME |
| 133 | + } |
| 134 | +
|
| 135 | + # Create a new resource group for the container app |
| 136 | + $myResourceGroup="${imageName}-resource-group" |
| 137 | + az group create --name $myResourceGroup --location eastus |
| 138 | +
|
| 139 | + # Get the custom location id |
| 140 | + $customLocationId=$(az customlocation show --resource-group $ARCResourceGroupName --name $CUSTOM_LOCATION_NAME --query id --output tsv) |
| 141 | + |
| 142 | + # Get info about the connected environment |
| 143 | + $myContainerApp="${imageName}-container-app" |
| 144 | + $myConnectedEnvironment=$(az containerapp connected-env list --custom-location $customLocationId -o tsv --query '[].id') |
| 145 | +
|
| 146 | + # create acr and group |
| 147 | + az group create --name $ARCResourceGroupName --location eastus |
| 148 | + az acr create --resource-group $ARCResourceGroupName --name $ACRName --sku Basic |
| 149 | +
|
| 150 | + # Wait for the ACR to be created |
| 151 | + Start-Sleep -Seconds 10 |
| 152 | +
|
| 153 | + # login to acr and get login server |
| 154 | + az acr login --name $ACRName |
| 155 | + $ACRLoginServer = $(az acr show --name $ACRName --query loginServer --output tsv) |
| 156 | +
|
| 157 | + # DEBUG: Test ACR login |
| 158 | + if ($Debug) { |
| 159 | + Write-Debug az acr show --name $ACRName |
| 160 | + Write-Debug az acr repository list --name $ACRName --output table |
| 161 | + } |
| 162 | +
|
| 163 | + # Build and push docker image |
| 164 | + cd .\DemoApp |
| 165 | + docker build -t ${imageName} . |
| 166 | + docker tag ${imageName} ${ACRLoginServer}/${imageName}:latest |
| 167 | + docker push ${ACRLoginServer}/${imageName}:latest |
| 168 | + cd .. |
| 169 | +
|
| 170 | + # Enable admin user in ACR and get password |
| 171 | + az acr update -n $ACRName --admin-enabled true |
| 172 | + $password = $(az acr credential show --name ${ACRName} --query passwords[0].value --output tsv) |
| 173 | +
|
| 174 | + # Create container app |
| 175 | + az containerapp create ` |
| 176 | + --resource-group $myResourceGroup ` |
| 177 | + --name $myContainerApp ` |
| 178 | + --environment $myConnectedEnvironment ` |
| 179 | + --environment-type connected ` |
| 180 | + --registry-server ${ACRLoginServer} ` |
| 181 | + --registry-username ${ACRName} ` |
| 182 | + --registry-password $password ` |
| 183 | + --image "${ACRLoginServer}/${imageName}:latest" ` |
| 184 | + --target-port 80 ` |
| 185 | + --ingress 'external' ` |
| 186 | + |
| 187 | + # Open the container app in a browser |
| 188 | + az containerapp browse --resource-group $myResourceGroup --name $myContainerApp |
| 189 | + } |
| 190 | + catch { |
| 191 | + # Catch any error |
| 192 | + Write-Error "An error occurred" |
| 193 | + Write-Error $Error[0] |
| 194 | + } |
| 195 | +} |
| 196 | +
|
| 197 | +CreateContainerAppOnArcEnabledAksEz -AKSClusterResourceGroupName "my-aks-cluster-group" ` |
| 198 | + -location "westus" ` |
| 199 | + -AKSName "my-aks-cluster"` |
| 200 | + -edgeZone "losangeles" ` |
| 201 | + -nodeCount 2 ` |
| 202 | + -vmSize "standard_nv12ads_a10_v5" ` |
| 203 | + -ARCResourceGroupName "myARCResourceGroup" ` |
| 204 | + -CONNECTED_ENVIRONMENT_NAME "myConnectedEnvironment" ` |
| 205 | + -CUSTOM_LOCATION_NAME "myCustomLocation" ` |
| 206 | + -SubscriptionId "<your subscription>"` |
| 207 | + -ACRName "containerappezacr" ` |
| 208 | + -imageName "myimage" |
| 209 | +``` |
| 210 | + |
| 211 | + |
| 212 | +## Clean up resources |
| 213 | + |
| 214 | +When no longer needed, delete **my-aks-cluster-group** resource group and all of the resources it contains using the [az group delete](/cli/azure/group#az-group-delete) command. |
| 215 | + |
| 216 | +```azurecli-interactive |
| 217 | +az group delete --name my-aks-cluster-group |
| 218 | +``` |
| 219 | + |
| 220 | +## Related content |
| 221 | + |
| 222 | +- [Deploy an AKS cluster in an Extended Zone](deploy-aks-cluster.md) |
| 223 | +- [Deploy a storage account in an Extended Zone](create-storage-account.md) |
| 224 | +- [Frequently asked questions](faq.md) |
0 commit comments