The Curious Developer's Guide to Portable Azure Functions
Tags: serverless, azure, keda, serverlessseptember
Published: 2020-09-06 16:41:20

This article is part of [#ServerlessSeptember](https://aka.ms/ServerlessSeptember2020). You'll find other helpful articles, detailed tutorials, and videos in this all-things-Serverless content collection. New articles from community members and cloud advocates are published every week from Monday to Thursday through September.&nbsp;&nbsp;&nbsp;
  
Find out more about how Microsoft Azure enables your Serverless functions at [https://docs.microsoft.com/azure/azure-functions/](https://docs.microsoft.com/azure/azure-functions/?WT.mc_id=servsept20-devto-cxaall).&nbsp;&nbsp;&nbsp;    


---



[Azure Functions](https://azure.microsoft.com/en-us/services/functions/) allow you to execute small snippets of code, in the cloud, without concern for cloud infrastructure. These functions are triggered by several different types of event sources, making them the building blocks of an event-driven or "serverless" architecture. They're easy to write, deploy, and connect to other cloud services to create powerful applications.

Azure Functions are also [open source](https://github.com/Azure/Azure-Functions)!    


*But did you know they're also... <i>portable</i>?*     


   





![Pee Wee on Bike](https://media.giphy.com/media/xT0GqrJNbZkRcr2Jgc/giphy.gif)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;









Function Apps runtimes can run in a container. And containers are managed by Kubernetes. And Kubernetes can run just about anywhere.


*Even outside of Azure.* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

<br><br><br><br>


**Azure Functions, in Kubernetes, running outside of Azure?**  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;         

<br><br>  


![Evil Witch](https://media.giphy.com/media/AVYgfmc0UiP9m/giphy.gif)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

<br><br>





What about the event-driven nature of "serverless" applications with Azure Functions? When Function Apps are fully-managed on Azure, container instances are added (or removed) based on the number of incoming trigger events. This makes scaling to support message load nearly seamless. The Azure Functions runtime can run *anywhere* but what about the scale controller?    



The [Horizontal Pod Autoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) in Kubernetes provides some autoscaling to support spikes in CPU intensity or some other custom application metrics. To replicate the event-based scaling that we're used to with Azure Functions, the HPA needs a little help from an open-source project called [KEDA](https://keda.sh/).    




<br><br>


KEDA, or <b>Kubernetes-based Event Driven Autoscaler</b>, does exactly as described. It extends (but doesn't duplicate) the functionality of the Horizontal Pod Autoscaler. It supports events triggers from a [large variety of sources](https://keda.sh/docs/1.5/scalers/) both internal and external to large cloud providers. KEDA's scaler takes metric values from the event source and creates custom metrics to send to the HPA to support scaling based on the event load.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

<br><br>

![Yada Yada Yada](https://media.giphy.com/media/xT9DPObyHCA9Mhya6A/giphy.gif)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  
<br><br>



**So, let's do it. Let's make portable Azure Functions.** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  




<br><br>



![Let's Do It](https://media.giphy.com/media/l1adwi79gVdIdWz3CB/giphy.gif)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;



 




<br><br>

---

Assumption is that you already have these things:

* Installed [Azure Function Core Tools v2](https://github.com/azure/azure-functions-core-tools#installing)

* An Azure Subscription (this is for the storage queue, not for Azure Functions)

* A Kubernetes cluster. It can be [AKS](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough-portal), GKE, EKS, OpenShift, Kubernetes on-prem, whatever and wherever.

* [`kubectl`](https://kubernetes.io/docs/tasks/tools/install-kubectl/) with `current-context` set to your Kubernetes cluster.   









<br><br>

And now we're ready to package up our function apps and take them on the road.   


![Leslie Knope](https://media.giphy.com/media/xRfxdozZr2zYI/giphy.gif)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;




    



<br><br><br>





---

####1. Make a directory for the Function App for your Azure Functions

```bash
mkdir functions-everywhere
cd functions-everywhere
```

####2. Initialize the Functions directory

```bash
func init . --docker
```

`--docker` flag creates a Dockerfile for a container using a base image that is based on the chosen `--worker-runtime`

Choose your runtime and language.

####3. Create a new Azure Function and define the trigger type 

```bash
func new
```

Use the *Azure Queue Storage Trigger*

You can rename the function, or just leave the default for this demo.

####4. Create an Azure storage account and queue

* [Create storage account in the portal](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-create?tabs=azure-portal)

* Go to your new storage account and under the **Queue service** heading, select **Queues** and create a new queue. Take note of the queue name.

####5. Update your function with the storage account names

Get the connection string for your new storage account:

```bash
az storage account show-connection-string --name <storage-name> --query connectionString
```

Edit the `local.settings.json` file in your function app, which contains the local debug connection string settings.  Replace the `{AzureWebJobsStorage}` with the connection string value:

`local.settings.json`

```json
{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=yourstorageaccount;AccountKey=shhhh==="
  }
}
```

Now, open the `function.json` file and set the `connection` setting value to `AzureWebJobsStorage`.  This tells the function to pull the connection string from the `AzureWebJobsStorage` key we set above.

`function.json`

```json
{
  "bindings": [
    {
      "name": "myQueueItem",
      "type": "queueTrigger",
      "direction": "in",
      "queueName": "<your-queue-name>",
      "connection": "AzureWebJobsStorage"
    }
  ]
}
```

####6. Enable the storage queue bundle for the function runtime

Ensure that `host.json` contains the extensions bundle to allow Azure Storage Queues binding support.

`host.json`

```json
{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[1.*, 2.0.0)"
  }
}
```

####7. Install KEDA in your cluster

```bash
func kubernetes install --namespace keda
```

Confirm that KEDA is installed:

```bash
kubectl get customresourcedefinition

NAME                        AGE
scaledobjects.keda.k8s.io   2h
```

####8. Deploy your Function App to Kubernetes

*Note: This assumes that you have a Docker account and you've already used `docker login` to sign-in through the cli.*

```bash
func kubernetes deploy --name <function-app-name-lowercase> --registry <your-docker-registry>
```

This command build the docker container, push it to the specified registry, generate a YAML file, and deploy to your Kubernetes cluster.

If you'd like to save a copy of the YAML deploy file, use the `dry-run` flag:

```bash
func kubernetes deploy --name <function-app-name-lowercase> --registry <your-docker-registry> --dry-run > func-deployment.yml
```


####9. See your function scaling as messages are added

To add a message to your storage queue, go to your Azure Storage account in the [Azure Portal](https://portal.azure.com) and open the *Storage Explorer*.  Select your storage queue and add a new message.

*You should initially see `0` pods since the function has not started scaling yet.*

```bash
kubectl get deploy
```

*Note: By default, the polling interval set is 30 seconds on the `ScaledObject` resource and the cooldown period is 300 seconds.*

```bash
kubectl get pods -w
```

After all messages are consumed by the function app, and the cooldown period has elapsed, the last pod should scale back down to `0`.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;


  




<br><br>

---
**Congrats!** You are now using *portable* Azure Functions.   



![Leslie Dancing](https://media.giphy.com/media/2AilMg2L8rTAA/giphy.gif)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;


   


<br><br>

---

####More Resources

* [Official KEDA Docs](https://keda.sh/) 
* [More Samples!](https://github.com/kedacore/samples)
* [Microsoft Azure Docs](https://docs.microsoft.com/en-us/azure/azure-functions/functions-kubernetes-keda)