The Fn project is an open-source container-native serverless platform that you can run anywhere -- any cloud or on-premise. It's easy to use, supports every programming language, and is extensible and performant.
These are the lines you are greeted with if you go to the fn projects website. So what does all this mean? Well, it basically means that another *AAS product has reached us, this time its FAAS, or Function as a service. This concept is not new, and several of the big *AAS providers have their own implementations running. AWS has it's Lambda, Google has Google Cloud Functions and Azure has Azure Functions. The fn project will be the serverless solution Oracle will offer to it's cloud customers, with some modifications based on it's own cloud infrastructure.
The common ground between these platforms are based on the serverless manifesto
- Function are the unit of deployment and scaling.
- No machines, VMs, or containers visible in the programming model.
- Permanent storage lives elsewhere.
- Scales per request; Users cannot over- or under-provision capacity.
- Never pay for idle (no cold servers/containers or their costs).
- Implicitly fault-tolerant because functions can run anywhere.
- BYOC - Bring Your Own Code.
- Metrics and logging are a universal right.
So what does the Fn project bring to the mix?
- First of all it is open source (sponsored by Oracle) which gives you all the open source goodies.
- It's cloud agnostic - no need to run at a specific cloud provider, or even in the cloud at all
- Docker based
- Very easy to get started (as you will see)
The first thing you need to get going is docker (version 17.10.0-ce or later). I run Windows on my laptop, but fired up an Ubuntu 16 VirtualBox image to run it. There is a quick guide on how to install docker on Ubuntu on the docker webpage which I used, and got it quickly up and running. To ensure that docker is correctly installed and that the daemon is running, run the following
sudo docker run hello-world
and you should see something like this
If you don't want to use sudo every time you run the docker commands, add your personal user to the docker group. I ran the below command, and afterwards I could run docker as my regular user.
sudo usermod -aG docker functiontester docker run hello-world
The next step is to get the CLI tool. This is strictly speaking not necessary, but it will make your life easier when we go forward. To use the automatic installer, run the following to download it
sudo curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
And that is actually all you need...
So now that you have all the needed components, it's time to fire up the stack
This will download the needed docker images (if not available on your machine) and give you an output similar to this
As you might see in the highlighted text, it has now fired up an image that listens to port :8080 locally. If you already have something running on that port you will get conflicts. The container can also be checked with standard docker commands.
functiontester@ubuntu:/media/sf_function/hello$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4c3484b3e59b fnproject/fnserver "./fnserver" 57 seconds ago Up 55 seconds 2375/tcp, 0.0.0.0:8080->8080/tcp fnserver
And if you go to the port in a webbrowser, you will see something like this if all is OK.
So now you are running your very own serverless platform!
Your first function (Java)
So your up and running, but it doesn't do much yet. So we need some code to run on our new platform. Again we will use the CLI tool we installed earlier, as this has commands for creating skeleton projects for different runtimes. So, for java we can do the following
functiontester@ubuntu:~/testfunction$ fn init --runtime java hello Creating function at: /hello Runtime: java Function boilerplate generated. func.yaml created.
This command will create a subdir called hello (or whatever you gave as the last parameter to the fn init command). Inside that directory you will find the following if you open the folder as a project in IntelliJ.
So what are you looking at? Well, from a Java developers perspective it's looking quite like a standard maven project, with a pom.xml file and a src folder for you code and tests. The only out of place part, is the func.yaml file, so let's have a quick look at it.
name: hello version: 0.0.1 runtime: java cmd: com.example.fn.HelloFunction::handleRequest build_image: fnproject/fn-java-fdk-build:jdk9-1.0.59 run_image: fnproject/fn-java-fdk:jdk9-1.0.59 format: http
So the first couple of values are quite straight forward, we have the name, version and runtime configuration of the project.
Then we have the cmd value is is what tells the runtime what code to call. So in the automatically generated code it's com.example.fn.HelloFunction:: handleRequest as you also can see in the image above from IntelliJ. So when you rename the package, class and method, remember to update this line in func.yaml.
The last lines are information about the build and run containers to use, and what format the input to the container is.
If Java isn't your cup of tea, the CLI tool supports other runtimes as well. At the time of writing, these runtimes are listed
dotnet, go, java8, java9, java, lambda-nodejs4.3, lambda-node-4, node, php, python, python3.6, ruby, rust and kotlin
The fn init command also has a lot of options that can be specified, check them out to tune the generated output from the command.
Build and run the code
Now that we have some code, lets run it and see it in action. If you just want to run your code, you can run the following command, or the next one to pass an argument to the function
functiontester@ubuntu:~/testfunction/hello$ fn build functiontester@ubuntu:~/testfunction/hello$ fn run Building image hello:0.0.1 . Hello, world! functiontester@ubuntu:~/testfunction/hello$ echo -n "Universe" | fn run Building image hello:0.0.1 . Hello, Universe!
As you might have guessed, this only does a onetime invocation and shuts down the container. Once we are happy with our function, we would like to deploy it so that it is callable by others. So in order to deploy to our local serverless platform we can do the following.
functiontester@ubuntu:~/testfunction/hello$ fn deploy --app myapp --local Deploying hello to app: myapp at path: /hello Bumped to version 0.0.2 Building image hello:0.0.2 . Updating route /hello using image hello:0.0.2...
This time no output from the function is shown, and instead the function has been deployed. In our deploy command we gave an app argument. This can be used if you have multiple functions that you want to group together.
But where is our app deployed? Well it is now running and available under the following URL, http://localhost:8080/r/myapp/hello As you can see, the value we gave to the app argument is a part of the url, as well as the project name itself. So now go can go there with curl or your browser or any tool that can access an http resource. If you go there with a browser it will look something like this.
So now you have a running setup with you very own function deployed and available, and you can go ahead and modify the code and play with it's capabilities. It is very easy to get started and get things up and running locally, so you shouldn't have any difficulty with following this guide.
Some of the runtimes have FDK's available that you can use to utilize different parts of the platform. One cool feature is the ability to use different types of data bindings, which will make it easier to use POJO's as input and output of your function. You can also have asynchronous invocations of your functions, and there is a separate project to do orchestration between functions called Fn Flow. My advice is to look around the Fn projects github repo to see what is available for you runtime and other tips and tricks.
I hope to look into setting up and deploying the function project to an Kubernetes cluster some time soon, so look out for a post about that.