From large desktop applications to client-server applications, to the Web, to mobile, and now to AI, software has changed from being centralized to being decentralized. In today's computing landscape, we still have large pieces of software, but smaller, independent components are increasingly common.
You may remember batch jobs running as Windows services or EXE applications running as scheduled jobs. Azure Functions combine the best of the worlds of scheduled software and Web services called by another system. Read on to ramp up on:
- Understanding the progression to Azure Functions
- Choosing the right payment model
- Setting the proper Azure configuration
- Developing with triggers, inputs, and outputs
What Are Azure Functions?
Azure Functions are the next logical step in Platform as a Service, or PaaS. Azure Functions provide the ability to run discrete small units of code, or functions, in an extremely flexible, scalable, and cost-effective manner. Azure Functions offer the ultimate in infrastructure abstraction, removing any concerns about the underlying servers or operating systems. Often dubbed a “Server-less” technology, Functions allow for the quickest path from idea to business value.
Azure Functions combine the best worlds of scheduled software and Web services.
An Evolution
Cloud offerings exist to simplify or eliminate many infrastructure concerns and allow teams to focus on delivering value. Starting with virtual machines (VMs), teams no longer need to be concerned about physical servers. They can customize the environment and create applications without thinking about what happens if a hard drive crashes. When PaaS was introduced, products like Azure App Services removed another layer of concern for developers. Developers no longer needed to care about the operating system! Using PaaS, your team can simply create an application and give it to Azure for hosting. Functions are the next evolution of PaaS.
Now, instead of needing to spin up an entire ASP.NET MVC application with controllers, routes, configurations, build scripts, and deploy scripts to test out an idea, you can simply provision a new Function and go from idea to deployed proof-of-concept in minutes. With Functions, you can have a production-grade integration or API created and deployed in under 60 seconds. Oh, and you haven't paid any money yet, either.
Traditional cloud pricing models operate on reserving resources and charging you for them even if they aren't being used. Functions completely change that by only charging for the time they're being used. A deployed function incurs no runtime costs if nothing triggers it. Teams can have, literally, hundreds of Functions deployed in a live production environment, complete with enterprise-grade logging, security, and scalability, and without so much as a dime being billed against their account. On a side note, if you manage to have that many functions in production, it's probably time to reconsider your architecture.
Be Viral Ready
One problem with using VMs and standard PaaS offerings is the difficulty in using every hertz of the CPU or kilobyte of RAM efficiently. You can't provision just the right amount of resources all the time because you need to be ready for a spike. Sure, there are many ways of mitigating unnatural load against your servers and APIs, but those ways can be complicated and don't offer any more precision than simply adding another VM or App Service Container.
Functions, however, are built to be scalable by default. The runtime monitors the various ways that a Function can be invoked (known aptly as triggers) and provisions additional instances of the Functions automatically, as needed. Once the load has lowered, the runtime then deprovisions the additional instances. This means that you never need to over provision for fear of not meeting viral demands. Even if the load has tremendous peaks and very low valleys, the Functions runtime expands and contracts automatically to continue to serve requests.
Consumption Plan
Azure provides two hosting models for Functions. The first is called the Consumption Plan. This is the canonical way of using Functions. The Consumption Plan offers the elastic scalability and pay-per-use model that Functions are known for. However, it does come with a few caveats:
- Functions running on a Consumption Plan have a timeout of five minutes. Should the function run longer than five minutes, the runtime may abruptly kill the Function and any data not persisted will be lost. It's possible to extend the timeout to 10 minutes, but the timeout is set to five minutes by default
- Memory usage is limited to 1.5 GB. Remember, this is also shared among all the Functions within the Function App.
- Scaling is handled automatically and transparently based on the back pressure of triggers; the unit of scale is the Function App. When a Function App is scaled, an additional instance was provisioned. How and when the runtime scales in Function Apps is heuristic by nature. For example, if a Function is triggered by a new message in an Azure Service Bus Queue, the runtime monitors the depth of the queue and the age of the oldest message to determine if additional instances should be provisioned. There are unique scaling heuristics for each trigger type.
- Functions “turn off” after idling for a period of time and can incur a startup cost in terms of performance. This performance penalty can be mitigated (more on that below).
The pricing model for the Consumption Plan is completely based on use, not provisioning. The cost of a Function is a combination of a GB-s (a unit of resource consumption) and the number of executions.
The pricing model for the Consumption Plan is completely based on use, not provisioning.
The formula for calculating GB-s is as follows: GB-s = (number of executions) x (execution duration in seconds) x (amount of RAM used in GB).
Once the GB-s has been calculated, the cost becomes $0.000016 per GB-s + $0.20 per million executions. Table-1 illustrates an example of a Function executed two million times, taking 500 milliseconds each time and using 512 MB of RAM. Execution times are rounded up to the nearest 100 milliseconds and RAM is rounded to the nearest 128 MB. That means that the minimum amount of resources a Function can use is 100 millisecond executions and 128 MB of RAM.
The minimum amount of resources a Function can use is 100 millisecond executions and 128 MB of RAM.
Azure does offer a monthly grant of 400,000 GB-s and 1 million executions. So, if the Function operates within those constraints, no charge is made for running it.
App Service Plan
The other hosting model is the App Service Plan. With the App Service Plan, you select the configuration of a VM to be provisioned. It's the same plan that's used for other PaaS offerings, like Azure Web Apps. The number of cores and amount of RAM is static within the configuration, obviously, so choosing the correct configuration should be thoughtful as the price is directly affected. Here are the main differences between the Consumption Plan and App Service Plan:
- With the App Service Plan, a dedicated VM is provisioned, meaning you are charged for those resources even if they aren't being fully used.
- Scaling beyond the bounds of the VM is not handled by the Functions App runtime and, instead, must be configured manually.
- The memory use of a Function within an App Service Plan is limited to the configuration of the VM. In other words, your limit can be much higher.
- There's no execution time limit. Because the CPU cores are provisioned for your VM, your Function may run for as long as it needs to.
- Functions can be “always on.” In App Service Plans, you may configure a Function to be always on, thereby eliminating the idle time, shut off, and accompanying performance penalty that may happen in the Consumption Plan.
Which One to Pick?
The Consumption Plan should be the default choice. However, choose the App Service Plan if:
- You already have an underused App Service Plan that can support your Function app.
- Your Functions consume more than 1.5 GB of memory.
- Your Functions need to run actively for more than 10 minutes.
- The startup performance penalty needs to be eliminated.
- You need to configure Network options for security or access to secured resource (like VNET integration or IP whitelisting).
Triggers, Inputs, and Outputs
All Functions are triggered by some event. It may be a message being added to an Azure Storage Queue, or a new Blob created in Azure Blob Storage. Triggers can also be HTTP requests (either REST or webhooks).
In addition to triggers, Functions can have data bindings for inputting and outputting data. These bindings serve as an easy way to access different resources. The bindings handle connecting to their respective resources and manage the credentials for you. Table 2 lists the various triggers and the input and output bindings. This list will change as Azure adds capabilities.
The Hello World of Functions
To create a Function, log into the Azure portal. Click New on the top left, click Compute, then select Function App (a Function App is a container for Functions).
Figure 1 shows the basic configurations you need to make.
- Give the Function App a globally unique name. It needs to be globally unique because these can be triggered via HTTP requests. Notice the
.azurewebsites.net
that appears underneath the App Name input box at the top of the far right column. - Select the appropriate subscription.
- Use an existing Resource Group or create a new one.
- Choose the right hosting plan for the Function App: either Consumption or App Service Plan.
- Select the correct region. As always, keep your resources close together to reduce latency. Calls within the same datacenter experience a latency of around 1-2 milliseconds in overhead. Going from East to West regions, for example, can incur more than 50 milliseconds overheard.
- Choose your storage account.
Once the Function App is created, clicking the plus (+) button next to Functions gives an option for quickly creating a new Function using either Webhook or API, and Timer or Data processing triggers. This is shown in Figure 2. These premade Functions, used for getting started quickly, come in C#, F# and JavaScript, but you can create custom Functions using any of the following languages: Bash, Batch, C#, F#, JavaScript, PHP, PowerShell, Python, and TypeScript.
The Online Editor
Once the Function is created, you're presented with the online editor; see Figure 3 for a quick look. Here, you can write C# (or the language you chose to write the Function in) to create APIs, process events, handle incoming data, etc. The function shown in Figure 3 uses an HTTP trigger and is ready to be invoked. Near the top right, there's a link for grabbing the URL of the Function. On the right lies a built-in mechanism for testing and the bottom shows the live logs. Expanding both of those sections, as shown in Figure 4, completes the lightweight development environment in the portal.
With the Test and Log sections, you'll find a sample body for the HTTP request that comes with the templated Function.
The sample input defaults to setting the name parameter to Azure
. Here, you can add any parameters that are needed to test the logic in your Function. Clicking Run executes the Function with the test data you've specified. The output is displayed in the bottom right corner and the Logs section shows the successful execution of your new Function.
And that's it. That's the Hello World for Functions. However, that's just the start of Functions. Going back to the Function App blade that was created, there's a tab on top called Platform Features. See Figure 5.
On the Platform Features tab, the rest of the Functions configuration story comes on the scene. Here, you can easily manage app settings and wire up deployments from a slew of providers, including the most popular Git hosting services. You can integrate your App Insights instance with your Functions. You are using App Insights, right?
Other features include setting CORS policies, forcing OAuth2 integrations, custom domains, and SSL certificates. Also, if your Function App uses the App Service Hosting Plan, you can configure VNET integration and whitelisting, among other network configurations.
With Visual Studio 2017 15.3.3, you can create Functions locally and even use the local runtime to host and test your Functions.
Local Development
Using the latest Visual Studio 2017 updates (as of this writing, the current version is 15.3.3), you can create Functions locally and even use the local runtime to host and test your Functions. To develop locally, simply create a new project using the Azure Function App project template and add a new Azure Function. The templated
file using HTTP triggers is the same as the one online. Pressing F5 from there gives you a console app that loads a small Functions hosting environment; Figure 6 shows this.
With the local development story complete, you can continue with all the best practices, including implementing a full, end-to-end, Continuous Delivery Pipeline complete with automated testing and deployments.
Conclusion
In this article, you've read an overview of Azure Functions. You've explored the pricing models available, and the methods by which they can be integrated into a production environment with triggers, inputs, and outputs. And you've followed along, configuring the necessary parts of Azure in order to develop your own code that can run in this environment. Now that you know what Functions are, you can explore where they can be used in your production scenarios. With Functions excelling in handling unpredictable loads, scaling to meet massive demand, and extending system architecture via events, you can use them in a number of scenarios. Whether you are refactoring a monolithic system into a series of smaller, “right sized” services, or quickly creating proofs of concept, Functions help your teams deliver value for their customers.
Table 1: An Example of Calculating Costs for a Function
Monthly Production Values | |
Average Memory Consumption | 512 MB |
Function Execution Duration | .5 Second |
Number of Executions | 2,000,000 |
Calculating Resource Consumption (Time) | |
Executions | 2,000,000 |
Multiplied by Execution Duration | X .5 seconds |
Resource Consumption (seconds) | 1,000,000 |
Calculating Resource Consumption (Memory) | |
Average Memory Used (in GB) | 512 MB / 1024 MB = .5 GB |
Multiplied by Consumption Seconds | X 1,000,000 |
Total Resource Consumption (GB-s) | 500,000 GB-s |
Minus Monthly Free Grant | - 400,000 GB-s |
Billable Resource Consumption | 100,000 GB-s |
Billable Resource Consumption | 100,000 GB-s |
Multiplied by Cost/GB-s | $0.000016 |
Resource Consumption Cost | $1.60 |
Cost of Executions | |
Total Executions | 2 million |
Minus Monthly Free Grant | - 1 million |
Billable Executions | 1 million |
Billable Executions | 1 million |
Multiplied by $.20 per Million Executions | X $.20 |
Total Cost of Executions | $0.20 |
Total Cost of Function | |
Resource Consumption Cost | $1.60 |
Plus Execution Cost | + $0.20 |
Total Cost | $1.80 |
Table 2: Trigger and Input/Output Bindings Options
Service | Trigger | Input | Output |
Azure Schedule | X | ||
HTTP (REST or webhook) | X | X | |
Azure Blob Storage | X | X | X |
Event from Azure Event Hubs/Grid | X | X | |
Azure Storage Queues | X | X | |
Azure Service Bus Queues and Topics | X | X | |
Azure Event Grid | X | ||
External Files * | X | X | X |
Azure Storage Tables | X | X | |
Azure Mobile Apps SQL Tables | X | X | |
Azure Cosmos DB Documents | X | X | |
Azure Push Notifications | X | ||
Twilio SMS Text | X | ||
SendGrid Email | X | ||
Bot Framework ** | X | ||
* External File triggers and input and output bindings are currently in preview and integrate with File Storage, DropBox, Box, OneDrive, OneDrive for Business, File System, FTP, and SFTP | |||
** Bot Framework output is currently in preview |