Debian 11 on a diskless Intel PC (i386/amd64) with root filesystem on NBD
Motivation
A definitive failure of the 16G SSD (PATA!) in my ancient Dell Vostro A90 nearly lead to trashing the machine. Not that it is used that much but occasionally it still worked well (eg. as a low-end machine for testing software performance or even a SNES emulation system).
At present, a disk with such interface (miniPCI-e PATA) is nearly impossible to acquire. Few are available for some ridiculus price on the other side of the Atlantic (USA) and have even more ridiculus shipment cost (more than the disk itself…).
Such catastrophies occasionally motivates to make something not necessarily very useful (as is that ancient machine…) - but gives an opportunity to play with features of today’s operating systems. An so it was this time…
A long time ago (20 years or so…), I was building custom diskless linuxes for computer labs
at my Alma Mater. Having a usual infrastructure in place, I was using read-only nfsroot, at some point
improved with some hacks like layered filesystem (unionfs/aufs
) - an approach that is used with
some live CDs / USBs and some years later became popular with Docker containers. The purpose was
similar - to keep the image (filesystem) of the original OS intact while letting work
with relatively large permissions and have a fresh OS with each restart (to keep security
and reliability on a reasonable level).
As preparing such an nfsroot system was taking quite a bit of time (dhbootstrap
, manual
scripting the init process etc.), I looked for other ways to keep my Vostro alive.
Not using NFS (which, AFAIK, at least in a standard unpatched Linux kernel, is the only network
filesytem that can be used as the root filesystem), the other way is to have a network disk / block device.
(Obviously) excluding enterprise solutions (not really great for home…), left me with iSCSI
and Network Block Device (NBD). I knew the first one a bit, as (also many years ago)
I have built the faculty’s first SAN storage system on a Linux machine with a disk array
exported with The Linux iSCSI Target (meaning server, “initiator / target”
are legacy terms from SCSI).
This still seemed a bit too complex for a home solution. Also, according to some opinions,
NBD is way simpler and - what goes along - lighter and faster.
So - NBD…
Network configuration
Network services that needs to be available to go any further: DHCP, TFTP, DNS. Detailed configuration of these are out of scope (esp. that each of these can be provided by several different software packages). So please, have a look elsewhere for this (also for firewall / iptables etc.). I will give only some details regarding this particular setup.
A little more will be about NBD which is the main focus here.
DHCP
Must have a static IP configuration for the client host, with the filename pointing to pxelinux.0
on your tftp server. (Unless you have a more complex config. - but then you know what to do…).
It must provide a proper configuration: the default router for your network and the DNS (otherwise Debian installer with not reach its package repositories).
TFTP
Must be set up and available for the client (ie. firewall). It will contain pxelinux.0
with
its modules, configuration, Debian’s network installer, later also Debian’s kernel and initrd
for the installed system - as we always boot from the network(!).
DNS
It should contain proper name resolution for the server(s) and the clients (if only this one is being sent by DHCP - it must also resolve properly all other internet domains).
Create and export a disk image using NDB
The NBD server allows to export a file (which can a regular file on a filesystem or, of course, can be another block device, like an LVM volume) to network clients.
So, there are 2 things to do:
- Creating a file that will serve as the exported NDB image.
For instance, for a 5G file:
dd if=/dev/zero of=/home/nbd/vostro-debian.img bs=1M count=5120
- Configure the NBD server to expose the image to the client(s)
Configuration of the
nbd-server
is fairly trivial. The following example is for Debian.- create a file
/etc/nbd-server/conf.d/
, eg./etc/nbd-server/conf.d/mydebian.conf
[mydebian] exportname = /home/nbd/mydebian.img readonly = false
- create a file
mydebian
is the name of the export (later used with nbd-client
with -N
option),
while the exportname
is the path to the exported image/file (why they named it exportname
- no idea…).
The client will need to access the nbd’s port (which, by default, is tcp/10809
).
(Re)start the nbd-server
- and you’re done (you may eventually check the system logs).
Note that by default (set in the global config of the nbd-server
: /etc/nbd-server/config
),
the nbd-server
will run as user ndb
/group ndb
- the ownership and permissions to the image file
must be properly set.
A (fully) network installation
The first step is to provide the OS installer available on the network - so that booting the machine using network card and the PXE protocol will start Debian installer.
The network installer for the Debian i386 can be found under netboot/
path in Debian repositories,
though, in fact, the content of debian-installer/i386/
is enough (talking about the whole subtree of course).
The downloaded contents has to be put on the tftp server and properly configured, ie. the DHCP info sent to the client
must be pointing to pxelinux.0
as boot file. Then, in the same directory, pxelinux.cfg/default
lies the default config.
The simplest way is to put it as the only thing on the tftp server (to the tftp’s main export directory) - you do not have to do anything more then - Debian installer should load and start.
I have decided to put the installer into some subdirectory (as I have more things available on tftp / pxelinux).
The installer put into eg. debian-installer/11/i386
(under tftp server path) require adding
the following section in your PXELINUX config:
LABEL PXE_Debian_11_i386_Installer
CONFIG debian-installer/11/i386/debian-installer/i386/pxelinux.cfg/default debian-installer/11/i386/debian-installer/i386/
However, the original network debian installer is not booting well from a subdirectory (why oh why???). It requires the following modifications:
- Adding the symbolic link:
$ cd debian-installer/11/i386/debian-installer/i386/pxelinux.cfg/ $ ln -s ../ debian-installer
- Changing the Debian’s
pxelinux.cfg/default
to use relative paths, ie. replace:path debian-installer/i386/boot-screens/ include debian-installer/i386/boot-screens/menu.cfg default debian-installer/i386/boot-screens/vesamenu.c32
with
path boot-screens/ include boot-screens/menu.cfg default vesamenu.c32
- Linking the proper modules
If you are not using Debian’s version of PXELinux, the modules in
boot-screens/
might not work properly. You may have to link there the ones from your pxelinux installation.
There are other ways to do it, maybe even better ones to have the installer in a subdirectory and as a submenu. This way seemed to me the least invasive - others will probably require editing more files as they have hardcoded paths (this actually could be done better in Debian).
(Note that Debian installer prepared in this way can be used to install a regular local debian - you just won’t need any CD/DVD/USB image anymore.)
NBD and Debian installer
While the original Debian installer provides loading nbd modules as an option, there are no tools allowing
to actually configure and use an nbd device (what just makes the nbd modules useless…). This is the major chore
during the installation - it is necessary to get the nbd-client
binary which will work within
the installation system (which is not the same package as in the regular system - as the installer
does not contain most of the libraries). I extracted the binary from package nbd-client-udeb_3.21-1_i386.udeb
(note the udeb
, which is micro-deb or debian installer package).
You can find one on any Debian mirror, eg. this one.
The binary has to be transferred to the installation system - tip: check what tools
are available (ftp/tftp/curl/scp/etc) and just make it available for download. Other way would be
a modification of the installator’s initrd
(this would make it permanent, useful in case of creating other
similar installations) - but I haven’t considered it worthwhile.
(Update: apparently, there is an easier way to get the nbd-client
binary
inside the installer system, see follow up notes at the bottom).
nbd-client
copied to the Debian installer system allows finally to setup the exported block device:
nbd-client -N mydebian nbdserverhostname /dev/nbd0
Then, such device can be partitioned, formatted and used by the installer as target.
(In case you cannot partition it from installer - it can also be done locally on the server using a loop device with whatever program you want. The Debian installer require just specifying mount points and filesystems).
Installation of the system
… is as usual. Except for the bootloader - installing it obviously does not make any sense
as the network bootloader (pxelinux
) will be loaded from network.
Before finishing the installation, remember to copy the kernel and initrd from /target/
to your server
- they will be needed to boot the new system (NBD and root filesystem will be available after loading kernel and initrd!). If you forget it - again, you can just mount the filesystem with a loop device.
After finishing the installation - a few more things has to be done manually to boot the new system.
Booting the newly installed Debian on NBD
Configuration for PXElinux:
LABEL Debian
LINUX mydebian/boot/vmlinuz
INITRD mydebian/boot/initrd.img
APPEND ip=dhcp nbdroot=192.168.1.1,mydebian,nbd0 root=/dev/nbd0p1
The vmlinuz
and initrd.img
are symbolic links to the original files copied from /boot
of the new system, so that the links can be easily changed on upgrades (or issues) - but of course
you may prefer overwriting the files or editing the config file instead.
(The nbdroot
parameter is not fully used in the first prototype version of initrd, ie. data provided is not parsed.
It is corrected in the improved version of initrd, see after the next section).
nbd-client binary for initrd
Getting an nbd-client
binary suitable for use inside initrd was a bit
tricky. The one used for installation (from udeb) could not be used as it was
linked with libraries which are not available inside the initrd image:
# ldd nbd-client
linux-gate.so.1 (0xb7f50000)
libnl-genl-3.so.200 => /lib/i386-linux-gnu/libnl-genl-3.so.200 (0xb7f25000)
libnl-3.so.200 => /lib/i386-linux-gnu/libnl-3.so.200 (0xb7f00000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7d18000)
libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb7cf6000)
/lib/ld-linux.so.2 (0xb7f52000)
and better not even to try to use the version from a regular system:
# ldd /sbin/nbd-client
linux-gate.so.1 (0xb7f3f000)
libgnutls.so.30 => /lib/i386-linux-gnu/libgnutls.so.30 (0xb7cf5000)
libnl-genl-3.so.200 => /lib/i386-linux-gnu/libnl-genl-3.so.200 (0xb7cec000)
libnl-3.so.200 => /lib/i386-linux-gnu/libnl-3.so.200 (0xb7cc7000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7adf000)
libp11-kit.so.0 => /lib/i386-linux-gnu/libp11-kit.so.0 (0xb798a000)
libidn2.so.0 => /lib/i386-linux-gnu/libidn2.so.0 (0xb7968000)
libunistring.so.2 => /lib/i386-linux-gnu/libunistring.so.2 (0xb77e6000)
libtasn1.so.6 => /lib/i386-linux-gnu/libtasn1.so.6 (0xb77cf000)
libnettle.so.8 => /lib/i386-linux-gnu/libnettle.so.8 (0xb7784000)
libhogweed.so.6 => /lib/i386-linux-gnu/libhogweed.so.6 (0xb773b000)
libgmp.so.10 => /lib/i386-linux-gnu/libgmp.so.10 (0xb76ab000)
libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb7689000)
/lib/ld-linux.so.2 (0xb7f41000)
libffi.so.7 => /lib/i386-linux-gnu/libffi.so.7 (0xb767f000)
libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xb7679000)
The solution was to build a custom nbd-client
on minimal Debian (with only
gcc
, make
and libglib2-dev
) - so that it is linked only with very basic stuff:
$ ldd nbd-3.24/nbd-client
linux-gate.so.1 (0xf7fce000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7d9b000)
/lib/ld-linux.so.2 (0xf7fd0000)
initrd with nbd support (quick’n’dirty prototype)
The generic initrd does not support NBD and has to be modified. The fastest way I found is to add
a simple script /init_nbd
setting-up the NBD:
#!/bin/sh
# setup networking
#modprobe r8169
ip link set enp4s0 up
ip link set enp4s0 arp on
ip addr add 192.168.1.10 dev enp4s0
ip route add 192.168.1.0/24 dev enp4s0
# download nbd client
tftp -g -r vostro/bin/nbd-client -l /bin/nbd-client 192.168.1.1
chmod a+rx /bin/nbd-client
# setup nbd
modprobe nbd
nbd-client -N mydebian 192.168.1.1 /dev/nbd0
add 3 lines to argument parsing code in /init
:
[...]
nbdroot=*)
NBDROOT="${x#nbdroot=}"
;;
nfsroot=*)
[...]
and execute the /init-nbd
script in /init
:
[...]
export starttime
if [ "$NBDROOT" ]
then
sh /init_nbd
fi
if [ "$ROOTDELAY" ]; then
sleep "$ROOTDELAY"
fi
[...]
Concerning the issues with the nbd-client
binary in initrd (above),
for the prototype, I decided to put the binary on the tftp server and download
it within init script - so that I could eventually update it without rebuilding
the whole initrd…
initrd with nbd support - more generic and parametrized
The recipe above was a quick prototype of initrd, with hardcoded values (adresses, NBD name etc.). It was possible to improve this and do it in a less invasive and more flexible (parametrized) way.
- Modification in
/init
- add settingNBDROOT
from the kernel parameternbdroot
:[...] # Parse command line options # shellcheck disable=SC2013 for x in $(cat /proc/cmdline); do case $x in [...] rootdelay=*) ROOTDELAY="${x#rootdelay=}" case ${ROOTDELAY} in *[![:digit:].]*) ROOTDELAY= ;; esac ;; nbdroot=*) NBDROOT="${x#nbdroot=}" ;; nfsroot=*) [...]
-
Add the
/scripts/nbd
script (configuring and mounting rootfs on NBD). - Put
nbd-client
binary into/bin/
(or better/sbin/
) on initrd (no need to download it separately).
This way, the nbd device can be configured in the pxelinux
configuration using the kernel parameter nbdroot
(as shown in example above).
(This version of initrd is still not perfect, but it could already be proposed to implement in Debian. It would make creating systems on NBD much simpler and faster).
Result and conclusions
My Vostro works fine running Debian on NBD since few months. The NBD, operating through a 100Mb network card, does not seem to be much slower than the weird SSD - the machine itself is very slow in today’s standards (Intel Atom). In the meantime, I’ve made the same setup for another machine (and may do similar things for some others).
As you can see, while there are a few tricky places, Debian (and probably other distros too)
can be installed and used with root filesystem on an NBD device. If Debian developers
remove the weird obstacles (like the lack of the nbd-client
in the installer system
and a lack of proper code for setting up an NBD device in the generic init scripts),
Debian could be ready to install and use from NBD nearly as easy as in other types
of installation.
Such installation has some drawbacks:
- it require a bit in infrastructure available (as described)
- it require copying the kernel and initrd to the tftp server on every update(!), along with updating either symlinks or the pxelinux configuration.
But it also has many advantages, eg. having an image of the system in a file gives the same flexibility as with a virtual machine: backup is just a file copy, templating, reuse for any experiments is equally simple - but it runs on a bare metal hardware(!). Also - the use of some “special” machines (like the one described above) can be extended, made more robust and easier to recover.
Follow-up and updates
I have contacted debian-user
about the issues and, following the advice, also the maintainer of the nbd-client
.
Some useful info from the feedback and a few new findings:
Debian installer
-
has a dedicated NBD parameter
modules=partman-nbd
, which, according to/usr/share/doc/nbd-client/README.Debian.gz
should let configure nbd using Debian installer. -
during the installation (within the installer system), the
nbd-client
can be manually installed with:anna install nbd-client
(information from the mailing list)
nbd-client and initrd scripts/configuration
nbd-client
package (deb
, not udeb
) contains a start-up script and
a iniramfs hook for putting the binary into the initrd (while working on
the target system).
I have tested these and unfortunately, there are still issues:
nbd-client
binary used for initrd is the one from deb package (linked to libraries unavailable inside initrd, see details above, and below)- the nbd startup script is not in the right place:
it is in
/usr/share/initramfs-tools/scripts/local-top/
while/init
expects it to be in/usr/share/initramfs-tools/scripts/
- the nbd startup script do not override the
mountroot
shell function, what is, again, expected by/init
(in which this function is just empty) and, in result, the rootfs is just not mounted during the startup…).
nbd-client binary for initrd
Bug reported.
to be continued…
Useful links
- NBD
- NBD on github
- UDEB
- DI_COMPONENTS
- iSCSI: iSCSI initiator, iSCSI target (stgt), iSCSI target (istgt)
- Discussions on Debian mailing lists: