DEV Community

loading...

Stateful Serverless with Unikernels

eyberg profile image Ian Eyberg ・6 min read

One of the big problems with cloud native computing is the reluctance to use what is called a stateful service. Think everything from databases to even just logging a web application and actually keeping those logs. To some of us it sounds a bit crazy to even distinguish between 'stateful' and 'stateless'. Unless you are on some functional programming kick you are going to deal with state.

If you do choose to use traditional cloud native stateful applications you get to learn about everything from stateful sets to persistent volumes to everything else. For your average dev this is just too much craziness.

It gets really crazy when you start to think that most cloud native and serverless frameworks are already running on top of public cloud environments like AWS or Google Cloud. If you haven't been told before "the cloud" is simply an API to virtualization -- and there's a non-trivial amount of software that makes that work. AWS and Google Cloud have gone out of their way to provide a truly cloud native experience by writing the underlying infrastructure to do the networking and the storage and everything else. To be clear - I'm not talking about ECS and GKE here. I'm talking about when you go and boot a VM on one of those respective systems think about what all is happening underneath.

It almost makes you wonder why development teams would choose to re-write all of that knowing full well that they are going to do a crappier job than the infrastructure teams at Google/Amazon. To boot, most of the cloud native ecosystem takes serious performance hits from having to replicate networking and storage layers on top of the existing infrastructure and they suffer from serious security issues like cryptojacking.

I was re-watching this talk from Andrew Tanenbaum recently and he asks a very poignant question:

"Why are our operating systems not like our tvs? You click the tv on and you click it off and it just works."

Good question Andrew!

The cloud native ecosystem makes some developers lives easier because they don't need to mess with configuring or deploying the base operating system but isn't there something that is kinda like a container but also has persistence and security baked in? Ever wish you could have a true cloud native serverless option but with actual real persistence?

Well you can. Today we'll show you how to deploy stateful serverless with unikernels.

Stateful Serverless

For this tutorial you'll want to go grab OPS. If you'd like to build from source go check out the github repo. We'll be deploying to Google Cloud today but you can also sub it out for AWS. On Google Cloud you need a bucket to store your images in and a service key (that I stick in ~/gcloud.json) and then create this config.json:

{
    "CloudConfig" :{
        "ProjectID" :"my-project",
        "Zone": "us-west1-b",
        "BucketName":"my-bucket"
    }
}

Now let's look at some sample code. We have a very simple node.js webserver that does two things. It either appends to a file through the '/savelog' url or it shows what's in the log via the '/getlog' url.

Also - I'm not a practicing node developer so please excuse the non-idiomatic craziness of the code. Apologies in advance.

I left the port configurable here so you can try it out on a different port locally before deploying it out on gcloud.

const fs = require('fs');
var http = require('http');

var logfile = 'bob.log';
var port = 80;

http.createServer(function (req, res) {

    if (req.url == '/getlog') {

      if(!fs.existsSync(logfile)) {
        console.log("File not found");
      } else {
        var contents = fs.readFileSync(logfile).toString();
        console.log(contents);
      }

      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end(contents + '\n');

    } else if (req.url == "/savelog") {
      fs.appendFileSync(logfile, 'test' + '\n');

      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end('logged\n');

    } else {

      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end('Hello World\n');

    }

}).listen(port, "0.0.0.0");

console.log('Server running at http://127.0.0.1:' + port);

We can build our image simply by stating we want the node version 12.13.0 package and pointing it at our entryfile bob.js.

export GOOGLE_APPLICATION_CREDENTIALS=~/gcloud.json

ops image create -c config.json -p node_v12.13.0 -a bob.js

If we run that we can build our image - it's fairly quick cause all it's doing is copying the disk image up into our bucket:

➜  stateful-serverless  ./build.sh
[node bob.js]
bucket found: my-bucket
Image creation started. Monitoring operation operation-1573675163382-5973fcd1af403-c6abea13-cf898ff6.
...............
Operation operation-1573675163382-5973fcd1af403-c6abea13-cf898ff6 completed successfully.
Image creation succeeded node-image.
gcp image 'node-image' created...

One that is done we can create an instance like so:

 ops instance create -z us-west1-b -i node-image
ProjectId not provided in config.CloudConfig. Using my-project from default credentials.Instance creation started using image projects/my-project/global/images/node-image. Monitoring operation operation-1573675228678-5973fd0ff4976-a6b7132d-9aa57c18.
.....
Operation operation-1573675228678-5973fd0ff4976-a6b7132d-9aa57c18 completed successfully.
Instance creation succeeded node-image-1573675228.

Then we can hit it up a few times by querying '/savelog'. After that we can view '/getlog' to see that we saved our data.

eyberg@box:~/stateful-services$ curl -XGET http://34.83.134.230/savelog
logged
eyberg@box:~/stateful-services$ curl -XGET http://34.83.134.230/savelog
logged
eyberg@box:~/stateful-services$ curl -XGET http://34.83.134.230/savelog
logged
eyberg@box:~/stateful-services$ curl -XGET http://34.83.134.230/getlog
test
test
test

Now let's stop the instance - not terminate it but stop it. This is where traditional cloud native solutions and other serverless based options tend to have problems. They view the running image as ephemeral and not ever coming back. However for true cloud native applications like unikernels this is simply not the case. All we are doing is telling Google to merely stop the instance not kill it. This is good if you want say a cough cloud native database cough.

We can go ahead and stop the instance and then restart it.

eyberg@box:~/stateful-services$ ops instance stop -p my-project -z us-west1-b node-image-1573676028
Instance stopping started. Monitoring operation operation-1573676572018-5974021110877-37b0f038-95aa93d0.
.............................................................
eyberg@box:~/stateful-services$ ops instance list -p my-project -z us-west1-b
+----------------------------------+------------+-------------------------------+-------------+---------------+
|               NAME               |   STATUS   |            CREATED            | PRIVATE IPS |  PUBLIC IPS   |
+----------------------------------+------------+-------------------------------+-------------+---------------+
| node-image-1573676028            | TERMINATED | 2019-11-13T12:13:50.106-08:00 | 10.240.0.50 |               |
+----------------------------------+------------+-------------------------------+-------------+---------------+
| releases-150719-14-07-1563179925 | TERMINATED | 2019-07-15T01:38:47.354-07:00 | 10.240.0.9  | 35.233.232.59 |
+----------------------------------+------------+-------------------------------+-------------+---------------+
eyberg@box:~/stateful-services$ ops instance start -p my-project -z us-west1-b node-image-1573676028
Instance started. Monitoring operation operation-1573676869968-5974032d363e0-d0a18a2c-0d56186d.
...
Operation operation-1573676869968-5974032d363e0-d0a18a2c-0d56186d completed successfully.
Instance started node-image-1573676028.
eyberg@box:~/stateful-services$ ops instance list -p my-project -z us-west1-b
+----------------------------------+------------+-------------------------------+-------------+---------------+
|               NAME               |   STATUS   |            CREATED            | PRIVATE IPS |  PUBLIC IPS   |
+----------------------------------+------------+-------------------------------+-------------+---------------+
| node-image-1573676028            | RUNNING    | 2019-11-13T12:13:50.106-08:00 | 10.240.0.50 | 34.82.108.88  |
+----------------------------------+------------+-------------------------------+-------------+---------------+
| releases-150719-14-07-1563179925 | TERMINATED | 2019-07-15T01:38:47.354-07:00 | 10.240.0.9  | 35.233.232.59 |
+----------------------------------+------------+-------------------------------+-------------+---------------+
eyberg@box:~/stateful-services$ curl -XGET http://34.82.108.88/getlog
test
test
test
test
test

Hooray - your data is still there. This is precisely what you would see in a traditional linux environment yet now you get the benefit of the container/serverless like atmosphere as well.

You'll notice that the ip might change as we don't have an elastic/static ip attached to it - that's ok and expected. If you were actually deploying to prod you might assign one or stick this behind a load balancer instead.

If you're new to this style of infrastructure it's worth pointing out a few things. These instances are not linux instances - as in they don't even have a linux kernel inside - at all. They don't have the concept of usernames/passwords. You can't login to them and they can not run more than one program per instance. This is all by design to make them faster, more secure and easier to manage then existing options.

This was just a quick and dirty example to show you the basic concepts behind the idea of stateful serverless. No servers to deal with but you can keep your state cake and eat it too.

What will you deploy?

Discussion (0)

pic
Editor guide