It is time to go serverless, at least for me. As a developer I want to write application code and get it running “somewhere” as quickly as possible.
I don’t want to switch hats during development just to get my code into some cloud by creating new Docker images and changing Kubernetes configuration.
Yes, of course I could set up some CI system, but sometimes that’s a bit too much for a local dev environment.
The serverless paradigm goes in the right direction, in my opinion. Time to get a feel for it.
I have chosen Kubeless as a framework because I want to play on my local k3s cluster.
From their website, they describe it as follows:
Kubeless is a Kubernetes-native serverless framework that lets you deploy small bits of code (functions) without having to worry about the underlying infrastructure. It is designed to be deployed on top of a Kubernetes cluster and take advantage of all the great Kubernetes primitives. If you are looking for an open source serverless solution that clones what you can find on AWS Lambda, Azure Functions, and Google Cloud Functions, Kubeless is for you!
Therefore, this post is a tutorial about the Kubeless framework to start exploring what it means to deploy serverless functions.
Enough said, let’s dive right in.
One second: before we start I have to mention that my setup is:
- a MacBook Pro as local workstation
- a two-node k3s cluster running on Ubuntu
If your setup is very different, please check the Kubeless website (links at the end of this post).
Installation
First, we install everything with the following commands:
$ export RELEASE=$(curl -s https://api.github.com/repos/kubeless/kubeless/releases/latest | grep tag_name | cut -d '"' -f 4)
$ kubectl create ns kubeless
$ kubectl create -f https://github.com/kubeless/kubeless/releases/download/$RELEASE/kubeless-$RELEASE.yaml
As with every good tool, Kubeless also comes with its own CLI to make things a little easier:
$ export OS=$(uname -s | tr '[:upper:]' '[:lower:]')
$ curl -OL https://github.com/kubeless/kubeless/releases/download/$RELEASE/kubeless_$OS-amd64.zip && unzip kubeless_$OS-amd64.zip && sudo mv bundles/kubeless_$OS-amd64/kubeless /usr/local/bin/
Check if it was installed properly with:
$ kubeless version
Let’s continue by deploying some functionality.
Deploying a Function
Now we are going to deploy those “small bits of code” mentioned earlier— the so-called “functions”.
Create the code in a file called echo.py:
def echo(event, context):
print(event)
return event['data']
Then deploy it to the cluster with:
$ kubeless function deploy echo --runtime python3.8 --from-file echo.py --handler echo.echo
That’s all!
Do a quick check that the function was created properly:
$ kubeless function ls echo
NAME NAMESPACE HANDLER RUNTIME DEPENDENCIES STATUS
echo default echo.echo python3.8 1/1 READY
Especially check the last column: the STATUS should be READY.
If it is not working, please check the “Troubleshooting” section.
Test
Finally, test the function by calling it via kubeless:
$ kubeless function call echo --data 'Hello World!'
Hello World!
Success!
Cleanup
If you want to remove everything again, here are the commands to clean up what was installed:
$ kubeless function delete echo
$ kubectl delete -f https://github.com/kubeless/kubeless/releases/download/$RELEASE/kubeless-$RELEASE.yaml
$ kubectl delete ns kubeless
Using Kubeless without kubeless
What a weird title, but it makes sense after reading this section.
We are still on Kubernetes and everything works without the additional CLI. A Kubeless function is, in Kubernetes terms, a custom resource (CRD). Therefore, Kubeless functions become Kubernetes objects and we can use kubectl as usual.
Let’s try it out.
First, delete the previous function if you haven’t already (see “Cleanup” ).
Put the following into a file called echo.yaml:
---
apiVersion: kubeless.io/v1beta1
kind: Function
metadata:
name: echo
spec:
deployment:
metadata:
annotations:
"annotation-to-deploy": "value"
spec:
replicas: 2
template:
metadata:
annotations:
"annotation-to-pod": "value"
deps: ""
function: |
def echo(event, context):
print(event)
return event['data']
function-content-type: text
handler: echo.echo
runtime: python3.8
service:
selector:
function: echo
ports:
- name: http-function-port
port: 8080
protocol: TCP
targetPort: 8080
nodePort: 30333
type: NodePort
Then install it onto the cluster:
$ kubectl apply -f echo.yaml
Check if it has been deployed:
$ kubectl get svc echo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
echo ClusterIP WW.XX.YYY.Z <none> 8080/TCP 16m
All fine!
Now, to test the service, forward the port of the cluster node to your local machine:
$ kubectl port-forward service/echo 8080:8080
Then, in another terminal, curl into the cluster to call the function:
$ curl -L --data '{"echo":"Hello World!"}' --header "Content-Type:application/json" localhost:8080/api/v1/namespaces/default/services/echo:http-function-part/proxy/
Et voilà— there is the output. If not, continue with the next section.
Troubleshooting
It ‘s likely something won’t work as expected the first time through this tutorial. I ran into a problem after deploying my first function: the service was not reachable and I couldn’t call the function.
When checking your function, the status might be “NOT READY” instead of “READY”, and the associated pod might be stuck in “CrashLoopBackOff”.
The usual approach is to check the logs of the Kubernetes pod.
First get the pod name:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
echo-xzy 1/1 Running 0 4m35s
Then get the logs:
$ kubectl logs echo-xzy
Only then was I able to find out that I had chosen a Python version that was no longer available; I had to switch to a higher version (>= 3.x). I also had to change my code syntax because this is valid in Python 2:
print "Hello World!"
but in Python 3 it must be:
print("Hello World!")
Just for the giggles.
Conclusion
Hopefully your first trip into serverless was successful. I enjoyed it and see many possibilities to streamline my development workflow.
Now explore more existing functions and write some on your own. I have put some links below to keep you going.
Feel free to buy me a coffee if you liked this post..