This is Node.js 4.3.0 LTS for Rumprun. Use any of the thousands of npm packages or run your own Javascript modules on the Rumprun unikernel.
- David Halls, dahalls@gmail.com
- Github: @davedoesdev
The build process first applies the NetBSD pkgsrc patches for Node.js and then
applies patches from the patches directory.
There are a couple of changes worth noting:
-
Rumprun doesn't have
execinfo.horbacktraceso C stack traces for exceptions won't be printed. -
Rumprun doesn't support the
mmap(MAP_NORESERVE)...mmap(MAP_FIXED)combination for allocating code range memory so this has to be fixed when Node is launched. 16Mb is allocated for your code by default, which should be enough for small programs. If you need more, use the--code-range-sizeargument when you userumprunto launch Node (e.g.--code-range-size=64).
Run make. This will produce build-4.3.0/out/Release/node-default, which you
can then pass to rumprun-bake — for example:
rumprun-bake hw_generic build-4.3.0/out/Release/node-default.bin build-4.3.0/out/Release/node-defaultYou can then run Node using something like this:
rumprun kvm -M 256 -i build-4.3.0/out/Release/node-default.binThe files _third_party_main.js and rumpmain.js in this directory are bundled
into build-4.3.0/out/Release/node-default. _third_party_main.js runs when
you launch Node using rumprun.
If you give an argument to node-default.bin when running rumprun, then
_third_party_main.js treats it as a pathname and runs the script in that file.
If you don't give an argument, it runs rumpmain.js, which just displays a
message and exits.
rumpmain.js ships as a symbolic link to default.js. You can of course link
rumpmain.js to a different file. Please note this will change the name of the
output binary too, based on the name of the file you link to. For example,
if you link rumpmain.js to foo.js then make will produce
build-4.3.0/out/Release/node-foo.
Most applications require code in separate modules, and you have four options for running these.
The first option is to put your application's files into a filesystem (e.g.
using genisoimage) and attach it to rumprun using the -b option. You can
then give the path to the main file of your application as an argument to
rumprun, after node-default.bin.
This is the most flexible option but doesn't give you a single image to distribute.
For instance, assuming Express is checked out into the
examples/express-4.13.3 directory, then to run the "Hello World" example:
cd examples
(cd express-4.13.3; npm install --production)
genisoimage -l -r -o express-4.13.3.iso express-4.13.3
rumprun kvm -M 256 -I 'nic,vioif,-net user,hostfwd=tcp::3000-:3000' -W nic,inet,dhcp -i -b express-4.13.3.iso,/express ../build-4.3.0/out/Release/node-default.bin /express/examples/hello-world/index.jsOr use the supplied make target:
cd examples
make run_express_hello_worldPoint your browser to http://localhost:3000.
The second option is similar to the first except you create your application's
filesystem using Rumprun's cookfs tool. You then bake the filesystem image
into the Rumpkernel using rumprun-bake:
cd examples
(cd express-4.13.3; npm install --production)
x86_64-rumprun-netbsd-cookfs express-4.13.3.fs express-4.13.3
rumprun-bake -m "add express-4.13.3.fs" hw_generic ../build-4.3.0/out/Release/node-express-4.13.3.bin ../build-4.3.0/out/Release/node-default
rumprun kvm -M 256 -I 'nic,vioif,-net user,hostfwd=tcp::3000-:3000' -W nic,inet,dhcp -i ../build-4.3.0/out/Release/node-express-4.13.3.bin /express-4.13.3/examples/hello-world/index.jsThis gives you a single image with everything in. Bear in mind the files will end up uncompressed in the image, so if you have a large application then the image will be large and take longer to load.
The third option is to bundle your entire application into a single file,
and link rumpmain.js to it. You can do this using
webpack or browserify.
For instance, to run the same Express "Hello World" example:
cd examples
npm install webpack json-loader
(cd express-4.13.3; npm install --production)
./node_modules/.bin/webpack --target node --module-bind json ./express-4.13.3/examples/hello-world/index.js hello-world.js
ln -sf examples/hello-world.js ../rumpmain.js
make -C ..
rumprun-bake hw_generic ../build-4.3.0/out/Release/node-hello-world.bin ../build-4.3.0/out/Release/node-hello-world
rumprun kvm -M 256 -I 'nic,vioif,-net user,hostfwd=tcp::3000-:3000' -W nic,inet,dhcp -i ../build-4.3.0/out/Release/node-hello-world.binThis approach does produce a single image and is fine for applications which are compatible with Webpack or Browserify. However, larger applications can require quite a bit of tweaking.
I've implemented a fourth option which works better than Webpack/Browserify for larger applications. Actually, it's probably better for small applications too.
First you Zip up your application. Then you run zipload.sh, passing the Zip
file as standard input and the name of the script inside the Zip file to run
as a parameter. zipload.sh will write a completely self-contained script to
standard output, which you can then redirect to a file and use as the link
target for rumpmain.js.
For example:
cd examples
(cd express-4.13.3; npm install --production)
zip -r express-4.13.3.zip express-4.13.3
./zipload.sh /express-4.13.3/examples/hello-world/index.js < express-4.13.3.zip > hello-world.js
ln -sf examples/hello-world.js ../rumpmain.js
make -C ..
rumprun-bake hw_generic ../build-4.3.0/out/Release/node-hello-world.bin ../build-4.3.0/out/Release/node-hello-world
rumprun kvm -M 256 -I 'nic,vioif,-net user,hostfwd=tcp::3000-:3000' -W nic,inet,dhcp -i ../build-4.3.0/out/Release/node-hello-world.binBehind the scenes, Node's fs module is monkey-patched to load modules and
files from the Zip file. The Zip file is written into the script as a
base64-encoded string and accessed using
JSZip, which is also bundled into the script.
This option produces a smaller single image than the baked-in filesystem option but does incur an overhead in decompressing modules before they're loaded.
Some Node modules use Node.js native Addons. These are compiled from C++ into shared libraries, which Node loads at runtime.
Rumprun doesn't support dynamic loading of shared libraries so you'll see a stacktrace like this if one of your modules tries to load an Addon:
Error: Service unavailable
at Error (native)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Module.require (module.js:365:17)
at require (module.js:384:17)
The solution is to compile the Addon into the Node binary itself. Whilst this
isn't something you usually do, it's quite simple. You need to add the full
pathnames of the Addon's source and header files to build-4.3.0/node.gyp.
- Either do this by hand:
- Add the source files to
targets→sources - Add the header files to
targets→include_dirs - Run
make
- Add the source files to
- Or use
nad:npm install -g nadcd /path/to/addonnad configure --nodedir /path/to/rumprun-packages/nodejs/build-4.3.0nad inject- Run
NODE_PATH=/path/to/addon/node_modules make
The Addon is now compiled into the Node binary, which you can bake and run as
normal. Remember the Node binary will be build-4.3.0/out/Release/node-default
unless you've linked rumpmain.js to something other than default.js.
I've added an example of using an Addon in examples/ursa/test.js:
- In the
examplesdirectory, runmake ursa.iso. This installs theursamodule and adds it to a.isofile. - Next, you have to add
ursa's Addon tobuild-4.3.0/node.gyp.
- Either modify
build-4.3.0/node.gypby hand:- In
sources(undertargets), add'../examples/ursa/node_modules/ursa/src/ursaNative.cc' - In
include_dirs(undertargets), add'../examples/ursa/node_modules/ursa/node_modules/nan' - Run
make
- In
- Or run
make inject_ursain theexamplesdirectory. This usesnadto modifybuild-4.3.0/node.gypand then runsmakewithNODE_PATHset.
- Bake the Node binary (e.g.
rumprun-bake hw_generic build-4.3.0/out/Release/node-default.bin build-4.3.0/out/Release/node-default, depending on whatrumpmain.jsis linked to) - In the
examplesdirectory, runmake run_ursa. You should see a PEM-formatted public key displayed.
I've got the Ghost blogging platform running under Rumprun:
cd examples
make ghost_data.img
zip -r ghost-0.7.8.zip Ghost-0.7.8
./zipload.sh /Ghost-0.7.8/index.js < ghost-0.7.8.zip > ghost-0.7.8.js
ln -sf examples/ghost-0.7.8.js ../rumpmain.js
make inject_ghost
rumprun-bake hw_generic ../build-4.3.0/out/Release/node-ghost-0.7.8.bin ../build-4.3.0/out/Release/node-ghost-0.7.8
qemu-system-x86_64 -enable-kvm -m 1024 -kernel ../build-4.3.0/out/Release/node-ghost-0.7.8.bin -drive if=virtio,file=ghost_data.img -net nic,model=virtio -net user,hostfwd=tcp::2368-:2368 -append '{"net": {"if": "vioif0",, "type": "inet",, "method":"dhcp"},, "blk": {"source": "dev",, "path": "/dev/ld0a",, "fstype": "blk",, "mountpoint": "/ghost_data"},, "env": "GHOST_CONFIG=/ghost_data/config.js",, "cmdline": "node --code-range-size=64"}'Point your browser to http://localhost:2368.
Ghost's configuration and persistent data is kept in ghost_data.img.
Here are instructions for running a couple of Node.js multiplayer games on Rumprun.
cd examples
git clone --depth=1 https://github.com/amirrajan/nodekick.git
(cd nodekick; npm install)
genisoimage -l -r -o nodekick.iso nodekick
qemu-system-x86_64 -enable-kvm -m 256 -kernel ../build-4.3.0/out/Release/node-default.bin -drive if=virtio,file=nodekick.iso -net nic,model=virtio -net user,hostfwd=tcp::3000-:3000 -append '{"net": {"if": "vioif0",, "type": "inet",, "method":"dhcp"},, "blk": {"source": "dev",, "path": "/dev/ld0a",, "fstype": "blk",, "mountpoint": "/nodekick"},, "cmdline": "/nodekick/node /nodekick/server.js"}'Point your browser to http://localhost:3000.
cd examples
git clone --depth=1 https://github.com/thebinarypenguin/socket.io-chess.git
(cd socket.io-chess; npm install)
genisoimage -l -r -o socket.io-chess.iso socket.io-chess
qemu-system-x86_64 -enable-kvm -m 512 -kernel ../build-4.3.0/out/Release/node-default.bin -drive if=virtio,file=socket.io-chess.iso -net nic,model=virtio -net user,hostfwd=tcp::3000-:3000 -append '{"net": {"if": "vioif0",, "type": "inet",, "method":"dhcp"},, "blk": {"source": "dev",, "path": "/dev/ld0a",, "fstype": "blk",, "mountpoint": "/chess"},, "cmdline": "node /chess/server.js"}'Point your browser to http://localhost:3000.
Node 5.6.0 is known to build successfully:
make NODE_VERSION=5.6.0 PKGSRC=nodejs- If you use
nadto inject more than one Addon intobuild-4.3.0/node.gyp, you might run into problems. You'll end up with conflicting definitions formodule_root_dir, so if both the Addons rely on its value then one will fail to compile. - It's best to use Node 4.3.0 (and associated
npmversion) on your build system when installing your application's dependencies.