Azure Functions and Serverless - configuration and bindings

Azure Functions support in Serverless is still a fresh thing, yet it gives us plenty of different configuration options and bindings with many Azure components. In this post we'll quickly go through configuration file and compare Serverless implementation of functions with an original solution using Azure Portal or Visual Studio.

Configuration

In Serverless the file responsible for configuring a function app is named serverless.yml. If you go to the one attached to the boilerplate code, which you can using following command:

/
serverless install --url https://github.com/azure/boilerplate-azurefunctions

You'll see following structure:

/
service: azure-functions-app1234

provider:
  name: azure
  location: West US
  #armTemplate:
     #file: YourARMTemplate.json
     #parameters:
      #VariableNameToUserInArmTemplate: Value

plugins:
  - serverless-azure-functions

package:
  exclude:
    - node_modules/**
    - .gitignore
    - package.json
    - .git/**

functions:
  httpjs: 
     handler: handler.hello
     events: 
       - http: true
         x-azure-settings:
           authLevel : anonymous
           
  queuejs: 
     handler: handler.helloQueue
     events: 
       - queue: SampleQueue
         x-azure-settings:
           name: queueItem
           connection : AzureWebJobsStorage
       - blob:
         x-azure-settings:
           name: blobOut
           direction: out
           connection : AzureWebJobsStorage

As you can still you can store whole configuration and functions references in the only one file. This greatly improves manageability and helps in keeping a solution clean. Let's dive a bit deeper into it.

Bindings

There're planty of different bindings to choose from when working both with Azure Functions and Serverless. Currently you can use HTTP, Timer, Queue, Service Bus, Event Hub or Blob Storage. What is more, Serverless allows to use additional bindings like DocumentDB, Notification Hubs or Mobile Apps. In fact, because the implementation of bindings is something that you don't have to think about, whole solution is pretty flexible and straightforward. Let's compare serverless.yml with a basic C# example:

/
functions:
  example:
    handler: handler.hello
    events:
      - http: true
        x-azure-settings:
            name: req #<string>, default - "req", specifies which name it's available on `context.bindings` 
            methods: #<array> [GET, POST, PUT, DELETE], default - all
                - GET
            route: example/hello #<string>, default - <function name>
            authLevel: anonymous #<enum - anonymous|function (default)|admin>
/
{
    "disabled": false,
    "bindings": [
        {
            "name": "req",
            "type": "httpTrigger",
            "direction": "in",
            "authLevel": "function",
            "methods": [
                "post"
            ],
            "route": "example/hello"
        },
        {
            "name": "res",
            "type": "http",
            "direction": "out"
        }
    ]
}

As you can see, both configurations are quite similar. The main difference is the whole structure of a solution - when it comes to working with Serverless, you have a single file where it stores all the data.

Remember that if you'd like to use multiple bindings, each one will be available from the context.bindings array with a corresponding name. Consider this example:

/
/
functions:
  example:
    handler: handler.hello
    events:
      - http: true
        x-azure-settings:
            name: req #<string>, default - "req", specifies which name it's available on `context.bindings` 
            methods: #<array> [GET, POST, PUT, DELETE], default - all
                - GET
            route: example/hello #<string>, default - <function name>
            authLevel: anonymous #<enum - anonymous|function (default)|admin>
      - eventHub:
        x-azure-settings:
            name: item #<string>, default - "myEventHubMessage", specifies which name it's available on `context.bindings` 
            path: hello #<string>, specifies the Name of the Event Hub
            consumerGroup: $Default #<string>, default - "$Default", specifies the consumerGroup to listen with
            connection: EventHubsConnection #<string>, App Setting/environment variable which contains Event Hubs Namespace Connection String

In such case you'll have both context.bindings.req and context.bindings.item properties available and usable.

Examples for Serverless were taken from the official documentation.

EventStore on Azure and Ubuntu - it's a piece of cake! #4

Finally - the last but not least post about setting your own EventStore instance using Ubuntu on Azure. We've already prepared the majority of work needed here, so it shouldn't be difficult to adjust is just a bit to use DNS instead of hardcoded IPs.

Configuration

Currently our configuration looks like this:

/
---
RunProjections: None
ClusterSize: 3
GossipSeed: 10.0.3.6:2112,10.0.3.5:2112
DiscoverViaDns: False
ExtIp: 10.0.3.4
IntIp: 10.0.3.4

Clearly the first change is to get rid of the DiscoverViaDns property. The reason why we're going to remove it is the fact, that it's set to true by default. However, it appears that we need two more properties: ClusterDns and ClusterGossipPort. Additionally we'll remove the GossipSeed property as it also won't work here anymore. Let's get to work!

Network, DNS and Azure

When we created Ubuntu VMs in Azure we're given a single instance of a virtual network. You can think about it as a logical representation of your network in Azure - you manage IPs, DNS and other settings without installing physical devices. It gives you isolation and security - if you want to, you can forbid both inbound and outbound traffic. What we're interested in right now is its DNS capability. Currently you have two options:

  • use a DNS provided by Azure
  • use your own DNS 

Unfortunately using the former won't work here - we have to add records manually, what is not allowed when using Azure DNS. Obtaining and configuring a DNS server is beyond the scope of this post - if you're interested take a look here. The good thing is that it's still possible within Azure and additional tools are required. Once you have your DNS, the configuration should look similar to:

/
---
RunProjections: None
ClusterSize: 3
ExtIp: 10.0.3.4
IntIp: 10.0.3.4
IntTcpPort: 1111
ExtTcpPort: 1112
IntHttpPort: 2113
ExtHttpPort: 2114
ClusterDns: domain.com
ClusterGossipPort: 2113

DNS entries

The tricky thing here is to set correct entries in your DNS server. What you have to do here is to add an A entry pointing to your private IPs inside a network. Note that there's no concerns in doing this - it's a common practice. The one issue here is that it describes a little how your local network looks like - while accessing domain from an Azure network will point to the correct machine, when one tries to access it from an external network, he will be redirected to the private IP being the same as the one used in an entry.

Summary

This is it - we've went through installing, configuring and managing EventStore using Ubuntu and Azure. I strongly encourage you to discover other OSS solution, which could be run using such configuration and play with them, it becomes more and more fun.