{{announcement.body}}
{{announcement.title}}

Running the Nanos Unikernel Inside Firecracker

DZone 's Guide to

Running the Nanos Unikernel Inside Firecracker

In this article, learn how to run the Nanos Unikernel inside Firecracker.

· IoT Zone ·
Free Resource

A lot of people seem to be under the impression that Firecracker is a competing technology against unikernels. It actually isn't. It's more of an alternative to existing machine monitors such as qemu and is actually complementary if you need the quick boot times. As for isolation levels you are going to have about the same isolation as you would get with KVM - it uses the same facilities underneath.

OSv was the first unikernel to have support for Firecracker but now Nanos has support as well.

There is another very common misconception that firecracker somehow makes your application go faster at runtime. This is, at least today, definitely not the case as discussed in this issue. If anything you will notice your application slows down considerably. What is faster is the boot time. The boot time, if you are concerned about such a thing, does indeed become much faster, although again, take this with a few caveats. If you are sticking something like the JVM or rails inside firecracker your boot time arguments get effectively thrown away because they are already incredibly slow to boot. I suppose comparing with a linux instance you would shave off some but comparing with a unikernel not so much. This isn't necessarily a bad thing.

As indicated in some of the linked mailing list comments there seem to be plans on fixing the runtime performance, however, you should be aware of what you are getting into.

Ok, enough disclaimers - let's get with the code.

Install Firecracker and Nanos

Before you try anything out be aware that none of this will work without having access to hardware acceleration. So if you are in the cloud that means nested virtualization on GCloud or metal instances on AWS otherwise stick to bare metal.

A quick way to check is to grep your cpuinfo:

Shell
 




xxxxxxxxxx
1


1
grep -woE 'svm|vmx' /proc/cpuinfo | uniq



There's a handful of other cpu flags that aren't documented that you'll need but suffice to say just make sure your server was built in the past 5-6 years and you'll probably be good. You might be asking how OPS and Nanos can deploy to the cloud without relying on special instance types. The reason is OPS deploys raw unikernels straight to the cloud. Not having an underlying linux means you get access to the raw virtualization inherently present, not to mention you won't have to deal with orchestration.

Ok, first things first, let's install firecracker:

Shell
 




xxxxxxxxxx
1


1
latest=$(basename $(curl -fsSLI -o /dev/null -w  %{url_effective} https://github.com/firecracker-microvm/firecracker/releases/latest))
2
curl -LOJ https://github.com/firecracker-microvm/firecracker/releases/download/${latest}/firecracker-${latest}-$(uname -m)
3
mv firecracker-${latest}-$(uname -m) firecracker
4
chmod +x firecracker



Now let's install ops:

Shell
 




xxxxxxxxxx
1


 
1
curl https://ops.city/get.sh -sSfL | sh



For the purposes of this article we'll only be using ops to build our unikernel - not run it. We'll leave the running to firecracker.

If this is your first time using unikernels or OPS please take the time to go through some hello world tutorials such as https://medium.com/javascript-in-plain-english/build-your-first-unikernel-with-express-js-and-nanovms-21d64ff7231c or https://codeforgeek.com/build-and-run-your-first-javascript-unikernel/ or https://dzone.com/articles/easily-build-and-run-unikernels-with-ops . They go more in depth on getting your first hello world off the ground and then you can come back to this article which is a bit more advanced.

If you wish to run the unikernel in firecracker you can use this vm_config.json template below.

As you will note I've pointed the kernel_image_path to the stage3 from Nanos found in ~/.ops/0.1.26/stage3.img and I've pointed the image to whatever image you've built with ops. In this case it is located in ~/.ops/images/my_img.img. Other than that everything is fairly vanilla.

JSON
 




x
26


1
{
2
  "boot-source": {
3
    "kernel_image_path": "/Users/bob/.ops/0.1.26/stage3.img",
4
    "boot_args": "console=ttyS0 reboot=k panic=1 pci=off"
5
  },
6
  "drives": [
7
    {
8
      "drive_id": "rootfs",
9
      "path_on_host": "/Users/bob/.ops/images/my_img.img",
10
      "is_root_device": true,
11
      "is_read_only": false
12
    }
13
  ],
14
  "network-interfaces": [
15
    {
16
      "iface_id": "eth0",
17
      "guest_mac": "AA:FC:00:00:00:01",
18
      "host_dev_name": "tap0"
19
    }
20
  ],
21
  "machine-config": {
22
    "vcpu_count": 1,
23
    "mem_size_mib": 1024,
24
    "ht_enabled": false
25
  }
26
}



Next you'll want to install a DHCP server - cause without it you really can't do anything. Keep in mind the only way to talk to a unikernel is over the network and there is no way to login to these. Also, using firecracker kind of implies that you'll probably have multi-tenants to begin with.

Shell
 




xxxxxxxxxx
1


 
1
sudo apt-get install isc-dhcp-server



You can setup the dhcp server with this config like so:

(Found in /etc/dhcp/dhcpd.conf):

Shell
 




xxxxxxxxxx
1
14


1
option domain-name "example.org";
2
option domain-name-servers ns1.example.org, ns2.example.org;
3
 
          
4
default-lease-time 600;
5
max-lease-time 7200;
6
 
          
7
ddns-update-style none;
8
 
          
9
INTERFACES="tap0";
10
 
          
11
subnet 10.0.2.0 netmask 255.255.255.0 {
12
    option routers 10.0.2.1;
13
    range 10.0.2.10 10.0.2.255;
14
}



Create a tap device for your dhcp server to listen in on:

Shell
 




xxxxxxxxxx
1


 
1
sudo ip tuntap add dev tap0 mode tap
2
sudo ip addr add 10.0.2.1/24 dev tap0
3
sudo ip link set tap0 up



Run it against your tap device. You'll want this running before running firecracker.

Shell
 




xxxxxxxxxx
1


1
dhcpd -f -d tap0



You should see some ARP requests fly by once firecracker boots:

Shell
 




x


 
1
bob@box:/home/bob~ dhcpd -f -d tap0
2
Internet Systems Consortium DHCP Server 4.3.5
3
Copyright 2004-2016 Internet Systems Consortium.
4
All rights reserved.
5
For info, please visit https://www.isc.org/software/dhcp/
6
Config file: /etc/dhcp/dhcpd.conf
7
Database file: /var/lib/dhcp/dhcpd.leases
8
PID file: /var/run/dhcpd.pid
9
lease 10.0.2.0: no subnet.
10
Wrote 0 leases to leases file.
11
Listening on LPF/tap0/96:ea:ca:e0:76:63/10.0.2.0/24
12
Sending on   LPF/tap0/96:ea:ca:e0:76:63/10.0.2.0/24
13
Sending on   Socket/fallback/fallback-net
14
Server starting service.
15
DHCPDISCOVER from aa:fc:00:00:00:01 via tap0
16
DHCPOFFER on 10.0.2.10 to aa:fc:00:00:00:01 via tap0
17
DHCPREQUEST for 10.0.2.10 (10.0.2.1) from aa:fc:00:00:00:01 via tap0
18
DHCPACK on 10.0.2.10 to aa:fc:00:00:00:01 (uniboot) via tap0
19
DHCPREQUEST for 10.0.2.10 from aa:fc:00:00:00:01 (uniboot) via tap0
20
DHCPACK on 10.0.2.10 to aa:fc:00:00:00:01 (uniboot) via tap0
21
DHCPREQUEST for 10.0.2.10 from aa:fc:00:00:00:01 (uniboot) via tap0
22
DHCPACK on 10.0.2.10 to aa:fc:00:00:00:01 (uniboot) via tap0
23
DHCPREQUEST for 10.0.2.10 from aa:fc:00:00:00:01 (uniboot) via tap0
24
DHCPACK on 10.0.2.10 to aa:fc:00:00:00:01 (uniboot) via tap0



Once you boot your unikernel you should also see it grab an ip:

Shell
 




xxxxxxxxxx
1


 
1
Server started on port 8080
2
assigned: 10.0.2.10
3
assigned: 0.0.0.0



I've found it easier to play with firecracker by breaking up the one large default vm_config.json into multiple commands each with different configs. I've also set up logging cause frankly it's a pain to understand what is going on without it.

Boot.sh:

This sets up the entry point for firecracker. Each socket needs to be unique so once you are done with it don't forget to remove it if you wish to re-use it later on. For this initial boot we simply point it at stage3 found in the latest ops release we have:

Shell
 




xxxxxxxxxx
1


1
#!/bin/sh
2
 
          
3
curl --unix-socket /tmp/firecracker.socket -i \
4
-X PUT 'http://localhost/boot-source' \
5
-H 'Accept: application/json' \
6
-H 'Content-Type: application/json' \
7
-d '{
8
"kernel_image_path": "/home/bob/.ops/0.1.26/stage3.img",
9
"boot_args": "console=ttyS0 reboot=k panic=1 pci=off" }'



Drives.sh:

This one points firecracker at our root fs that gets loaded. This is the actual image that you are building with ops. We treat this differently than ops does as we bypass a bunch of booting setup that we'd normally have to do using something like KVM.

Shell
 




xxxxxxxxxx
1
14


1
#!/bin/sh
2
 
          
3
curl --unix-socket /tmp/firecracker.socket -i \
4
-X PUT 'http://localhost/drives/rootfs' \
5
-H 'Accept: application/json' \
6
-H 'Content-Type: application/json' \
7
-d '{
8
      "drive_id": "rootfs",
9
      "path_on_host": "/home/bob/.ops/0.1.26/images/my_img.img",
10
      "is_root_device": true,
11
      "is_read_only": false
12
 
          
13
}'
13
}'



Machine.sh:

This sets the machine parameters for our firecracker instance. Nothing special here.

Shell
 




xxxxxxxxxx
1
11


 
1
#!/bin/sh
2
 
          
3
 curl --unix-socket /tmp/firecracker.socket -i \
4
-X PUT 'http://localhost/machine-config' \
5
-H 'Accept: application/json' \
6
-H 'Content-Type: application/json' \
7
-d '{
8
    "vcpu_count": 1,
9
    "mem_size_mib": 1024,
10
    "ht_enabled": false
11
}'



Start.sh:

Next we start the instance:

Shell
 




xxxxxxxxxx
1


 
1
#!/bin/sh
2
 
          
3
curl --unix-socket /tmp/firecracker.socket -i \
4
-X PUT 'http://localhost/actions' \
5
-H 'Accept: application/json' \
6
-H 'Content-Type: application/json' \
7
-d '{
8
"action_type": "InstanceStart"
9
}'



Logs.sh:

Then we'll create 2 pipes and attach the logs to them. Note: You most definitely should do this. I'm not quite sure why this is so obtuse or why the documentation for this was hidden inside of a github ticket. The logs are invaluable to understand what is going on in firecracker when things "just don't work".

Shell
 




xxxxxxxxxx
1
11


 
1
#!/bin/sh
2
 
          
3
mkfifo log.fifo
4
mkfifo metrics.fifo
5
 
          
6
curl --unix-socket /tmp/firecracker.socket -i \
7
-X PUT 'http://localhost/logger' \
8
-H "accept: application/json" \
9
-H "Content-Type: application/json" \
10
-d '{ "log_fifo": "log.fifo", "metrics_fifo": "metrics.fifo", "level":
11
"Info", "show_level": true, "show_log_origin": true }'



Read_fifo.sh:

To read your logs you'll need this little script:

Shell
 




xxxxxxxxxx
1
13


 
1
#!/bin/bash
2
 
          
3
while true
4
do
5
    if read line <$1; then
6
        if [[ "$line" == 'quit' ]]; then
7
            break
8
        fi
9
        echo $line
10
    fi
11
done
12
 
          
13
echo "Reader exiting"



Then to actually read the logs:

Shell
 




xxxxxxxxxx
1


1
./read_fifo.sh log.fifo



If you've gotten this far then congrats! You can now run Nanos unikernels inside of firecracker.
Topics:
firecracker, iot, serverless, unikernel

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}