Ejecutar Azure Web Apps, Functions y Logic Apps donde quieras con Azure Arc

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:

pods desplegados por la extensión de App Service para Kubernetes

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:

App Service ejecutándose en k8s con Azure Arc

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:

Publicar una Azure Function usando Azure Arc

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

Azure Function ejecutándose en k8s con Azure Arc

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:

Azure Logic App ejecutándose en k8s con Azure Arc

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:

Azure Function, Logic App y Azure Web App desplegados en Kubernetes

¡Saludos!

logo lemoncode

 

 

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?

Más Info