Playing with Azure Functions kubernetes integration

Tsuyoshi Ushio
6 min readAug 13, 2018

--

We can deploy Azure Functions on your kubernetes cluster. I’d like to share some learnings when I create/deploy queue trigger functions.

Azure Functions Queue Trigger on Kubernetes

Install

The official documentation’s process works when I tried my WSL (Ubuntu 18.06).

You may need to install dependency in advance. I share my personal script to configure new WSL environment. I already have an Docker for Windows and Kubernetes cluster on Azure.

NOTE: I can see the different install instruction on the GitHub repo, it didn’t work when I tried. I create an issue.

Create a Queue Trigger function

func init --docker command create a template of the project. I tried node.

func init --dockerSelect a worker runtime:1. dotnet2. node3. javaChoose option: 2nodeWriting .gitignoreWriting host.jsonWriting local.settings.jsonWriting /mnt/c/Users/tsushi/Codes/AzureFunctions/k8s/.vscode/extensions.jsonWriting Dockerfile

Dockerfile seems very simple.

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

Then create Queue Trigger function. We can choose several bindings. I’m very happy because one of the charm of the Auzre Functions is bindings.

$ func new echo
Select a language: Select a template:
1. Blob trigger
2. Cosmos DB trigger
3. Event Grid trigger
4. HTTP trigger
5. Queue trigger
6. SendGrid
7. Service Bus Queue trigger
8. Service Bus Topic trigger
9. Timer trigger
Choose option: 5
Queue trigger
Function name: [QueueTriggerJS]
Writing /k8s/QueueTriggerJS/index.js
Writing /k8s/QueueTriggerJS/readme.md
Writing /k8s/QueueTriggerJS/sample.dat
Writing /k8s/QueueTriggerJS/function.json

It generates template. These are exactly the same as the normal azure functions.

index.js

module.exports = function (context, myQueueItem) {context.log('JavaScript queue trigger function processed work item', myQueueItem);context.done();};

function.json

{
"disabled": false,
"bindings": [
{
"name": "myQueueItem",
"type": "queueTrigger",
"direction": "in",
"queueName": "js-queue-items",
"connection": ""
}
]
}

Deploy to the cluster

Unfortunately, It doesn’t work.

$ docker login
func deploy --platform kubernetes --name queuefuncti
on --registry tsuyoshiushio
Building Docker image...
Running docker build -t tsuyoshiushio/queuefunction-azurefunc /mnt/c/Users/tsushi/Codes/AzureFunctions/k8s
Sending build context to Docker daemon 11.26kB
Step 1/3 : FROM microsoft/azure-functions-node8:2.0
2.0: Pulling from microsoft/azure-functions-node8
be8881be8156: Pulling fs layer
f854db899319: Pulling fs layer
4591fd524b8e: Pulling fs layer
65f224da8749: Pulling fs layer
8f02cd78a468: Pulling fs layer
60b13fe54b74: Pulling fs layer
65f224da8749: Waiting
8f02cd78a468: Waiting
60b13fe54b74: Waiting
4591fd524b8e: Verifying Checksum
4591fd524b8e: Download complete
f854db899319: Verifying Checksum
f854db899319: Download complete
be8881be8156: Download complete
be8881be8156: Pull complete
f854db899319: Pull complete
4591fd524b8e: Pull complete
60b13fe54b74: Retrying in 5 seconds
60b13fe54b74: Retrying in 4 seconds
60b13fe54b74: Retrying in 3 seconds
60b13fe54b74: Retrying in 2 seconds
60b13fe54b74: Retrying in 1 second
60b13fe54b74: Verifying Checksum
60b13fe54b74: Download complete
65f224da8749: Verifying Checksum
65f224da8749: Download complete
65f224da8749: Pull complete
8f02cd78a468: Verifying Checksum
8f02cd78a468: Download complete
8f02cd78a468: Pull complete
60b13fe54b74: Pull complete
Digest: sha256:43cf17941edc9ab7e2985cd1f269bb477e349d13fc162e205fb59db4d92550a9
Status: Downloaded newer image for microsoft/azure-functions-node8:2.0
---> 653b8b504214
Step 2/3 : ENV AzureWebJobsScriptRoot=/home/site/wwwroot
---> Running in 6cf35d7ab4b7
Removing intermediate container 6cf35d7ab4b7
---> 2a8e10b180c3
Step 3/3 : COPY . /home/site/wwwroot
---> 6664f628614e
Successfully built 6664f628614e
Successfully tagged tsuyoshiushio/queuefunction-azurefunc:latest
Pushing function image to registry...
Running docker push tsuyoshiushio/queuefunction-azurefunc
The push refers to repository [docker.io/tsuyoshiushio/queuefunction-azurefunc]
f820929a76f9: Preparing
49b283ab3161: Preparing
3322eb9a475b: Preparing
9f652cc2dda0: Preparing
324d9a4273a9: Preparing
3cf68b57ae87: Preparing
cdb3f9544e4c: Preparing
3cf68b57ae87: Waiting
cdb3f9544e4c: Waiting
324d9a4273a9: Mounted from microsoft/azure-functions-node8
9f652cc2dda0: Mounted from microsoft/azure-functions-node8
49b283ab3161: Mounted from microsoft/azure-functions-node8
3cf68b57ae87: Mounted from microsoft/azure-functions-node8
f820929a76f9: Pushed
cdb3f9544e4c: Mounted from microsoft/azure-functions-node8
3322eb9a475b: Mounted from microsoft/azure-functions-node8
latest: digest: sha256:5d921a0a416a1a15b508a53eb35194325854dd19081b236468952fe7f9d14550 size: 1796
Deploying function to Kubernetes...
Deployment successful
Waiting for External IP...
Function deployed successfully!
Function IP: XXX.XXX.XXX.XXX

It seems work, however, when I see the logs, it shows some error. We need to require the configuration of the AzureWebJosbsStorage Connection Strings. However, we have no way to configure this. NOTE: the namespace is azure-functions

$ kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
azure-functions queuefunction-deployment-7b54d974c5-g27s7 1/1 Running 0 7m
$ kubectl logs queuefunction-deployment-7b54d974c5-g27s7 --namespace azure-functions
:
The following 1 functions are in error:
QueueTriggerJS: Microsoft.Azure.WebJobs.Host: Error indexing method 'Functions.QueueTriggerJS'. Microsoft.Azure.WebJobs.Host: Microsoft Azure WebJobs SDK 'Storage' connection string is missing or empty. The Microsoft Azure Storage account connection string can be set in the following ways:
1. Set the connection string named 'AzureWebJobsStorage' in the connectionStrings section of the .config file in the following format <add name="AzureWebJobsStorage" connectionString="DefaultEndpointsProtocol=http|https;AccountName=NAME;AccountKey=KEY" />, or
2. Set the environment variable named 'AzureWebJobsStorage', or
3. Set corresponding property of JobHostConfiguration.

When I export the yaml file of the deployment, there is no configuration of the Environment Variables.

$ kubectl get deployments --namespace azure-functions
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
queuefunction-deployment 1 1 1 1 1h
$ kubectl get deployments queuefunction-deployment -o yaml --namespace azure-functions > deployment.yaml

NOTE: If you want to know the whole configuration of the CLI, you can get all config via this command.

$ kubectl get all --export=true -o yaml --namespace azure-functions

Also, If you want to see the directory structure of the Docker container, use this command. You can login on the container.

kubectl exec queuefunction-deployment-7b54d974c5-g27
s7 --namespace azure-functions -i -i -- bash -il

Adding Environment Variables

Unfortunately, I can’t find the configuration option to set the Configuration. However, we can edit the deployment.yaml which I exported before. Edit like this. I just added the env: part which configure AzureWebJobsStorage as an environment variables.

:
spec:
containers:
- image: tsuyoshiushio/queuefunction-azurefunc
imagePullPolicy: Always
name: queuefunction-deployment
env:
- name: AzureWebJobsStorage
value: YOUR_STORAGE_ACCOUNT_CONNECTION_STRING_HERE
ports:
- containerPort: 80
protocol: TCP
:

Then Apply it .

$ kubectl apply -f deployment.yaml
deployment.extensions/queuefunction-deployment configured

It works.

$ kubectl get pods --namespace azure-functions
NAME READY STATUS RESTARTS AGE
queuefunction-deployment-7fc9fc98cd-sc97r 1/1 Running 0 22s
$ kubectl describe pod queuefunction-deployment-7fc9fc98cd-sc97r --namespace azure-functions
Name: queuefunction-deployment-7fc9fc98cd-sc97r
Namespace: azure-functions
Node: aks-nodepool1-14587345-0/10.240.0.4
Start Time: Sun, 12 Aug 2018 21:53:41 -0700
Labels: app=queuefunction-deployment
pod-template-hash=3975975478
Annotations: <none>
Status: Running
IP: 10.244.0.13
Controlled By: ReplicaSet/queuefunction-deployment-7fc9fc98cd
Containers:
queuefunction-deployment:
Container ID: docker://64b8e420325e4d7ce184171de0df5759ac188bd6289f8b57fbc5ff1542252047
Image: tsuyoshiushio/queuefunction-azurefunc
Image ID: docker-pullable://tsuyoshiushio/queuefunction-azurefunc@sha256:5d921a0a416a1a15b508a53eb35194325854dd19081b236468952fe7f9d14550
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Sun, 12 Aug 2018 21:53:45 -0700
Ready: True
Restart Count: 0
Requests:
cpu: 100m
memory: 128Mi
Environment:
AzureWebJobsStorage: YOUR_STORAGE_ACCOUNT_CONNECTION_STRING_HERE
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-p77kb (ro)
Conditions:
Type Status
Initialized True
Ready True
PodScheduled True
Volumes:
default-token-p77kb:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-p77kb
Optional: false
QoS Class: Burstable
Node-Selectors: <none>
Tolerations: azure.com/aci:NoSchedule
node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 55s default-scheduler Successfully assigned queuefunction-deployment-7fc9fc98cd-sc97r to aks-nodepool1-14587345-0
Normal SuccessfulMountVolume 54s kubelet, aks-nodepool1-14587345-0 MountVolume.SetUp succeeded for volume "default-token-p77kb"
Normal Pulling 54s kubelet, aks-nodepool1-14587345-0 pulling image "tsuyoshiushio/queuefunction-azurefunc"
Normal Pulled 52s kubelet, aks-nodepool1-14587345-0 Successfully pulled image "tsuyoshiushio/queuefunction-azurefunc"
Normal Created 51s kubelet, aks-nodepool1-14587345-0 Created container
Normal Started 51s kubelet, aks-nodepool1-14587345-0 Started container

Configuration is OK!

Unfortunately, Queue Trigger on Docker doesn’t seem to create a queue on the Storage Account let’s create by yourself. Then send a queue message.

Create/Send a queue

As you can see, It works! :)

$ kubectl logs queuefunction-deployment-7fc9fc98cd-sc9
7r --namespace azure-functions
Hosting environment: Production
Content root path: /
Now listening on: http://[::]:80
:
info: Host.Startup[0]
Job host started
info: Function.QueueTriggerJS[0]
=> System.Collections.Generic.Dictionary`2[System.String,System.Object]
Executing 'Functions.QueueTriggerJS' (Reason='New queue message detected on 'js-queue-items'.', Id=92fbec78-f083-413c-82a3-ba4d43c636b3)
info: Function.QueueTriggerJS.User[0]
=> System.Collections.Generic.Dictionary`2[System.String,System.Object]
JavaScript queue trigger function processed work item Hello World!
info: Function.QueueTriggerJS[0]
=> System.Collections.Generic.Dictionary`2[System.String,System.Object]
Executed 'Functions.QueueTriggerJS' (Succeeded, Id=92fbec78-f083-413c-82a3-ba4d43c636b3)
info: Host.General[0]
Host lock lease acquired by instance ID '000000000000000000000000F608D148'.

Conclusion

Deploy Azure Functions on the Kubernetes is fantastic feature. It helps people to run on the OnPrem secnario, Kubernetes Cluster scenario, and even other cloud providers with scale. My concern was, what about the triggers and bindings? However, it seems work as well! It doesn’t scale automatically now, however, it still useful.

Downside is, for the current version, I need to configure the environment variables manually. I create an issue as an future request for this fantastic new feature. :)

--

--

Tsuyoshi Ushio
Tsuyoshi Ushio

Written by Tsuyoshi Ushio

Senior Software Engineer — Microsoft

Responses (2)