Desplegar una aplicación con Dapr en AKS

En el último artículo compartí contigo cómo desplegar un ejemplo con Dapr, bastante completo, en Kubernetes, usando Kind. Hoy quiero mostrarte cómo desplegar el control plane de Dapr en AKS, gracias a la extensión que tenemos disponible para ello y desplegar el mismo ejemplo.

Prepara tu suscripción

Antes de nada, es necesario preparar tu suscripción para que la extensión de Dapr para AKS esté disponible. Para ello es necesario lanzar los siguientes comandos:

# In order to use the AKS Dapr extension, you must first enable the AKS-ExtensionManager and AKS-Dapr feature flags on your Azure subscription.
az feature register --namespace "Microsoft.ContainerService" --name "AKS-ExtensionManager"
az feature register --namespace "Microsoft.ContainerService" --name "AKS-Dapr"
# Confirm the registration status 
az feature list -o table --query "[?contains(name, 'Microsoft.ContainerService/AKS-ExtensionManager')].{Name:name,State:properties.state}"
az feature list -o table --query "[?contains(name, 'Microsoft.ContainerService/AKS-Dapr')].{Name:name,State:properties.state}"
# Refresh the registration of the Microsoft.KubernetesConfiguration and Microsoft.ContainerService
az provider register --namespace Microsoft.KubernetesConfiguration
az provider register --namespace Microsoft.ContainerService

El proceso puede tardar unos minutos, sobre todo el registro de AKS-Dapr.

Instalar k8s-extension

Para poder usar la extensión de Dapr a través de Azure CLI, necesitas instalar la extension llamada k8s-extension:

# Enable the Azure CLI extension for cluster extensions
az extension add --name k8s-extension
# or update it
az extension update --name k8s-extension

Con estos pasos previos ya puedes provisionar tu clúster e instalar Dapr en él.

Crear clúster de AKS

Ahora que ya tienes todo listo, el siguiente paso es crear tu clúster como lo harías siempre:

# Variables
RESOURCE_GROUP="aks-for-dapr"
LOCATION="northeurope"
AKS_NAME="dapr-demo"
# Create resource group
az group create -n $RESOURCE_GROUP -l $LOCATION
# Create cluster
az aks create --resource-group $RESOURCE_GROUP \
--name $AKS_NAME \
--node-count 2 \
--node-vm-size Standard_B4ms \
--generate-ssh-keys
# Get credentials
az aks get-credentials -n $AKS_NAME -g $RESOURCE_GROUP

Instalar la extensión de Dapr

Por último, utiliza k8s-extension para desplegar el control plane de Dapr:

# Create the extension and install Dapr on your AKS cluster
az k8s-extension create --cluster-type managedClusters \
--cluster-name $AKS_NAME \
--resource-group $RESOURCE_GROUP \
--name daprExtension \
--extension-type Microsoft.Dapr \
--auto-upgrade-minor-version true

Este comando te preguntará si Dapr está ya instalado en tu clúster (le decimos que no) y el proceso comenzará. Para confirmar que todo se ha instalado correctamente puedes ver los pods bajo dapr-system:

# Check Dapr control plane
kubectl get pods -n dapr-system

El resultado debería ser parecido al siguiente:

dapr-system en AKS

Desplegar la aplicación de ejemplo

Si quieres desplegar mi ejemplo de Tour of Heroes, que te muestra diferentes aspectos de Dapr, puedes utilizar los manifiestos del directorio dapr-k8s-manifests, pero antes debemos modificar los servicios de las API, cambiando el tipo NodePort a LoadBalancer:

backend/tour-of-heroes-api.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: tour-of-heroes-api
  name: tour-of-heroes-api
spec:
  # type: NodePort
  type: LoadBalancer
  ports:
    - name: web
      # port: 5222
      port: 80
      targetPort: 5222
      # nodePort: 30080
  selector:
    app: tour-of-heroes-api
---

backend/tour-of-villains-api.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: tour-of-villains-api
  name: tour-of-villains-api
spec:
  # type: NodePort
  type: LoadBalancer
  ports:
    - name: web
      port: 80
      targetPort: 5111
      # nodePort: 30090
  selector:
    app: tour-of-villains-api
---

Y para Zipkin lo dejaría como ClusterIP y accedería a él a través de port forwarding.

services/zipkin.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    name: zipkin
  name: zipkin
spec:
  # type: NodePort
  type: ClusterIP
  ports:
    - port: 9411
      targetPort: 9411
      # nodePort: 30070
  selector:
    app: zipkin
---

Una vez hechos los cambios puedes desplegar todos los manifiestos con este comando:

# Deploy demo
NAMESPACE="tour-of-heroes"
kubectl create ns $NAMESPACE
kubectl apply -f dapr-k8s-manifests --recursive -n $NAMESPACE

En este artículo he desplegado mi aplicación en un namespace diferente de default para que veas lo siguiente:

Si compruebas el estado de los pods:

# Check pods
watch kubectl get pods -n $NAMESPACE

Verás que tour-of-heroes-api no ha arrancado correctamente. Si revisas los logs te encontrarás con lo siguiente:

failed getting secret from secret store in non-default namespace

Esto es así porque la instalación de Dapr solo da permisos de lectura sobre el namespace default, tal y como se cuenta aquí. Para arreglarlo entonces debemos lanzar lo siguiente:

# If your Dapr enabled apps are using components that fetch secrets from non-default namespaces, apply the following resources to that namespace:
kubectl create -f - <<EOF
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-reader
  namespace: $NAMESPACE
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: dapr-secret-reader
  namespace: $NAMESPACE
subjects:
- kind: ServiceAccount
  name: default
roleRef:
  kind: Role
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io
EOF

Si quieres probar el ejemplo, como en el artículo anterior, puedes usar los siguientes comandos:

# Get public IPs
TOUR_OF_HEROES_PUBLIC_IP=$(kubectl get svc tour-of-heroes-api  -n $NAMESPACE -o jsonpath="{.status.loadBalancer.ingress[0].ip}")
TOUR_OF_VILLAIN_PUBLIC_IP=$(kubectl get svc tour-of-villains-api  -n $NAMESPACE -o jsonpath="{.status.loadBalancer.ingress[0].ip}")
# Variables for cURL commands
HERO_API_URL=http://$TOUR_OF_HEROES_PUBLIC_IP/api/hero
VILLAIN_API_URL=http://$TOUR_OF_VILLAIN_PUBLIC_IP/villain
# Check if hero api is working
curl $HERO_API_URL | jq
curl --header "Content-Type: application/json" \
  --request POST \
  --data '{
    "name": "Batman",
    "description": "Un multimillonario magnate empresarial y filántropo dueño de Empresas Wayne en Gotham City. Después de presenciar el asesinato de sus padres, el Dr. Thomas Wayne y Martha Wayne en un violento y fallido asalto cuando era niño, juró venganza contra los criminales, un juramento moderado por el sentido de la justicia.",
    "alterEgo": "Bruce Wayne" 
   
}' \
  $HERO_API_URL | jq
# Get heroes again
curl $HERO_API_URL | jq
# Check if villain api is working
curl --header "Content-Type: application/json" \
  --request POST \
  --data '{
    "name": "Octopus",
    "hero":{
        "name": "Spiderman",
        "description": "un joven huérfano neoyorquino que adquiere superpoderes después de ser mordido por una araña radiactiva, y cuya ideología como héroe se ve reflejada primordialmente en la expresión «un gran poder conlleva una gran responsabilidad».20​21​ Suele ser asociado con una personalidad bromista, amable, inventiva y optimista, lo que le ha llevado a ser catalogado como el «vecino amigable» de cualquiera lo cual, aunado a sus vivencias caracterizadas por los problemas cotidianos.",
        "alterEgo": "Peter Parker"     
    },
    "description": "Es un científico loco muy inteligente y algo fornido que tiene cuatro apéndices fuertes que se asemejan a los tentáculos de un pulpo, que se extienden desde la parte posterior de su cuerpo y pueden usarse para varios propósitos."
}' \
  $VILLAIN_API_URL | jq
# Check if pub sub is working viewing logs
kubectl logs -l app=tour-of-heroes-api -c tour-of-heroes-api -n $NAMESPACE
# Check if service to service invocation is working
curl $HERO_API_URL/villain/spiderman | jq
# Forward a port to Dapr dashboard
dapr dashboard -k -p 9999
# Access to zipkin to see traces
kubectl port-forward deployment/zipkin -n $NAMESPACE 9411:9411 
http://localhost:9411

Todos estos pasos los tienes en mi GitHub.

¡Saludos!