How to install a Node.js app in a LXD container

Update #1: Added working screenshot and some instructions.

Update #2: Added instructions on how to get the app to autostart through systemd.

Installing a Node.js app on your desktop Linux computer is a messy affair, since you need to add a new repository and install lots of additional packages.

The alternative to messing up your desktop Linux, is to create a new LXD (LexDee) container, and install into that. Once you are done with the app, you can simply delete the container and that’s it. No trace whatsoever and you keep your clean Linux installation.

First, see how to setup LXD on your Ubuntu desktop. There are extra resources if you want to install LXD on other distributions.

Second, for this post we are installing chalktalk, a Node.js app that turns your browser into an interactive blackboard. Nothing particular about Chalktalk, it just appeared on HN and it looks interesting.

Here is what we are going to see today,

  1. Create a LXD container
  2. Install Node.js in the LXD container
  3. Install Chalktalk
  4. Testing Chalktalk

Creating a LXD container

Let’s create a new LXD container with Ubuntu 16.04 (Xenial, therefore ubuntu:x), called mynodejs. Feel free to use something more descriptive, like chalktalk.

$ lxc launch ubuntu:x mynodejs
Creating mynodejs
Starting mynodejs
$ lxc list -c ns4
+----------+---------+----------------------+
| NAME     | STATE   | IPV4                 |
+----------+---------+----------------------+
| mynodejs | RUNNING | 10.52.252.246 (eth0) |
+---------------+----+----------------------+

Note down the IP address of the container. We need it when we test Chalktalk at the end of this howto.

Then, we get  a shell into the LXD container.

$ lxc exec mynodejs -- sudo --login --user ubuntu
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

ubuntu@mynodejs:~$

We executed in the mynodejs container the command sudo –login –user ubuntu. It gives as a login shell for the non-root default user ubuntu, that is always found in Ubuntu container images.

Installing Node.js in the LXD container

Here are the instructions to install Node.js 8 on Ubuntu.

ubuntu@mynodejs:~$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
## Installing the NodeSource Node.js v8.x repo...
...
## Run `apt-get install nodejs` (as root) to install Node.js v8.x and npm

ubuntu@mynodejs:~$ sudo apt-get install -y nodejs
...
Setting up python (2.7.11-1) ...
Setting up nodejs (8.9.0-1nodesource1) ...
ubuntu@mynodejs:~$ sudo apt-get install -y build-essential
...
ubuntu@mynodejs:~$

The curl | sh command makes you want to install in a container rather than on your desktop. Just saying. We also install the build-essential meta-package because it is needed when you install packages on top of Node.js.

Installing Chalktalk

We follow the installation instructions for Chalktalk to clone the repository. We use depth=1 to get a shallow copy (18MB) instead of the full repository (100MB).

ubuntu@mynodejs:~$ git clone https://github.com/kenperlin/chalktalk.git --depth=1
Cloning into 'chalktalk'...
remote: Counting objects: 195, done.
remote: Compressing objects: 100% (190/190), done.
remote: Total 195 (delta 5), reused 51 (delta 2), pack-reused 0
Receiving objects: 100% (195/195), 8.46 MiB | 8.34 MiB/s, done.
Resolving deltas: 100% (5/5), done.
Checking connectivity... done.
ubuntu@mynodejs:~$ cd chalktalk/server/
ubuntu@mynodejs:~/chalktalk/server$ npm install
> bufferutil@1.2.1 install /home/ubuntu/chalktalk/server/node_modules/bufferutil
> node-gyp rebuild

make: Entering directory '/home/ubuntu/chalktalk/server/node_modules/bufferutil/build'
 CXX(target) Release/obj.target/bufferutil/src/bufferutil.o
 SOLINK_MODULE(target) Release/obj.target/bufferutil.node
 COPY Release/bufferutil.node
make: Leaving directory '/home/ubuntu/chalktalk/server/node_modules/bufferutil/build'

> utf-8-validate@1.2.2 install /home/ubuntu/chalktalk/server/node_modules/utf-8-validate
> node-gyp rebuild

make: Entering directory '/home/ubuntu/chalktalk/server/node_modules/utf-8-validate/build'
 CXX(target) Release/obj.target/validation/src/validation.o
 SOLINK_MODULE(target) Release/obj.target/validation.node
 COPY Release/validation.node
make: Leaving directory '/home/ubuntu/chalktalk/server/node_modules/utf-8-validate/build'
npm WARN chalktalk@0.0.1 No description
npm WARN chalktalk@0.0.1 No repository field.
npm WARN chalktalk@0.0.1 No license field.

added 5 packages in 3.091s
ubuntu@mynodejs:~/chalktalk/server$ cd ..
ubuntu@mynodejs:~/chalktalk$

Trying out Chalktalk

Let’s run the app, using the following command (you need to be in the chalktalk directory):

ubuntu@mynodejs:~/chalktalk$ node server/main.js 
HTTP server listening on port 11235

Now, we are ready to try out Chalktalk! Use your favorite browser and visit http://10.52.252.246/11235 (replace according the IP address of your container).

You are presented with a blackboard! You use the mouse to sketch objects, then click on your sketch to get Chalktalk to try to identify it and create the actually responsive object.

It makes more sense if you watch the following video,

And this is how you can cleanly install Node.js into a LXD container. Once you are done testing, you can delete the container and it’s gone.

Update #1:

Here is an actual example. The pendulum responds to the mouse and we can nudge it.

The number can be incremented or decremented using the mouse; do an UP gesture to increment, and a DOWN gesture to decrement. You can also multiply/divide by 10 if you do a LEFT/RIGHT gesture.

Each type of object has a corresponding sketch in Chalktalk. In the source there is a directory with the sketches, with the ability to add new sketches.

Update #2:

Let’s see how to get this Node.js app to autostart when the LXD container is started. We are going to use systemd to control the autostart feature.

First, let’s create a script, called chalktalk-service.sh, that starts the Node.js app:

ubuntu@mynodejs:~$ pwd
/home/ubuntu
ubuntu@mynodejs:~$ cat chalktalk-service.sh 
#!/bin/sh
cd /home/ubuntu/chalktalk
/usr/bin/node /home/ubuntu/chalktalk/server/main.js
ubuntu@mynodejs:~$

We have created a script instead of running the command directly. The reason is that chalktalk uses relative pathes and we need to chdir to the appropriate directory first so it works. You may want to contact the author to attend to this.

Then, we create a service file for Chalktalk.

ubuntu@mynodejs:~$ cat /lib/systemd/system/chalktalk.service 
[Unit]
Description=Chalktalk - your live blackboard
Documentation=https://github.com/kenperlin/chalktalk/wiki
After=network.target
After=network-online.target

[Service]
Type=simple
User=ubuntu
Group=ubuntu
ExecStart=/home/ubuntu/chalktalk-service.sh
Restart=on-failure
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=chalktalk

[Install]
WantedBy=multi-user.target

ubuntu@mynodejs:~$

We have configured to get Chalktalk to autostart once the network is up and online. The service gets to become user ubuntu, then run the command. Any output or error goes to syslog, using the chalktalk syslog identifier.

Let’s get Systemd to learn about this new service file.

ubuntu@mynodejs:~$ sudo systemctl daemon-reload
ubuntu@mynodejs:~$

Let’s enable the Chalktalk service, then start it.

ubuntu@mynodejs:~$ sudo systemctl enable chalktalk.service
ubuntu@mynodejs:~$ sudo systemctl start chalktalk.service
ubuntu@mynodejs:~$

Now we verify whether it works.

Let’s restart the container and test whether the Chalktalk actually autostarted!

ubuntu@mynodejs:~$ logout

myusername@mycomputer /home/myusername:~$ lxc restart mynodejs
myusername@mycomputer /home/myusername:~$

That’s it!

Permanent link to this article: https://blog.simos.info/how-to-install-a-node-js-app-in-a-lxd-container/

1 ping

  1. […] the container so that the Jupyter Notebook gets autostarted when you reboot your computer. See https://blog.simos.info/how-to-install-a-node-js-app-in-a-lxd-container/ for some hints on […]

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.