0 Comments Posted in:

One of the great things about version 2 of the Azure Functions runtime, is that it runs on .NET Core, which means it is cross-platform. This is great for anyone wanting to use the Azure Functions Core Tools on a non-Windows platform, but it also opens up the possibility of running your Azure Function App in a Docker container.

Why Docker?

Now you might be wondering - why would you even want to do this? After all, the "consumption plan" is a superb way to host your Function App, bringing you all the benefits of serverless - you don't need to provision any infrastructure yourself, you pay only while your functions are running, and you get automatic scale out.

None of this is true if you choose to host your Function App in a Docker container. However, it does open the door to hosting in a lot more environments than were previously possible. You can use it to host Function Apps on premises or in other cloud providers for example. Or maybe you're using something like AKS for all your other services, and would just like the consistency of everything being packaged as a Docker container.

Creating a Dockerfile

The easiest way to create a Dockerfile for an Azure Function app is to install the Azure Functions Core Tools (you will need v2), and run the func init --docker command.

This will prompt you for what worker runtime you want - the choices are currently dotnet, node or java - choose dotnet if you're writing your functions in C# or F#, and node if you're writing in JavaScript.

This will create a new empty Function App, including a Dockerfile. The only real difference is based on what worker runtime we chose as there's a different base image used - for dotnet its microsoft/azure-functions-dotnet-core2.0 and for node it's microsoft/azure-functions-node8.

Here's the Dockerfile it creates for a node Function App. It simply sets an environment variable and then copies everything in to the home/site/wwwroot folder.

FROM microsoft/azure-functions-node8:2.0
ENV AzureWebJobsScriptRoot=/home/site/wwwroot
COPY . /home/site/wwwroot

I took a very slightly different approach for my C# Function App, allowing me to do a docker build from the root directory of my project, and copying the contents of the release build of my Function App. Obviously its just a matter of preference how you set this up:

FROM microsoft/azure-functions-dotnet-core2.0:2.0
ENV AzureWebJobsScriptRoot=/home/site/wwwroot
COPY ./bin/Release/netstandard2.0 /home/site/wwwroot

Build and run locally

Building the container is very simple - just issue a docker build command, and give it a name and tag:

docker build -t myfuncapp:v1 . 

And then to run your Docker container locally, it's again very straightforward. You might want to expose a port if you have HTTP triggered functions, and set up any environment variables to connection strings your function needs:

docker run -e MyConnectionString=$connStr -p 8080:80 myfuncapp:v1 

Try it out in Azure Container Instances

Once you've created your Docker image, you can easily get it up and running in Azure, with Azure Container Instances and the Azure CLI (check out my Pluralsight courses on ACI and Azure CLI for tutorials on getting started with these).

I've create a very simple Azure Functions demo app, and created a container for it which is available here. It implements a simple TODO API, and it also uses Azure Functions proxies to proxy through to a static webpage (hosted externally in blob storage) that can be used to test the API. It also stores its data in table storage, so it does need a storage account.

Here's some PowerShell that uses the Azure CLI to create a resource group and a storage account (to store the todo items), and then creates a container to run the function app. The container needs two environment variables - one to tell it the connection string of the storage account, and one to tell our proxy where to find the static web content, currently I've put it in a public blob container available at https://serverlessfuncsbed6.blob.core.windows.net/website but that won't necessarily be available forever, so if you want to try this out yourself, you may need to copy the website's static HTML, CSS and JavaScript content from my GitHub repository to a public blob and set the proxy destination to point there.

# Create a resource group to store everything in
$resGroup="serverless-funcs-docker2"
$location="westeurope"
az group create -n $resGroup -l $location

# create a storage account
$storageAccount="serverlessfuncsdocker2"
az storage account create -n $storageAccount -g $resGroup -l $location --sku Standard_LRS

# get the connection string
$connStr=az storage account show-connection-string -n $storageAccount -g $resGroup --query connectionString -o tsv

# create a container
# point it at the storage account (AzureWebJobsStorage) and at static web content (WEB_HOST)
$containerName="serverless-funcs-2"
$proxyDestination="https://serverlessfuncsbed6.blob.core.windows.net/website"
az container create `
-n $containerName `
-g $resGroup `
--image markheath/serverlessfuncs:v2 `
--ip-address public `
--ports 80 `
--dns-name-label serverlessfuncsv2 `
-e AzureWebJobsStorage=$connStr `
   WEB_HOST=$proxyDestination

# check if the container has finished provisioning yet
az container show -n $containerName -g $resGroup --query provisioningState

# get the domain name (e.g. serverlessfuncsv2.westeurope.azurecontainer.io)
az container show -n $containerName -g $resGroup --query ipAddress.fqdn -o tsv

# check the container logs
az container logs -n $containerName -g $resGroup

# to clean up everything (ACI containers are paid per second, so you don't want to leave one running long-term)
az group delete -n $resGroup -y

If all goes well, here's what you should see. The Function App also has a scheduled function that deletes completed tasks every five minutes:

image

Want to learn more about how easy it is to get up and running with Azure Container Instances? Be sure to check out my Pluralsight course Azure Container Instances: Getting Started.

0 Comments Posted in:

Last week I posted my third LINQ Challenge. Thanks to everyone who attempted the puzzles. As usual I learned a lot from the different ways people attempted the puzzles. In this post I'll share how I solved the puzzles, and I've tried to take on board a few of the tips I picked up from looking at other people's approaches.

Problem 1 - Longest Sequence

The following string contains number of sales made per day in a month:

"1,2,1,1,0,3,1,0,0,2,4,1,0,0,0,0,2,1,0,3,1,0,0,0,6,1,3,0,0,0"

How long is the longest sequence of days without a sale? (in this example it's 4).

This puzzle was a tricky one, as we need to group on sequences within a sequence. The approach I took was to convert it into a string of Y's and N's based on whether there was a sale that day. Then I split the string on the letter 'Y', to leave me with a sequence of strings containing only the letter 'N'. The longest string is then the longest sequence of days without a sale.

"1,2,1,1,0,3,1,0,0,2,4,1,0,0,0,0,0,2,1,0,3,1,0,0,0"
    .Split(',')
    .Select(n => n == "0" ? "N":"Y")
    .Aggregate ("", (acc, next) => acc+next)
    .Split(new [] {'Y'}, StringSplitOptions.RemoveEmptyEntries)
    .Max(n=>n.Length)

Another way to solve it is to make use of the GroupAdjacent method from MoreLINQ. This lets you select a "key" for every element in the sequence, and then group the sequence up based on that key. So here I group the days sales totals into groups of "Y" and "N", and then I find the largest group whose key is "N":

sales
.Split(',')
.GroupAdjacent(n => n == "0" ? "N" : "Y")
.Where(g => g.Key == "N")
.Max(g => g.Count())

Problem 2 - Full House

In poker a hand is a "full house" if it contains three cards of one value and two of another value. The following string defines five poker hands, separated by a semicolon:

"4♣ 5♦ 6♦ 7♠ 10♥;10♣ Q♥ 10♠ Q♠ 10♦;6♣ 6♥ 6♠ A♠ 6♦;2♣ 3♥ 3♠ 2♠ 2♦;2♣ 3♣ 4♣ 5♠ 6♠".

Write a LINQ expression that returns an sequence containing only the "full house" hands.

We can detect if a string representing a single hand is a full house by grouping on the card value (we can cheat by just looking at the first character which is always unique - which was a nice optimization from Mark Jones). Then it's a full house if our hand only contains groups of size 2 or 3.

"4♣ 5♦ 6♦ 7♠ 10♥;10♣ Q♥ 10♠ Q♠ 10♦;6♣ 6♥ 6♠ A♠ 6♦;2♣ 3♥ 3♠ 2♠ 2♦;2♣ 3♣ 4♣ 5♠ 6♠"
    .Split(';')
    .Where(hand => hand.Split(' ')
                       .GroupBy(value => value[0])
                       .All(g => g.Count() == 2 || g.Count() == 3))

Problem 3 - Christmas Days

What day of the week is Christmas day (25th December) on for the next 10 years (starting with 2018)? The answer should be a string (or sequence of strings) starting: Tuesday,Wednesday,Friday,...

This one was a nice easy one, meant to highlight the usefulness of the Enumerable.Range method which lets you specify a starting number and the number of elements in the sequence. We can then construct a new DateTime, and access the DayOfWeek property:

String.Join(",", Enumerable.Range(2018,10)
    .Select(y => new DateTime(y,12,25))
    .Select(d => d.DayOfWeek))

Problem 4 - Anagrams

From the following dictionary of words,

"parts,traps,arts,rats,starts,tarts,rat,art,tar,tars,stars,stray"

return all words that are an anagram of star (no leftover letters allowed).

The key to solving this challenge is realizing that if you sort the letters in the words into alphabetical order, those that are anagrams will all have the same value (in this case arst). This allows us to do a simple filter on the sorted values of each string

"parts,traps,arts,rats,starts,tarts,rat,art,tar,tars,stars,stray"
    .Split(',')
    .Where(x => new string(x.OrderBy(c => c).ToArray()) == "arst")

By the way, this is a good example of where a C# 7 "local function" can help readability. If we define our own local Sort method that sorts a string, the LINQ expression becomes more readable:

string Sort(string s) => new string(s.OrderBy(c => c).ToArray());
"parts,traps,arts,rats,starts,tarts,rat,art,tar,tars,stars,stray"
    .Split(',')
    .Where(x => Sort(x) == Sort("star"))

Problem 5 - Initial Letters

From the following list of names

"Santi Cazorla, Per Mertesacker, Alan Smith, Thierry Henry, Alex Song, Paul Merson, Alexis Sánchez, Robert Pires, Dennis Bergkamp, Sol Campbell"

find any groups of people who share the same initials as each other.

This was a fairly simple grouping problem. I learned from Mark Jones' answer that there is an overload of String.Split that allows us to split on a string ", " which saves us trimming the space as a second step after splitting on comma.

We then just group by the initials, using String.Join to construct them out of the first letter of each part of the name, and then filter out any groups with only one element:

names
    .Split(new[] { ", "},StringSplitOptions.None)
    .GroupBy(n => String.Join("", n.Split(' ').Select(p => p[0])))
    .Where(g => g.Count() > 1)

Problem 6 - Video Editing

A video is two hours long exactly, and we want to make some edits, cutting out the following time ranges (expressed in H:MM:SS):

"0:00:00-0:00:05;0:55:12-1:05:02;1:37:47-1:37:51".

(You can assume that the input ranges are in order and contain no overlapping portions)

We would like to turn this into a sequence of time-ranges to keep. So in this example, the output should be:

"0:00:05-0:55:12;1:05:02-1:37:47;1:37:51-2:00:00"

This one was a tricky one, and I was intending to show the value of MoreLINQ's Batch, Prepend and Append methods. The approach I am taking is treating every time as simply an edit point, and then by appending an extra element to the start and end of the list, we can then group them up into pairs (with Batch) and then filter out any empty ranges before joining it all together.

Here's the approach with MoreLINQ:

"0:00:00-0:00:05;0:55:12-1:05:02;1:37:47-1:37:51"
    .Split(';', '-')
    .Prepend("0:00:00")
    .Append("2:00:00")
    .Batch(2, b => b.ToArray())
    .Where(a => a[0] != a[1])
    .Select(a => $"{a[0]}-{a[1]}")
    .Aggregate((x, y) => x + ";" + y)

However, there is a problem with some MoreLINQ extension method names clashing with LINQ methods. For example, the Prepend and Append methods are new in .NET 4.7.1. This is something the MoreLINQ maintainers are aware of and the workaround is to explictly enable only the MoreLINQ extension methods you plan to use with the following syntax (there's an "extension" per MoreLINQ method):

using static MoreLinq.Extensions.BatchExtension;

Now the code will compile in .NET 4.7.1 (and use LINQ's built-in Prepend and Append) but it does mean that if you wanted to use any other MoreLINQ methods (such as ToDelimitedString instead of Aggregate) you'd need to add another using static statement

using static MoreLinq.Extensions.ToDelimitedStringExtension;

...

.ToDelimitedString(";")

Of course you can create a pure LINQ solution to this problem, but it needs a bit of a hack to batch into groups of two:

"0:00:00-0:00:05;0:55:12-1:05:02;1:37:47-1:37:51"
    .Split(';','-')
    .Prepend("0:00:00")
    .Append("2:00:00")

    // ugly way to batch
    .Select((item, index) => new { item, index })
    .GroupBy(x => x.index / 2)
    .Select(g => g.Select(x => x.item).ToArray())

    // eliminate empty ranges
    .Where(a => a[0] != a[1])

    .Select(a => $"{a[0]}-{a[1]}")
    .Aggregate ((x, y) => x + ";" + y)

Hear me speak on LINQ at Techorama

Finally, on 3rd October 2018 I'll be speaking on LINQ at Techorama. Do come and say hello if you can make it.

Want to learn more about LINQ? Be sure to check out my Pluralsight course More Effective LINQ.

0 Comments Posted in:

I'm very excited to announce that in October I'll be speaking on LINQ at Techorama Netherlands, sharing some of my best practices for becoming more effective with LINQ.

This means its time for another LINQ Challenge! These are short programming challenges, can be solved with a single LINQ expression. Of course, that might not always make for the most readable code, so feel free to solve these with or without the help of LINQ, and of course solutions in other languages are welcome. If you're using LINQ, then the MoreLINQ library often has extension methods that simplify the task.

This challenge is a little more tricky than the previous ones, so if you'd like a more gentle introduction, try some of the earlier challenges first (I've linked to the answers, but I recommend trying to solve them first before looking at the solutions)

Problem 1 - Longest Sequence

The following string contains number of sales made per day in a month:

"1,2,1,1,0,3,1,0,0,2,4,1,0,0,0,0,2,1,0,3,1,0,0,0,6,1,3,0,0,0"

How long is the longest sequence of days without a sale? (in this example it's 4)

Problem 2 - Full House

In poker a hand is a "full house" if it contains three cards of one value and two of another value. The following string defines five poker hands, separated by a semicolon:

"4♣ 5♦ 6♦ 7♠ 10♥;10♣ Q♥ 10♠ Q♠ 10♦;6♣ 6♥ 6♠ A♠ 6♦;2♣ 3♥ 3♠ 2♠ 2♦;2♣ 3♣ 4♣ 5♠ 6♠".

Write a LINQ expression that returns an sequence containing only the "full house" hands.

Problem 3 - Christmas Days

What day of the week is Christmas day (25th December) on for the next 10 years (starting with 2018)? The answer should be a string (or sequence of strings) starting: Tuesday,Wednesday,Friday,...

Problem 4 - Anagrams

From the following dictionary of words,

"parts,traps,arts,rats,starts,tarts,rat,art,tar,tars,stars,stray"

return all words that are an anagram of star (no leftover letters allowed).

Problem 5 - Initial Letters

From the following list of names

"Santi Cazorla, Per Mertesacker, Alan Smith, Thierry Henry, Alex Song, Paul Merson, Alexis Sánchez, Robert Pires, Dennis Bergkamp, Sol Campbell"

find any groups of people who share the same initials as each other.

Problem 6 - Video Editing

A video is two hours long exactly, and we want to make some edits, cutting out the following time ranges (expressed in H:MM:SS):

"0:00:00-0:00:05;0:55:12-1:05:02;1:37:47-1:37:51".

(You can assume that the input ranges are in order and contain no overlapping portions)

We would like to turn this into a sequence of time-ranges to keep. So in this example, the output should be:

"0:00:05-0:55:12;1:05:02-1:37:47;1:37:51-2:00:00"

Share your answers

I hope you have fun solving these. Why not your solutions in GitHub Gists, and share your approach with the rest of us in the comments below. Although I've already created LINQ solutions to each of these puzzles which I'll post later, every time I do this I find I learn something from the way other people have approached the problems.

Want to learn more about LINQ? Be sure to check out my Pluralsight course More Effective LINQ.