Si estás desarrollando aplicaciones basadas en Azure Web Apps, Functions o Logic Apps probablemente te gustaría usarlos en otros entornos donde Microsoft Azure les queda lejos. Gracias a Azure Arc ahora es posible plantearse este tipo de escenarios, donde puedes usar estos servicios en otras clouds, en local o en el famoso edge. En este artículo te muestro cómo hacerlo.
Nota: esta funcionalidad todavía está en Preview, por lo que no está preparada, ni soportada, a día de hoy para entornos productivos.
Instalar extensiones de Azure CLI
Antes de nada, ya que en este artículo voy a hacer todo a través de Azure CLI, es importante que instales o actualices varias extensiones:
# Enable all extensions
az extension add --upgrade --yes --name connectedk8s
az extension add --upgrade --yes --name k8s-extension
az extension add --upgrade --yes --name customlocation
az extension remove --name appservice-kube
az extension add --upgrade --yes --name appservice-kube
Estas son necesarias tanto para interactuar con Azure Arc y Kubernetes como más específicamente con los servicios que dependen de App Service.
Registrar proveedores
Del mismo modo, para que nuestra suscripción tenga habilitadas las opciones necesaria para acondicionar este tipo de entornos necesitamos asegurarnos de que tenemos registrados estos proveedores:
az provider register --namespace Microsoft.ExtendedLocation --wait
az provider register --namespace Microsoft.Web --wait
az provider register --namespace Microsoft.KubernetesConfiguration --wait
Ahora que ya tenemos Azure CLI y la suscripción lista para generar los recursos necesarios vamos a crearlos.
Crear un clúster de Kubernetes
Para este ejemplo, vamos a crear un clúster de Kubernetes en AKS, de la forma habitual:
# Variables
RESOURCE_GROUP="k8s-for-app-svc"
LOCATION="westeurope"
# Create a resource group
az group create --name $RESOURCE_GROUP --location $LOCATION
# Create an AKS cluster
AKS_NAME="aks-appsvc"
az aks create --resource-group $RESOURCE_GROUP \
--name $AKS_NAME \
--generate-ssh-keys \
--location $LOCATION
# Get credentials
az aks get-credentials --resource-group $RESOURCE_GROUP --name $AKS_NAME
Es importante que sepas que para poder alojar todos los objetos que se van a crear después necesitarás más de un nodo (te lo digo porque yo a veces me creo clústeres de solo uno, para pruebas, y en este ejemplo al empezar así se me quedaban algunos pods pending). Por otro lado en esta fase de preview solo puedes usar West Europe o East US.
Una vez que la creación finalice, vamos a crear otro grupo de recursos donde estará el recurso de Azure Arc para Kubernetes, al que conectaremos el clúster recién creado:
# Arc resource group
RESOURCE_GROUP_ARC="arc-resources"
az group create --name $RESOURCE_GROUP_ARC --location $LOCATION
# Connect the cluster to Azure Arc
az connectedk8s connect \
--resource-group $RESOURCE_GROUP_ARC \
--name $AKS_NAME \
--location $LOCATION
Comprueba que el mismo está correctamente provisionado dentro de Azure Arc con este comando, antes de continuar:
# Validate connection
az connectedk8s show --resource-group $RESOURCE_GROUP_ARC --name $AKS_NAME --query "provisioningState"
Instalar la extensión de App Service en el clúster de Kubernetes
Para poder alojar servicios basados en App Service, como son Azure Web App, Azure Functions y Logic Apps, necesitamos instalar una extensión dentro del clúster para que el mismo sepa cómo manejarlos.
# Install App Service extension
EXTENSION_NAME="appservice-ext"
NAMESPACE="app-service-resources"
az k8s-extension create \
--resource-group $RESOURCE_GROUP_ARC \
--name $EXTENSION_NAME \
--cluster-type connectedClusters \
--cluster-name $AKS_NAME \
--extension-type 'Microsoft.Web.Appservice' \
--release-train stable \
--auto-upgrade-minor-version true \
--scope cluster \
--release-namespace $NAMESPACE \
--configuration-settings "Microsoft.CustomLocation.ServiceAccount=default" \
--configuration-settings "appsNamespace=${NAMESPACE}" \
--configuration-settings "clusterName=${AKS_NAME}" \
--configuration-settings "keda.enabled=true" \
--configuration-settings "buildService.storageClassName=default" \
--configuration-settings "buildService.storageAccessMode=ReadWriteOnce" \
--configuration-settings "customConfigMap=${NAMESPACE}/kube-environment-config" \
--configuration-settings "envoy.annotations.service.beta.kubernetes.io/azure-load-balancer-resource-group=${RESOURCE_GROUP}"
Como ves, necesitas darle un nombre a la instalación de la extensión en el clúster, un namespace de Kubernetes donde se almacenarán los «recursos» que creamos dentro del marco de App Service, además de otros propios de la extensión, y varias configuration settings donde indicamos en este caso valores propios del clúster, como por ejemplo qué cuenta de servicio vamos a utilizar, la storage class, si vamos a querer usar KEDA para auto escalar, etcétera.
Cuando el proceso termine, recuperamos el ID de la extensión y esperamos a que el proceso de instalación de la misma termine por completo:
# Store the extension Id
EXTENSION_ID=$(az k8s-extension show \
--cluster-type connectedClusters \
--cluster-name $AKS_NAME \
--resource-group $RESOURCE_GROUP_ARC \
--name $EXTENSION_NAME \
--query id \
--output tsv)
# Wait for the extension to be ready
az resource wait --ids $EXTENSION_ID --custom "properties.installState!='Pending'" --api-version "2020-07-01-preview"
Puedes comprobar que todo se está ejecutando correctamente echando un vistazo a los pods dentro del namespace elegido:
# Check pods created
kubectl get pods -n $NAMESPACE
El resultado será parecido al siguiente:
Crear una ubicación personalizada
Si bien cuando has estado trabajando con App Service en el pasado has necesitado de un App Service Plan para alojar tus recursos, en el caso de Azure Arc necesitarás lo que se llama una custom location. Esto en realidad es un mapeo entre un namespace y un objeto que es manejable desde Azure. Para crearlo utilizamos el siguiente comando:
# Create a custom location
CUSTOM_LOCATION_NAME="custom-location"
CONNECTED_CLUSTER_ID=$(az connectedk8s show --resource-group $RESOURCE_GROUP_ARC --name $AKS_NAME --query id -o tsv)
az customlocation create \
--resource-group $RESOURCE_GROUP_ARC \
--name $CUSTOM_LOCATION_NAME \
--host-resource-id $CONNECTED_CLUSTER_ID \
--namespace $NAMESPACE \
--cluster-extension-id $EXTENSION_ID \
--location $LOCATION
Note: en el caso de App Service, al ser una extensión a nivel de clúster es necesario que el namespace sea el mismo que donde está instalada esta.
Recupera el ID de tu custom location para después:
CUSTOM_LOCATION_ID=$(az customlocation show \
--resource-group $RESOURCE_GROUP_ARC \
--name $CUSTOM_LOCATION_NAME \
--query id \
--output tsv)
Crear un Kubernetes App Service Environment dentro de la custom location
Por cada custom location necesitas al menos un Kubernetes App Service Environment:
# Create a Kube Environment for this location
KUBE_ENV="kube-env"
az appservice kube create \
--resource-group $RESOURCE_GROUP_ARC \
--name $KUBE_ENV \
--custom-location $CUSTOM_LOCATION_ID \
--location $LOCATION
# Wait for the Kube Environment to be ready
az appservice kube show --resource-group $RESOURCE_GROUP_ARC --name $KUBE_ENV
Antes de continuar asegurate de que ha terminado de provisionarse correctamente:

Ahora ya tienes todo lo que necesitas para desplegar tus servicios dentro de este clúster de Kubernetes manejado por Azure Arc. Vamos a ver ahora un ejemplo de cada uno.
Desplegar una Web App en Azure Arc
Desplegar una Web App en nuestro nuevo entorno es tan sencillo como utilizar el comando al que venías estando acostumbrad@, con un ligero cambio:
# Create a web app
WEB_APP_NAME="arc-webapp"
az webapp create \
--resource-group $RESOURCE_GROUP \
--name $WEB_APP_NAME \
--custom-location $CUSTOM_LOCATION_ID \
--runtime "NODE|14-lts"
Como ves, la única diferencia es el parámetro –custom-location que es el que dice dónde se va a hospedar este App Service, en lugar de usar un App Service Plan.
Si quieres ver una aplicación ejecutándose en este entorno, puedes desplegar la aplicación de ejemplo de la documentación oficial:
# Deploy an app
git clone https://github.com/Azure-Samples/nodejs-docs-hello-world
cd nodejs-docs-hello-world
zip -r package.zip .
az webapp deployment source config-zip --resource-group $RESOURCE_GROUP --name $WEB_APP_NAME --src package.zip
Para recuperar la URL del sitio puedes hacerlo a través de este comando:
# Get Azure App Service - Arc URL
az webapp show --resource-group $RESOURCE_GROUP --name $WEB_APP_NAME --query defaultHostName -o tsv
O igualmente la verás en el recurso de App Service que tendrás igualmente en el portal de Azure:

Crear una Azure Function para Arc
Este caso es similar al anterior solo que en este necesitamos apoyarnos en una Azure Storage Account para su funcionamiento. Este servicio a día de hoy no está contemplado dentro de Azure Arc, por lo que tienes que crearlo de la forma que lo harías habitualmente:
# Create a storage account
STORAGE_ACCOUNT_NAME="storearc"
az storage account create \
--resource-group $RESOURCE_GROUP \
--name $STORAGE_ACCOUNT_NAME \
--location $LOCATION \
--sku Standard_LRS
Una vez hecho, ya podemos crear la Azure Function que, al igual que en el caso anterior, en vez de estar asociada a un App Service Plan estará asociada a la custom location que creamos:
# Create Azure Function resource
FUNCTION_APP_NAME="arc-func"
az functionapp create --resource-group $RESOURCE_GROUP \
--name $FUNCTION_APP_NAME --custom-location $CUSTOM_LOCATION_ID \
--storage-account $STORAGE_ACCOUNT_NAME \
--functions-version 3 --runtime node --runtime-version 12
Al igual que con la web app, podriamos crearnos un proyecto de ejemplo:
mkdir func-app && cd func-app
func init
func new --name HttpTrigger --template "Http trigger" --authlevel "anonymous"
Probarlo en local sin problemas, para ver su funcionamiento:
func start
y desplegarlo dentro del clúster de Kubernetes con el siguiente comando:
# Publish the code
func azure functionapp publish $FUNCTION_APP_NAME
Al hacer esto nos devolverá la URL donde está ubicada esta función:

Igualmente podrás ver su recurso a través del portal:

Crear una Logic App para Arc
Para terminar, en el caso de las Logic Apps es necesario instalar otra extensión, todavía en preview, a través del siguiente comando:
az extension add --yes --source "https://aka.ms/logicapp-latest-py2.py3-none-any.whl"
Gracias a ella ya podremos usar el parámetro –custom-location para ubicarla dentro de nuestro clúster:
LOGIC_APP_NAME="arc-logic-app"
az logicapp create --name $LOGIC_APP_NAME \
--resource-group $RESOURCE_GROUP \
--storage-account $STORAGE_ACCOUNT_NAME \
--custom-location $CUSTOM_LOCATION_ID
En el caso de las Logic Apps también necesitamos de una cuenta de almacenamiento. Por simplificar el ejemplo, he reutilizado la que cree en el paso anterior para la Azure Function.
Al igual que en los casos anteriores, podrás ver tu Logic App desplegada en Kubernetes en el portal de Azure, y podrás interactuar con el diseñador:

Si por último revisas los pods dentro del namespace definido en Kubernetes para tu localización:
kubectl get pods -n $NAMESPACE
Verás que tienes tus tres despliegues:
¡Saludos!

Bootcamp Backend
Si tienes ganas de ponerte al día con tecnologías de backend, formo parte del
equipo de docentes del Bootcamp Backend de Lemoncode, ¿Te animas a
aprender con nosotros?